src/Platform/SecurityBundle/Listeners/OneRoster/OneRosterPrepareSubscriber.php line 168

Open in your IDE?
  1. <?php
  2. namespace Platform\SecurityBundle\Listeners\OneRoster;
  3. use Cms\CoreBundle\Entity\AbstractOneRosterEntity;
  4. use Cms\CoreBundle\Entity\OneRosterSync;
  5. use Cms\CoreBundle\Events\OneRosterEvents;
  6. use Cms\CoreBundle\Model\Interfaces\OneRosterable\AbstractOneRosterableSubscriber;
  7. use Doctrine\Common\Util\ClassUtils;
  8. use Platform\QueueBundle\Event\AsyncEvent;
  9. use Platform\SecurityBundle\Entity\Identity\Account;
  10. use Platform\SecurityBundle\Entity\Identity\Group;
  11. /**
  12.  * Class OneRosterPrepareSubscriber
  13.  * @package Platform\SecurityBundle\Listeners\OneRoster
  14.  */
  15. final class OneRosterPrepareSubscriber extends AbstractOneRosterableSubscriber
  16. {
  17.     // TODO: before running one roster sync, need to change these in the database
  18.     const NAME_FORMAT 'campussuite.platform.security.groups.fixed.one_roster.%s';
  19.     const NAMES = [
  20.         AbstractOneRosterEntity::ENUMS__ROLE_TYPE__ADMINISTRATOR,
  21.         AbstractOneRosterEntity::ENUMS__ROLE_TYPE__AIDE,
  22.         AbstractOneRosterEntity::ENUMS__ROLE_TYPE__PROCTOR,
  23.         AbstractOneRosterEntity::ENUMS__ROLE_TYPE__TEACHER,
  24.         AbstractOneRosterEntity::ENUMS__ROLE_TYPE__STAFF,
  25.     ];
  26.     // NEW
  27.     const ALIASES = [
  28.         'oneroster.global.all' => [
  29.             AbstractOneRosterEntity::ENUMS__ROLE_TYPE__ADMINISTRATOR,
  30.             AbstractOneRosterEntity::ENUMS__ROLE_TYPE__AIDE,
  31.             AbstractOneRosterEntity::ENUMS__ROLE_TYPE__PROCTOR,
  32.             AbstractOneRosterEntity::ENUMS__ROLE_TYPE__TEACHER,
  33.             AbstractOneRosterEntity::ENUMS__ROLE_TYPE__STAFF,
  34.         ],
  35.         'oneroster.global.'.AbstractOneRosterEntity::ENUMS__ROLE_TYPE__ADMINISTRATOR => [AbstractOneRosterEntity::ENUMS__ROLE_TYPE__ADMINISTRATOR],
  36.         'oneroster.global.'.AbstractOneRosterEntity::ENUMS__ROLE_TYPE__AIDE => [AbstractOneRosterEntity::ENUMS__ROLE_TYPE__AIDE],
  37.         'oneroster.global.'.AbstractOneRosterEntity::ENUMS__ROLE_TYPE__PROCTOR => [AbstractOneRosterEntity::ENUMS__ROLE_TYPE__PROCTOR],
  38.         'oneroster.global.'.AbstractOneRosterEntity::ENUMS__ROLE_TYPE__TEACHER => [AbstractOneRosterEntity::ENUMS__ROLE_TYPE__TEACHER],
  39.         'oneroster.global.'.AbstractOneRosterEntity::ENUMS__ROLE_TYPE__STAFF => [AbstractOneRosterEntity::ENUMS__ROLE_TYPE__STAFF],
  40.     ];
  41.     /**
  42.      * {@inheritdoc}
  43.      */
  44.     static public function getSubscribedEvents(): array
  45.     {
  46.         return [
  47.             OneRosterEvents::EVENT__PREPARE => [
  48.                 ['groupsSync'0],
  49.                 ['deactivateMissing'0],
  50.             ],
  51.         ];
  52.     }
  53.     /**
  54.      * @param AsyncEvent $event
  55.      */
  56.     public function groupsSync(AsyncEvent $event): void
  57.     {
  58.         // data should be an array with an id of a sync
  59.         $job $this->loadJob($event);
  60.         // DEBUGGING
  61.         $event->getOutput()->writeln(sprintf(
  62.             'Sync #%s loaded',
  63.             $job->getIdentifier()
  64.         ));
  65.         // ensure we are meant to process this
  66.         if ( ! $this->checkTypes($job->getSync(), [
  67.             OneRosterSync::STRATEGIES__SSO,
  68.         ])) {
  69.             return;
  70.         }
  71.         // holder for groups to make
  72.         $groups = [];
  73.         // loop over all the groups we need to make
  74.         foreach (self::NAMES as $name) {
  75.             // fix the name
  76.             $name sprintf(
  77.                 self::NAME_FORMAT,
  78.                 $name
  79.             );
  80.             // attempt to load the group by name
  81.             $group $this->em->getRepository(Group::class)->findOneBy([
  82.                 'name' => $this->translator->trans($name),
  83.             ]);
  84.             // now if null, we need to make a new one
  85.             if (empty($group)) {
  86.                 $group = new Group();
  87.             }
  88.             // update fields on the account
  89.             $group
  90.                 ->setName($this->translator->trans($name))
  91.                 ->setAlias($name)
  92.                 ->setFixed(true)
  93.                 ->setOneRosterId($job->getSync()->getDistrictId())
  94.             ;
  95.             // attach to array of things to save
  96.             $groups[] = $group;
  97.         }
  98.         // NEW: loop over all the groups we need to make
  99.         foreach (array_keys(self::ALIASES) as $alias) {
  100.             // attempt to load the group by alias
  101.             $group $this->em->getRepository(Group::class)->findOneBy([
  102.                 'alias' => $alias,
  103.             ]);
  104.             // now if null, we need to make a new one
  105.             if (empty($group)) {
  106.                 $group = new Group();
  107.             }
  108.             // update fields on the account
  109.             $group
  110.                 ->setName($this->translator->trans(sprintf(
  111.                     'campussuite.platform.security.groups.aliases.%s',
  112.                     $alias
  113.                 )))
  114.                 ->setAlias($alias)
  115.                 ->setFixed(true)
  116.                 ->setOneRosterId($job->getSync()->getDistrictId())
  117.             ;
  118.             // attach to array of things to save
  119.             $groups[] = $group;
  120.         }
  121.         // cache the output
  122.         $output array_map(
  123.             function (Group $grp) {
  124.                 return sprintf(
  125.                     '    %s    %s    (%s | %s | %s)',
  126.                     ((empty($grp->getId())) ? 'Generating' 'Updating'),
  127.                     ClassUtils::getClass($grp),
  128.                     $grp->getName(),
  129.                     $grp->getOneRosterId(),
  130.                     $grp->getId() ?: '-'
  131.                 );
  132.             },
  133.             $groups
  134.         );
  135.         // let's save the groups
  136.         $this->em->saveAll($groups);
  137.         // DEBUGGING
  138.         $event->getOutput()->writeln($output);
  139.     }
  140.     /**
  141.      * @param AsyncEvent $event
  142.      */
  143.     public function deactivateMissing(AsyncEvent $event): void
  144.     {
  145.         // data should be an array with an id of a sync
  146.         $job $this->loadJob($event);
  147.         // run bulk query
  148.         // any account that has oneroster id that does not exist needs to be deactivated
  149.         $changes $this->em->createQueryBuilder()
  150.             ->update(Account::class, 'accounts')
  151.             ->set('accounts.active'':active')
  152.             ->setParameter('active'false)
  153.             ->andWhere('accounts.tenant = :tenant')// filter by tenant
  154.             ->setParameter('tenant'$job->getTenant()->getId())
  155.             ->andWhere('accounts.onerosterId IS NOT NULL')// for performance, only update ones not already without oneroster hooks
  156.             ->andWhere($this->em->getExpressionBuilder()->notIn(
  157.                 'accounts.onerosterId',
  158.                 $this->em->createQueryBuilder()
  159.                     ->select('objects.sourcedId')
  160.                     ->from(AbstractOneRosterEntity::class, 'objects')
  161.                     ->andWhere('objects.tenant = :tenant')// we are making a string subquery, so the tenant var in the other qb will be used here eventually
  162.                     ->getDQL()
  163.             ))// only match objects that are missing from stashed objects
  164.             ->getQuery()
  165.             ->execute();
  166.         // DEBUGGING
  167.         $event->getOutput()->writeln(sprintf(
  168.             'Deactivated %s accounts for sync #%s',
  169.             $changes,
  170.             $job->getIdentifier()
  171.         ));
  172.     }
  173. }