%PDF- %PDF-
Direktori : /home/vacivi36/intranet.vacivitta.com.br/protected/modules/auth-keycloak/jobs/ |
Current File : /home/vacivi36/intranet.vacivitta.com.br/protected/modules/auth-keycloak/jobs/GroupsFullSync.php |
<?php /** * Keycloak Sign-In * @link https://github.com/cuzy-app/humhub-modules-auth-keycloak * @license https://github.com/cuzy-app/humhub-modules-auth-keycloak/blob/master/docs/LICENCE.md * @author [Marc FARRE](https://marc.fun) for [CUZY.APP](https://www.cuzy.app) */ namespace humhub\modules\authKeycloak\jobs; use humhub\modules\authKeycloak\authclient\Keycloak; use humhub\modules\authKeycloak\components\KeycloakApi; use humhub\modules\authKeycloak\models\ConfigureForm; use humhub\modules\authKeycloak\models\GroupKeycloak; use humhub\modules\queue\ActiveJob; use humhub\modules\user\models\Auth; use Throwable; use Yii; use yii\base\InvalidConfigException; use yii\db\StaleObjectException; use yii\helpers\ArrayHelper; use yii\queue\RetryableJobInterface; class GroupsFullSync extends ActiveJob implements RetryableJobInterface { /** * On first sync, Humhub groups and members are added to Keycloak * Then, changes on Humhub are sync on real time to Keycloak (in Events.php) * But changes on Keycloak are sync by cron to Humhub (in this class) * @var bool */ public $firstSync = false; /** * @var KeycloakApi */ protected $keycloakApi; /** * @var array */ protected $keycloakGroupsNamesById = []; /** * @var array Keycloak group ID => [Keycloak user ID] * Contains only users that have an account on Humhub */ protected $keycloakGroupsMembers = []; /** * @var array Humhub group ID => [Humhub user ID] * Contains only users that have logged in with Keycloak */ protected $humhubGroupsMembers = []; /** * @var GroupKeycloak[] */ protected $humhubGroupsByKeycloakId = []; /** * @var array */ protected $usersKeycloakIdToHumhubId = []; /** * @inhertidoc * @var int maximum 1 hour */ private $maxExecutionTime = 60 * 60; /** * @inheritdoc * @return void * @throws InvalidConfigException * @throws StaleObjectException * @throws Throwable */ public function run() { $config = new ConfigureForm(); if ( !$config->enabled || !$config->apiUsername || !$config->apiPassword || $config->groupsSyncMode === ConfigureForm::GROUP_SYNC_MODE_NONE ) { return; } $this->keycloakApi = new KeycloakApi(); if (!$this->initKeycloakGroupsNamesById()) { return; } $this->initHumhubGroupsByKeycloakId(); if ($config->syncKeycloakGroupsToHumhub()) { $this->addKeycloakGroupsToHumhub(); } if ($this->firstSync && $config->syncHumhubGroupsToKeycloak()) { $this->addHumhubGroupsToKeycloak(); } $this->initUsersKeycloakIdToHumhubId(); $this->initKeycloakGroupsMembers(); $this->initHumhubGroupsMembers(); if ($config->syncKeycloakGroupsToHumhub()) { $this->addKeycloakUsersToHumhubGroups(); } if ($this->firstSync) { if ($config->syncHumhubGroupsToKeycloak()) { $this->addHumhubUsersToKeycloakGroups(); } } else { if ($config->syncKeycloakGroupsToHumhub(true)) { $this->deleteHumhubGroups(); $this->deleteHumhubUsersFromHumhubGroups(); } if ($config->syncKeycloakGroupsToHumhub()) { $this->renameHumhubGroups(); } } } /** * @return bool */ protected function initKeycloakGroupsNamesById() { $this->keycloakGroupsNamesById = $this->keycloakApi->getGroupsNamesById(); if (!is_array($this->keycloakGroupsNamesById)) { Yii::error('Error retrieving groups on Keycloak: ', 'auth-keycloak'); return false; } return true; } /** * @return void */ protected function initHumhubGroupsByKeycloakId() { $this->humhubGroupsByKeycloakId = GroupKeycloak::find() ->where(['not', ['keycloak_id' => null]]) ->indexBy('keycloak_id') ->all(); // Remove groups not in Keycloak foreach ($this->humhubGroupsByKeycloakId as $keycloakGroupId => $humhubGroup) { if (!array_key_exists($keycloakGroupId, $this->keycloakGroupsNamesById)) { unset($this->humhubGroupsByKeycloakId[$keycloakGroupId]); } } } /** * @return void */ protected function addKeycloakGroupsToHumhub() { $groupsKeycloakByHumhubName = GroupKeycloak::find() ->where(['keycloak_id' => null]) ->indexBy('name') ->all(); $allHumhubGroupsByKeycloakId = GroupKeycloak::find() ->where(['not', ['keycloak_id' => null]]) ->indexBy('keycloak_id') ->all(); foreach ($this->keycloakGroupsNamesById as $keycloakGroupId => $keycloakGroupName) { // Check if Humhub group exists if (!array_key_exists($keycloakGroupId, $allHumhubGroupsByKeycloakId)) { // Search for existing Humhub group with same name if (array_key_exists($keycloakGroupName, $groupsKeycloakByHumhubName)) { $groupKeycloak = $groupsKeycloakByHumhubName[$keycloakGroupName]; } else { // Add missing group to Humhub $groupKeycloak = new GroupKeycloak(); $groupKeycloak->name = $keycloakGroupName; } $groupKeycloak->keycloak_id = $keycloakGroupId; if (!$groupKeycloak->save()) { continue; } $this->humhubGroupsByKeycloakId[$keycloakGroupId] = $groupKeycloak; } } } /** * Only on first sync, then sync from Humhub is done by events * @return void */ protected function addHumhubGroupsToKeycloak() { foreach (GroupKeycloak::find()->all() as $humhubGroup) { // Check if Keycloak group exists if ( $humhubGroup->keycloak_id === null || !array_key_exists($humhubGroup->keycloak_id, $this->keycloakGroupsNamesById) ) { // Link to a same group name on Keycloak or create group on Keycloak if ($this->keycloakApi->linkSameGroupNameOrCreateGroup($humhubGroup->id)) { // Update Humhub group with the new created Keycloak ID $humhubGroup = GroupKeycloak::findOne($humhubGroup->id); $this->keycloakGroupsNamesById[$humhubGroup->keycloak_id] = $humhubGroup->name; } } } } /** * @return void */ protected function initUsersKeycloakIdToHumhubId() { $auths = Auth::find() ->orderBy(['id' => SORT_ASC]) // Get the latest if it has multiple ->where(['source' => Keycloak::DEFAULT_NAME]) ->indexBy('user_id') // Remove duplicated ->all(); $this->usersKeycloakIdToHumhubId = ArrayHelper::map($auths, 'source_id', 'user_id'); } /** * @return void */ protected function initKeycloakGroupsMembers() { foreach ($this->keycloakGroupsNamesById as $keycloakGroupId => $keycloakGroupName) { $this->keycloakGroupsMembers[$keycloakGroupId] = []; foreach ($this->keycloakApi->getGroupMemberIds($keycloakGroupId) as $keycloakUserId) { // If this user has an account on Humhub if ($this->getHumhubUserId($keycloakUserId) !== null) { $this->keycloakGroupsMembers[$keycloakGroupId][] = $keycloakUserId; } } } } /** * @param $keycloakUserId * @return string|null */ protected function getHumhubUserId($keycloakUserId) { return $this->usersKeycloakIdToHumhubId[$keycloakUserId] ?? null; } /** * @return void */ protected function initHumhubGroupsMembers() { foreach ($this->humhubGroupsByKeycloakId as $humhubGroup) { $this->humhubGroupsMembers[$humhubGroup->id] = []; foreach ($humhubGroup->groupUsers as $groupUser) { $userId = $groupUser->user_id; if ($this->isKeycloakUser($userId)) { $this->humhubGroupsMembers[$humhubGroup->id][] = $userId; } } } } /** * @param $humhubUserId * @return bool */ protected function isKeycloakUser($humhubUserId) { return in_array($humhubUserId, $this->usersKeycloakIdToHumhubId); } /** * @return void * @throws InvalidConfigException */ protected function addKeycloakUsersToHumhubGroups() { foreach ($this->keycloakGroupsMembers as $keycloakGroupId => $keycloakUserIds) { foreach ($keycloakUserIds as $keycloakUserId) { $humhubUserId = $this->usersKeycloakIdToHumhubId[$keycloakUserId]; $humhubGroup = $this->humhubGroupsByKeycloakId[$keycloakGroupId]; $humhubGroupMembers = $this->humhubGroupsMembers[$humhubGroup->id]; if (!in_array($humhubUserId, $humhubGroupMembers)) { $humhubGroup->addUser($humhubUserId); $this->humhubGroupsMembers[$humhubGroup->id][] = $humhubUserId; } } } } /** * Only on first sync, then sync from Humhub is done by events * @return void */ protected function addHumhubUsersToKeycloakGroups() { foreach ($this->humhubGroupsMembers as $humhubGroupId => $humhubUserIds) { $keycloakGroupId = $this->getKeycloakGroupId($humhubGroupId); $keycloakGroupMembers = $this->keycloakGroupsMembers[$keycloakGroupId]; foreach ($humhubUserIds as $humhubUserId) { $keycloakUserId = $this->getKeycloakUserId($humhubUserId); if ( $keycloakUserId !== null && !in_array($keycloakUserId, $keycloakGroupMembers) ) { $this->keycloakApi->addUserToGroup($humhubUserId, $humhubGroupId); $this->keycloakGroupsMembers[$keycloakGroupId][] = $keycloakUserId; } } } } /** * @param $humhubGroupId * @return string|null */ protected function getKeycloakGroupId($humhubGroupId) { foreach ($this->humhubGroupsByKeycloakId as $humhubGroup) { if ($humhubGroup->id === $humhubGroupId) { return $humhubGroup->keycloak_id; } } return null; } /** * @param $humhubUserId * @return string|null */ protected function getKeycloakUserId($humhubUserId) { return array_search($humhubUserId, $this->usersKeycloakIdToHumhubId) ?: null; } /** * Check for deleted groups on Keycloak * @return void * @throws StaleObjectException * @throws Throwable */ protected function deleteHumhubGroups() { foreach ($this->humhubGroupsByKeycloakId as $keycloakGroupId => $humhubGroup) { if (!array_key_exists($keycloakGroupId, $this->keycloakGroupsNamesById)) { $humhubGroup->delete(); unset($this->humhubGroupsByKeycloakId[$keycloakGroupId]); } } } /** * Check for delete users in Keycloak groups * @return void * @throws StaleObjectException * @throws Throwable */ protected function deleteHumhubUsersFromHumhubGroups() { foreach ($this->humhubGroupsMembers as $humhubGroupId => $humhubUserIds) { $keycloakGroupId = $this->getKeycloakGroupId($humhubGroupId); $keycloakGroupMembers = $this->keycloakGroupsMembers[$keycloakGroupId]; $humhubGroup = $this->humhubGroupsByKeycloakId[$keycloakGroupId]; foreach ($humhubUserIds as $humhubUserKey => $humhubUserId) { $keycloakUserId = $this->getKeycloakUserId($humhubUserId); if (!in_array($keycloakUserId, $keycloakGroupMembers)) { $humhubGroup->removeUser($humhubUserId); unset($this->humhubGroupsMembers[$humhubGroupId][$humhubUserKey]); } } } } /** * Check if some groups have been renamed on Keycloak * @return void * @throws Throwable */ protected function renameHumhubGroups() { foreach ($this->keycloakGroupsNamesById as $keycloakGroupId => $keycloakGroupName) { $humhubGroup = $this->humhubGroupsByKeycloakId[$keycloakGroupId]; if ($humhubGroup->name !== $keycloakGroupName) { $humhubGroup->name = $keycloakGroupName; $humhubGroup->save(); } } } /** * @inheritDoc */ public function getTtr() { return $this->maxExecutionTime; } /** * @inheritDoc for RetryableJobInterface */ public function canRetry($attempt, $error) { return false; } }