Source of file SyncOktaGroupsJob.php
Size: 7,718 Bytes - Last Modified: 2021-12-23T10:09:09+00:00
/var/www/docs.ssmods.com/process/src/src/jobs/SyncOktaGroupsJob.php
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246 | <?php namespace NZTA\OktaAPI\Jobs; use NZTA\OktaAPI\Model\OktaGroupFilter; use SilverStripe\ORM\DataList; use SilverStripe\ORM\Queries\SQLDelete; use SilverStripe\Security\Group; use Symbiote\QueuedJobs\Services\QueuedJob; use NZTA\OktaAPI\Services\OktaService; class SyncOktaGroupsJob extends AbstractOktaSyncJob implements QueuedJob { /** * @var OktaService */ public $OktaService; /** * @var array */ private static $dependencies = [ 'OktaService' => '%$' . OktaService::class, ]; /** * Time in seconds to reschedule for, from when this job finishes. * * @var integer */ private static $reschedule_time = 86400; /** * @return string */ public function getTitle() { return 'Sync the groups from Okta into the SilverStripe CMS'; } /** * Use the {@link OktaService} to get the full list of groups from the Okta * API. * * We then compare the groups from Okta to the records stored in the * DB and use it determine which groups need to be created, updated or * removed. * * We split out the groups into their own arrays to make it easy * to create a single SQL call for each (as we may be processing a large * amount of groups and want to prevent a separate DB call for each group). * * We finish up by rescheduling this job and marking as complete so it can * be removed from the queue. * * @return void * @throws \Exception */ public function process() { $this->syncOktaGroups(); $this->scheduleNextExecution(); $this->scheduleAdditionalJobs(); $this->markJobAsDone(); } /** * Retrieve all the groups from Okta and save all the groups that match * the filters created in the CMS. If no filters are added to the CMS, * save all the groups. * * @return void * @throws \Exception */ private function syncOktaGroups() { $groups = $this->OktaService->getAllGroups(); $groupFilters = OktaGroupFilter::get(); $hasFilters = ($groupFilters->count() > 0); $createdGroups = 0; $updatedIds = []; // go through each group from the API and save if applicable foreach ($groups as $group) { if ($this->saveGroup($group, $groupFilters, $hasFilters)) { $createdGroups++; } // record each group id from the API so we can remove groups if necessary if (isset($group->id)) { $updatedIds[] = $group->id; } } // remove any groups we have created from Okta that no longer exist in Okta $this->removeDeletedGroups($updatedIds); $this->addMessage(sprintf( 'Created %d groups', $createdGroups )); } /** * Save the group into the CMS unless there are filters. If there are * filters, check the group matches against one of the filters first. * * Also check we have not already created this group from Okta. * * @param stdObject $group The group data * @param DataList $filters The list of OktaGroupFilters * @param boolean $hasFilters Is there any filters? * * @return boolean * @throws \Exception */ private function saveGroup($group, $filters, $hasFilters) { if ($hasFilters && !$this->checkMatchesFilter($group, $filters)) { return false; } // base the unique code on the Okta group ID $groupId = isset($group->id) ? $group->id : 'Okta Group'; $groupName = isset($group->profile->name) ? $group->profile->name : ''; // ensure this group has not already been created if (Group::get()->filter('OktaGroupID', $groupId)->first()) { return false; } // otherwise we create the new group $newGroup = new Group(); $newGroup->Title = $groupName ? $groupName : $groupId; $newGroup->OktaGroupID = $groupId; $newGroup->OktaGroupName = $groupName; $newGroup->IsOktaGroup = true; $newGroup->write(); return true; } /** * Check if the specified group matches against one of the filters. We * assume the filter keys can only be nested two levels down using the * dot (e.g. "key.subkey") syntax. * * @param stdObject $group * @param DataList $filters * * @return boolean * @throws \Exception */ private function checkMatchesFilter($group, $filters) { // check through each of the filters to see if any match the current group foreach ($filters as $filter) { $filterKey = $filter->Filter; $filterValue = $filter->Value; // provide ability to filter by a nested key $filterKeyParts = explode('.', $filterKey); $partsCount = count($filterKeyParts); // assume only two levels deep for filtering if ($partsCount == 2) { $part1 = $filterKeyParts[0]; $part2 =$filterKeyParts[1]; $groupValue = isset($group->$part1->$part2) ? $group->$part1->$part2 : null; } elseif ($partsCount == 1) { $part1 = $filterKeyParts[0]; $groupValue = isset($group->$part1) ? $group->$part1 : null; } // if we find one matching filter, we should save this group if ($groupValue == $filterValue) { return true; } } // if we don't match against any filters, we shouldn't create this group return false; } /** * Compare the list of current groups from the Okta API and remove any SS * created groups that are Okta flagged groups if they no longer exist. * * @param array $updatedIds * * @return void */ private function removeDeletedGroups($updatedIds) { // check we have ids from the API if (count($updatedIds) > 0) { $currentGroups = Group::get() ->filter('IsOktaGroup', true) ->column('OktaGroupID'); // check we have current okta groups if (count($currentGroups) > 0) { $toDelete = array_diff($currentGroups, $updatedIds); $toDeleteCount = count($toDelete); // check if we have any groups to delete if ($toDeleteCount > 0) { $delete = SQLDelete::create('"Group"'); foreach ($toDelete as $id) { $delete->addWhere([ '"Group"."OktaGroupID"' => $id ]); } // set to use OR instead of AND $delete->useDisjunction(); try { $delete->execute(); // inform job we have deleted groups $this->addMessage(sprintf( 'Deleted %d groups', $toDeleteCount )); } catch (\Exception $e) { $this->getLogger()->error( sprintf( 'Error occurred attempting to delete users in SyncOktaGroupsJob. %s', $e->getMessage() ) ); } } } } } } |