564 lines
22 KiB
PHP
564 lines
22 KiB
PHP
<?php
|
|
/* For license terms, see /license.txt */
|
|
|
|
use Chamilo\CoreBundle\Entity\ExtraFieldValues;
|
|
use Chamilo\CoreBundle\Entity\TrackELogin;
|
|
use Doctrine\DBAL\Exception\UniqueConstraintViolationException;
|
|
use League\OAuth2\Client\Provider\AbstractProvider;
|
|
use League\OAuth2\Client\Provider\Exception\IdentityProviderException;
|
|
use League\OAuth2\Client\Provider\GenericProvider;
|
|
use League\OAuth2\Client\Token\AccessToken;
|
|
use League\OAuth2\Client\Tool\ArrayAccessorTrait;
|
|
|
|
/**
|
|
* OAuth2 plugin class.
|
|
*
|
|
* @author Sébastien Ducoulombier <seb@ldd.fr>
|
|
* inspired by AzureActiveDirectory plugin class from Angel Fernando Quiroz Campos <angel.quiroz@beeznest.com>
|
|
*
|
|
* @package chamilo.plugin.oauth2
|
|
*/
|
|
class OAuth2 extends Plugin
|
|
{
|
|
use ArrayAccessorTrait;
|
|
|
|
public const SETTING_ENABLE = 'enable';
|
|
|
|
public const SETTING_FORCE_REDIRECT = 'force_redirect';
|
|
public const SETTING_SKIP_FORCE_REDIRECT_IN = 'skip_force_redirect_in';
|
|
|
|
public const SETTING_CLIENT_ID = 'client_id';
|
|
public const SETTING_CLIENT_SECRET = 'client_secret';
|
|
|
|
public const SETTING_AUTHORIZE_URL = 'authorize_url';
|
|
public const SETTING_SCOPES = 'scopes';
|
|
public const SETTING_SCOPE_SEPARATOR = 'scope_separator';
|
|
|
|
public const SETTING_ACCESS_TOKEN_URL = 'access_token_url';
|
|
public const SETTING_ACCESS_TOKEN_METHOD = 'access_token_method';
|
|
// const SETTING_ACCESS_TOKEN_RESOURCE_OWNER_ID = 'access_token_resource_owner_id';
|
|
|
|
public const SETTING_RESOURCE_OWNER_DETAILS_URL = 'resource_owner_details_url';
|
|
|
|
public const SETTING_RESPONSE_ERROR = 'response_error';
|
|
public const SETTING_RESPONSE_CODE = 'response_code';
|
|
public const SETTING_RESPONSE_RESOURCE_OWNER_ID = 'response_resource_owner_id';
|
|
|
|
public const SETTING_UPDATE_USER_INFO = 'update_user_info';
|
|
public const SETTING_CREATE_NEW_USERS = 'create_new_users';
|
|
public const SETTING_RESPONSE_RESOURCE_OWNER_FIRSTNAME = 'response_resource_owner_firstname';
|
|
public const SETTING_RESPONSE_RESOURCE_OWNER_LASTNAME = 'response_resource_owner_lastname';
|
|
public const SETTING_RESPONSE_RESOURCE_OWNER_STATUS = 'response_resource_owner_status';
|
|
public const SETTING_RESPONSE_RESOURCE_OWNER_TEACHER_STATUS = 'response_resource_owner_teacher_status';
|
|
public const SETTING_RESPONSE_RESOURCE_OWNER_SESSADMIN_STATUS = 'response_resource_owner_sessadmin_status';
|
|
public const SETTING_RESPONSE_RESOURCE_OWNER_DRH_STATUS = 'response_resource_owner_drh_status';
|
|
public const SETTING_RESPONSE_RESOURCE_OWNER_STUDENT_STATUS = 'response_resource_owner_student_status';
|
|
public const SETTING_RESPONSE_RESOURCE_OWNER_ANON_STATUS = 'response_resource_owner_anon_status';
|
|
public const SETTING_RESPONSE_RESOURCE_OWNER_EMAIL = 'response_resource_owner_email';
|
|
public const SETTING_RESPONSE_RESOURCE_OWNER_USERNAME = 'response_resource_owner_username';
|
|
|
|
public const SETTING_RESPONSE_RESOURCE_OWNER_URLS = 'response_resource_owner_urls';
|
|
|
|
public const SETTING_LOGOUT_URL = 'logout_url';
|
|
|
|
public const SETTING_BLOCK_NAME = 'block_name';
|
|
|
|
public const SETTING_MANAGEMENT_LOGIN_ENABLE = 'management_login_enable';
|
|
public const SETTING_MANAGEMENT_LOGIN_NAME = 'management_login_name';
|
|
|
|
public const SETTING_ALLOW_THIRD_PARTY_LOGIN = 'allow_third_party_login';
|
|
|
|
public const EXTRA_FIELD_OAUTH2_ID = 'oauth2_id';
|
|
|
|
private const DEBUG = false;
|
|
|
|
protected function __construct()
|
|
{
|
|
parent::__construct(
|
|
'0.1',
|
|
'Sébastien Ducoulombier',
|
|
[
|
|
self::SETTING_ENABLE => 'boolean',
|
|
|
|
self::SETTING_FORCE_REDIRECT => 'boolean',
|
|
self::SETTING_SKIP_FORCE_REDIRECT_IN => 'text',
|
|
|
|
self::SETTING_CLIENT_ID => 'text',
|
|
self::SETTING_CLIENT_SECRET => 'text',
|
|
|
|
self::SETTING_AUTHORIZE_URL => 'text',
|
|
self::SETTING_SCOPES => 'text',
|
|
self::SETTING_SCOPE_SEPARATOR => 'text',
|
|
|
|
self::SETTING_ACCESS_TOKEN_URL => 'text',
|
|
self::SETTING_ACCESS_TOKEN_METHOD => [
|
|
'type' => 'select',
|
|
'options' => [
|
|
AbstractProvider::METHOD_POST => 'POST',
|
|
AbstractProvider::METHOD_GET => 'GET',
|
|
],
|
|
],
|
|
// self::SETTING_ACCESS_TOKEN_RESOURCE_OWNER_ID => 'text',
|
|
|
|
self::SETTING_RESOURCE_OWNER_DETAILS_URL => 'text',
|
|
|
|
self::SETTING_RESPONSE_ERROR => 'text',
|
|
self::SETTING_RESPONSE_CODE => 'text',
|
|
self::SETTING_RESPONSE_RESOURCE_OWNER_ID => 'text',
|
|
|
|
self::SETTING_UPDATE_USER_INFO => 'boolean',
|
|
self::SETTING_CREATE_NEW_USERS => 'boolean',
|
|
self::SETTING_RESPONSE_RESOURCE_OWNER_FIRSTNAME => 'text',
|
|
self::SETTING_RESPONSE_RESOURCE_OWNER_LASTNAME => 'text',
|
|
self::SETTING_RESPONSE_RESOURCE_OWNER_STATUS => 'text',
|
|
self::SETTING_RESPONSE_RESOURCE_OWNER_TEACHER_STATUS => 'text',
|
|
self::SETTING_RESPONSE_RESOURCE_OWNER_SESSADMIN_STATUS => 'text',
|
|
self::SETTING_RESPONSE_RESOURCE_OWNER_DRH_STATUS => 'text',
|
|
self::SETTING_RESPONSE_RESOURCE_OWNER_STUDENT_STATUS => 'text',
|
|
self::SETTING_RESPONSE_RESOURCE_OWNER_ANON_STATUS => 'text',
|
|
self::SETTING_RESPONSE_RESOURCE_OWNER_EMAIL => 'text',
|
|
self::SETTING_RESPONSE_RESOURCE_OWNER_USERNAME => 'text',
|
|
|
|
self::SETTING_RESPONSE_RESOURCE_OWNER_URLS => 'text',
|
|
|
|
self::SETTING_LOGOUT_URL => 'text',
|
|
|
|
self::SETTING_BLOCK_NAME => 'text',
|
|
|
|
self::SETTING_MANAGEMENT_LOGIN_ENABLE => 'boolean',
|
|
self::SETTING_MANAGEMENT_LOGIN_NAME => 'text',
|
|
|
|
self::SETTING_ALLOW_THIRD_PARTY_LOGIN => 'boolean',
|
|
]
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Instance the plugin.
|
|
*
|
|
* @staticvar null $result
|
|
*
|
|
* @return $this
|
|
*/
|
|
public static function create(): OAuth2
|
|
{
|
|
static $result = null;
|
|
|
|
return $result ?: $result = new self();
|
|
}
|
|
|
|
public function getProvider(): GenericProvider
|
|
{
|
|
$redirectUri = api_get_path(WEB_PLUGIN_PATH).'oauth2/src/callback.php';
|
|
// In cases not precisely defined yet, this alternative version might be necessary - see BT#20611
|
|
//$redirectUri = api_get_path(WEB_PATH).'authorization-code/callback';
|
|
$options = [
|
|
'clientId' => $this->get(self::SETTING_CLIENT_ID),
|
|
'clientSecret' => $this->get(self::SETTING_CLIENT_SECRET),
|
|
'redirectUri' => $redirectUri,
|
|
'urlAuthorize' => $this->get(self::SETTING_AUTHORIZE_URL),
|
|
'urlResourceOwnerDetails' => $this->get(self::SETTING_RESOURCE_OWNER_DETAILS_URL),
|
|
];
|
|
|
|
if ('' === $scopeSeparator = (string) $this->get(self::SETTING_SCOPE_SEPARATOR)) {
|
|
$scopeSeparator = ' ';
|
|
}
|
|
|
|
$options['scopeSeparator'] = $scopeSeparator;
|
|
|
|
if ('' !== $scopes = (string) $this->get(self::SETTING_SCOPES)) {
|
|
$options['scopes'] = explode($scopeSeparator, $scopes);
|
|
}
|
|
|
|
if ('' !== $urlAccessToken = (string) $this->get(self::SETTING_ACCESS_TOKEN_URL)) {
|
|
$options['urlAccessToken'] = $urlAccessToken;
|
|
}
|
|
|
|
if ('' !== $accessTokenMethod = (string) $this->get(self::SETTING_ACCESS_TOKEN_METHOD)) {
|
|
$options['accessTokenMethod'] = $accessTokenMethod;
|
|
}
|
|
|
|
// if ('' !== $accessTokenResourceOwnerId = (string) $this->get(self::SETTING_ACCESS_TOKEN_RESOURCE_OWNER_ID)) {
|
|
// $options['accessTokenResourceOwnerId'] = $accessTokenResourceOwnerId;
|
|
// }
|
|
|
|
if ('' !== $responseError = (string) $this->get(self::SETTING_RESPONSE_ERROR)) {
|
|
$options['responseError'] = $responseError;
|
|
}
|
|
|
|
if ('' !== $responseCode = (string) $this->get(self::SETTING_RESPONSE_CODE)) {
|
|
$options['responseCode'] = $responseCode;
|
|
}
|
|
|
|
if ('' !== $responseResourceOwnerId = (string) $this->get(self::SETTING_RESPONSE_RESOURCE_OWNER_ID)) {
|
|
$options['responseResourceOwnerId'] = $responseResourceOwnerId;
|
|
}
|
|
|
|
return new GenericProvider($options);
|
|
}
|
|
|
|
/**
|
|
* @throws IdentityProviderException
|
|
*
|
|
* @return array user information, as returned by api_get_user_info(userId)
|
|
*/
|
|
public function getUserInfo(GenericProvider $provider, AccessToken $accessToken): array
|
|
{
|
|
$url = $provider->getResourceOwnerDetailsUrl($accessToken);
|
|
$request = $provider->getAuthenticatedRequest($provider::METHOD_GET, $url, $accessToken);
|
|
$response = $provider->getParsedResponse($request);
|
|
$this->log('response', print_r($response, true));
|
|
|
|
if (false === is_array($response)) {
|
|
$this->log('invalid response', print_r($response, true));
|
|
throw new UnexpectedValueException($this->get_lang('InvalidJsonReceivedFromProvider'));
|
|
}
|
|
$resourceOwnerId = $this->getValueByKey(
|
|
$response,
|
|
$this->get(self::SETTING_RESPONSE_RESOURCE_OWNER_ID)
|
|
);
|
|
if (empty($resourceOwnerId)) {
|
|
$this->log('missing setting', 'response_resource_owner_id');
|
|
throw new RuntimeException($this->get_lang('WrongResponseResourceOwnerId'));
|
|
}
|
|
$this->log('response resource owner id', $this->get(self::SETTING_RESPONSE_RESOURCE_OWNER_ID));
|
|
$extraFieldValue = new ExtraFieldValue('user');
|
|
$result = $extraFieldValue->get_item_id_from_field_variable_and_field_value(
|
|
self::EXTRA_FIELD_OAUTH2_ID,
|
|
$resourceOwnerId
|
|
);
|
|
if (false === $result) {
|
|
$this->log('user not found', "extrafield 'oauth2_id' with value '$resourceOwnerId'");
|
|
|
|
$username = $this->getValueByKey(
|
|
$response,
|
|
$this->get(self::SETTING_RESPONSE_RESOURCE_OWNER_USERNAME),
|
|
'oauth2user_'.$resourceOwnerId
|
|
);
|
|
|
|
$userInfo = api_get_user_info_from_username($username);
|
|
|
|
if (false !== $userInfo && !empty($userInfo['id']) && 'platform' === $userInfo['auth_source']) {
|
|
$this->log('platform user exists', print_r($userInfo, true));
|
|
|
|
$userId = $userInfo['id'];
|
|
} else {
|
|
// authenticated user not found in internal database
|
|
if ('true' !== $this->get(self::SETTING_CREATE_NEW_USERS)) {
|
|
$this->log('exception', 'create_new_users setting is disabled');
|
|
$message = sprintf(
|
|
$this->get_lang('NoUserAccountAndUserCreationNotAllowed'),
|
|
Display::encrypted_mailto_link(api_get_setting('emailAdministrator'))
|
|
);
|
|
throw new RuntimeException($message);
|
|
}
|
|
|
|
require_once __DIR__.'/../../../main/auth/external_login/functions.inc.php';
|
|
|
|
$firstName = $this->getValueByKey(
|
|
$response,
|
|
$this->get(self::SETTING_RESPONSE_RESOURCE_OWNER_FIRSTNAME),
|
|
$this->get_lang('DefaultFirstname')
|
|
);
|
|
$lastName = $this->getValueByKey(
|
|
$response,
|
|
$this->get(self::SETTING_RESPONSE_RESOURCE_OWNER_LASTNAME),
|
|
$this->get_lang('DefaultLastname')
|
|
);
|
|
$status = $this->mapUserStatusFromResponse($response);
|
|
$email = $this->getValueByKey(
|
|
$response,
|
|
$this->get(self::SETTING_RESPONSE_RESOURCE_OWNER_EMAIL),
|
|
'oauth2user_'.$resourceOwnerId.'@'.(gethostname() or 'localhost')
|
|
);
|
|
|
|
$userInfo = [
|
|
'firstname' => $firstName,
|
|
'lastname' => $lastName,
|
|
'status' => $status,
|
|
'email' => $email,
|
|
'username' => $username,
|
|
'auth_source' => 'oauth2',
|
|
];
|
|
$userId = external_add_user($userInfo);
|
|
if (false === $userId) {
|
|
$this->log('user not created', print_r($userInfo, true));
|
|
throw new RuntimeException($this->get_lang('FailedUserCreation'));
|
|
}
|
|
$this->log('user created', (string) $userId);
|
|
}
|
|
|
|
$this->updateUser($userId, $response);
|
|
// Not checking function update_extra_field_value return value because not reliable
|
|
UserManager::update_extra_field_value($userId, self::EXTRA_FIELD_OAUTH2_ID, $resourceOwnerId);
|
|
$this->updateUserUrls($userId, $response);
|
|
} else {
|
|
$this->log('user found', "extrafield 'oauth2_id' with value '$resourceOwnerId'");
|
|
// authenticated user found in internal database
|
|
if (is_array($result) and array_key_exists('item_id', $result)) {
|
|
$userId = $result['item_id'];
|
|
} else {
|
|
$userId = $result;
|
|
}
|
|
if ('true' === $this->get(self::SETTING_UPDATE_USER_INFO)) {
|
|
$this->updateUser($userId, $response);
|
|
$this->updateUserUrls($userId, $response);
|
|
|
|
Event::addEvent(LOG_USER_UPDATE, LOG_USER_ID, $userId);
|
|
}
|
|
}
|
|
$userInfo = api_get_user_info($userId);
|
|
if (empty($userInfo)) {
|
|
$this->log('user info not found', (string) $userId);
|
|
throw new LogicException($this->get_lang('InternalErrorCannotGetUserInfo'));
|
|
}
|
|
|
|
$this->log('user info', print_r($userInfo, true));
|
|
|
|
return $userInfo;
|
|
}
|
|
|
|
public function getSignInURL(): string
|
|
{
|
|
return api_get_path(WEB_PLUGIN_PATH).$this->get_name().'/src/callback.php';
|
|
// In cases not precisely defined yet, this alternative version might be necessary - see BT#20611
|
|
//return api_get_path(WEB_PATH).'authorization-code/callback';
|
|
}
|
|
|
|
public function getLogoutUrl(): string
|
|
{
|
|
$token = ChamiloSession::read('oauth2AccessToken');
|
|
$idToken = !empty($token['id_token']) ? $token['id_token'] : null;
|
|
|
|
return $this->get(self::SETTING_LOGOUT_URL).'?'.http_build_query(
|
|
[
|
|
'id_token_hint' => $idToken,
|
|
'post_logout_redirect_uri' => api_get_path(WEB_PATH),
|
|
]
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Create extra fields for user when installing.
|
|
*/
|
|
public function install()
|
|
{
|
|
UserManager::create_extra_field(
|
|
self::EXTRA_FIELD_OAUTH2_ID,
|
|
ExtraField::FIELD_TYPE_TEXT,
|
|
$this->get_lang('OAuth2Id'),
|
|
''
|
|
);
|
|
}
|
|
|
|
public static function isFirstLoginAfterAuthSource(int $userId): bool
|
|
{
|
|
$em = Database::getManager();
|
|
|
|
$lastLogin = $em
|
|
->getRepository(TrackELogin::class)
|
|
->findOneBy(
|
|
['loginUserId' => $userId],
|
|
['loginDate' => 'DESC']
|
|
)
|
|
;
|
|
|
|
if (!$lastLogin) {
|
|
return false;
|
|
}
|
|
|
|
$objExtraField = new ExtraField('user');
|
|
$field = $objExtraField->getHandlerEntityByFieldVariable(self::EXTRA_FIELD_OAUTH2_ID);
|
|
|
|
$fieldValue = $em
|
|
->getRepository(ExtraFieldValues::class)
|
|
->findOneBy(
|
|
['itemId' => $userId, 'field' => $field]
|
|
)
|
|
;
|
|
|
|
if (!$fieldValue) {
|
|
return false;
|
|
}
|
|
|
|
return $fieldValue->getCreatedAt() >= $lastLogin->getLoginDate();
|
|
}
|
|
|
|
private function mapUserStatusFromResponse(array $response, int $defaultStatus = STUDENT): int
|
|
{
|
|
$status = $this->getValueByKey(
|
|
$response,
|
|
$this->get(self::SETTING_RESPONSE_RESOURCE_OWNER_STATUS),
|
|
$defaultStatus
|
|
);
|
|
|
|
$responseStatus = [];
|
|
|
|
if ($teacherStatus = $this->get(self::SETTING_RESPONSE_RESOURCE_OWNER_TEACHER_STATUS)) {
|
|
$responseStatus[COURSEMANAGER] = $teacherStatus;
|
|
}
|
|
|
|
if ($sessAdminStatus = $this->get(self::SETTING_RESPONSE_RESOURCE_OWNER_SESSADMIN_STATUS)) {
|
|
$responseStatus[SESSIONADMIN] = $sessAdminStatus;
|
|
}
|
|
|
|
if ($drhStatus = $this->get(self::SETTING_RESPONSE_RESOURCE_OWNER_DRH_STATUS)) {
|
|
$responseStatus[DRH] = $drhStatus;
|
|
}
|
|
|
|
if ($studentStatus = $this->get(self::SETTING_RESPONSE_RESOURCE_OWNER_STUDENT_STATUS)) {
|
|
$responseStatus[STUDENT] = $studentStatus;
|
|
}
|
|
|
|
if ($anonStatus = $this->get(self::SETTING_RESPONSE_RESOURCE_OWNER_ANON_STATUS)) {
|
|
$responseStatus[ANONYMOUS] = $anonStatus;
|
|
}
|
|
|
|
$map = array_flip($responseStatus);
|
|
|
|
return $map[$status] ?? $status;
|
|
}
|
|
|
|
/**
|
|
* Extends ArrayAccessorTrait::getValueByKey to return a list of values
|
|
* $key can contain wild card character *
|
|
* It will be replaced by 0, 1, 2 and so on as long as the resulting key exists in $data
|
|
* This is a recursive function, allowing for more than one occurrence of the wild card character.
|
|
*/
|
|
private function getValuesByKey(array $data, string $key, array $default = []): array
|
|
{
|
|
if (!is_string($key) || empty($key) || !count($data)) {
|
|
return $default;
|
|
}
|
|
$pos = strpos($key, '*');
|
|
if ($pos === false) {
|
|
$value = $this->getValueByKey($data, $key);
|
|
|
|
return is_null($value) ? [] : [$value];
|
|
}
|
|
$values = [];
|
|
$beginning = substr($key, 0, $pos);
|
|
$remaining = substr($key, $pos + 1);
|
|
$index = 0;
|
|
do {
|
|
$newValues = $this->getValuesByKey(
|
|
$data,
|
|
$beginning.$index.$remaining
|
|
);
|
|
$values = array_merge($values, $newValues);
|
|
$index++;
|
|
} while ($newValues);
|
|
|
|
return $values;
|
|
}
|
|
|
|
/**
|
|
* @throws Exception
|
|
*/
|
|
private function updateUser($userId, $response)
|
|
{
|
|
$user = UserManager::getRepository()->find($userId);
|
|
$user->setFirstname(
|
|
$this->getValueByKey(
|
|
$response,
|
|
$this->get(
|
|
self::SETTING_RESPONSE_RESOURCE_OWNER_FIRSTNAME
|
|
),
|
|
$user->getFirstname()
|
|
)
|
|
);
|
|
$user->setLastname(
|
|
$this->getValueByKey(
|
|
$response,
|
|
$this->get(self::SETTING_RESPONSE_RESOURCE_OWNER_LASTNAME),
|
|
$user->getLastname()
|
|
)
|
|
);
|
|
$user->setUserName(
|
|
$this->getValueByKey(
|
|
$response,
|
|
$this->get(self::SETTING_RESPONSE_RESOURCE_OWNER_USERNAME),
|
|
$user->getUsername()
|
|
)
|
|
);
|
|
$user->setEmail(
|
|
$this->getValueByKey(
|
|
$response,
|
|
$this->get(self::SETTING_RESPONSE_RESOURCE_OWNER_EMAIL),
|
|
$user->getEmail()
|
|
)
|
|
);
|
|
$status = $this->mapUserStatusFromResponse(
|
|
$response,
|
|
$user->getStatus()
|
|
);
|
|
$user->setStatus($status);
|
|
$user->setAuthSource('oauth2');
|
|
$configFilePath = __DIR__.'/../config.php';
|
|
if (file_exists($configFilePath)) {
|
|
require_once $configFilePath;
|
|
$functionName = 'oauth2UpdateUserFromResourceOwnerDetails';
|
|
if (function_exists($functionName)) {
|
|
$functionName($response, $user);
|
|
}
|
|
}
|
|
|
|
try {
|
|
UserManager::getManager()->updateUser($user);
|
|
} catch (UniqueConstraintViolationException $exception) {
|
|
throw new Exception(get_lang('UserNameUsedTwice'));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Updates the Access URLs associated to a user
|
|
* according to the OAuth2 server response resource owner
|
|
* if multi-URL is enabled and SETTING_RESPONSE_RESOURCE_OWNER_URLS defined.
|
|
*
|
|
* @param $userId integer
|
|
* @param $response array
|
|
*/
|
|
private function updateUserUrls($userId, $response)
|
|
{
|
|
if (api_is_multiple_url_enabled()) {
|
|
$key = (string) $this->get(self::SETTING_RESPONSE_RESOURCE_OWNER_URLS);
|
|
if (!empty($key)) {
|
|
$availableUrls = [];
|
|
foreach (UrlManager::get_url_data() as $existingUrl) {
|
|
$urlId = $existingUrl['id'];
|
|
$availableUrls[strval($urlId)] = $urlId;
|
|
$availableUrls[$existingUrl['url']] = $urlId;
|
|
}
|
|
$allowedUrlIds = [];
|
|
foreach ($this->getValuesByKey($response, $key) as $value) {
|
|
if (array_key_exists($value, $availableUrls)) {
|
|
$allowedUrlIds[] = $availableUrls[$value];
|
|
} else {
|
|
$newValue = ($value[-1] === '/') ? substr($value, 0, -1) : $value.'/';
|
|
if (array_key_exists($newValue, $availableUrls)) {
|
|
$allowedUrlIds[] = $availableUrls[$newValue];
|
|
}
|
|
}
|
|
}
|
|
$grantedUrlIds = [];
|
|
foreach (UrlManager::get_access_url_from_user($userId) as $grantedUrl) {
|
|
$grantedUrlIds[] = $grantedUrl['access_url_id'];
|
|
}
|
|
foreach (array_diff($grantedUrlIds, $allowedUrlIds) as $extraUrlId) {
|
|
UrlManager::delete_url_rel_user($userId, $extraUrlId);
|
|
}
|
|
foreach (array_diff($allowedUrlIds, $grantedUrlIds) as $missingUrlId) {
|
|
UrlManager::add_user_to_url($userId, $missingUrlId);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private function log(string $key, string $content)
|
|
{
|
|
if (self::DEBUG) {
|
|
error_log("OAuth2 plugin: $key: $content");
|
|
}
|
|
}
|
|
}
|