|
|
|
|
@ -2,7 +2,7 @@ |
|
|
|
|
/* For license terms, see /license.txt */ |
|
|
|
|
|
|
|
|
|
use League\OAuth2\Client\Provider\Exception\IdentityProviderException; |
|
|
|
|
use \League\OAuth2\Client\Provider\GenericProvider; |
|
|
|
|
use League\OAuth2\Client\Provider\GenericProvider; |
|
|
|
|
use League\OAuth2\Client\Token\AccessToken; |
|
|
|
|
use League\OAuth2\Client\Tool\ArrayAccessorTrait; |
|
|
|
|
|
|
|
|
|
@ -24,12 +24,12 @@ class OAuth2 extends Plugin |
|
|
|
|
const SETTING_CLIENT_SECRET = 'client_secret'; |
|
|
|
|
|
|
|
|
|
const SETTING_AUTHORIZE_URL = 'authorize_url'; |
|
|
|
|
# const SETTING_SCOPES = 'scopes'; |
|
|
|
|
# const SETTING_SCOPE_SEPARATOR = 'scope_separator'; |
|
|
|
|
// const SETTING_SCOPES = 'scopes'; |
|
|
|
|
// const SETTING_SCOPE_SEPARATOR = 'scope_separator'; |
|
|
|
|
|
|
|
|
|
const SETTING_ACCESS_TOKEN_URL = 'access_token_url'; |
|
|
|
|
const SETTING_ACCESS_TOKEN_METHOD = 'access_token_method'; |
|
|
|
|
# const SETTING_ACCESS_TOKEN_RESOURCE_OWNER_ID = 'access_token_resource_owner_id'; |
|
|
|
|
// const SETTING_ACCESS_TOKEN_RESOURCE_OWNER_ID = 'access_token_resource_owner_id'; |
|
|
|
|
|
|
|
|
|
const SETTING_RESOURCE_OWNER_DETAILS_URL = 'resource_owner_details_url'; |
|
|
|
|
|
|
|
|
|
@ -68,8 +68,8 @@ class OAuth2 extends Plugin |
|
|
|
|
self::SETTING_CLIENT_SECRET => 'text', |
|
|
|
|
|
|
|
|
|
self::SETTING_AUTHORIZE_URL => 'text', |
|
|
|
|
# self::SETTING_SCOPES => 'text', |
|
|
|
|
# self::SETTING_SCOPE_SEPARATOR => 'text', |
|
|
|
|
// self::SETTING_SCOPES => 'text', |
|
|
|
|
// self::SETTING_SCOPE_SEPARATOR => 'text', |
|
|
|
|
|
|
|
|
|
self::SETTING_ACCESS_TOKEN_URL => 'text', |
|
|
|
|
self::SETTING_ACCESS_TOKEN_METHOD => [ |
|
|
|
|
@ -77,9 +77,9 @@ class OAuth2 extends Plugin |
|
|
|
|
'options' => [ |
|
|
|
|
GenericProvider::METHOD_POST => 'POST', |
|
|
|
|
GenericProvider::METHOD_GET => 'GET', |
|
|
|
|
] |
|
|
|
|
], |
|
|
|
|
], |
|
|
|
|
# self::SETTING_ACCESS_TOKEN_RESOURCE_OWNER_ID => 'text', |
|
|
|
|
// self::SETTING_ACCESS_TOKEN_RESOURCE_OWNER_ID => 'text', |
|
|
|
|
|
|
|
|
|
self::SETTING_RESOURCE_OWNER_DETAILS_URL => 'text', |
|
|
|
|
|
|
|
|
|
@ -133,13 +133,13 @@ class OAuth2 extends Plugin |
|
|
|
|
'redirectUri' => api_get_path(WEB_PLUGIN_PATH).'oauth2/src/callback.php', |
|
|
|
|
|
|
|
|
|
'urlAuthorize' => $this->get(self::SETTING_AUTHORIZE_URL), |
|
|
|
|
# 'scopes' => $this->get(self::SETTING_SCOPES) or null, |
|
|
|
|
# 'scopeSeparator' => $this->get(self::SETTING_SCOPE_SEPARATOR) ?: ',', |
|
|
|
|
// 'scopes' => $this->get(self::SETTING_SCOPES) or null, |
|
|
|
|
// 'scopeSeparator' => $this->get(self::SETTING_SCOPE_SEPARATOR) ?: ',', |
|
|
|
|
|
|
|
|
|
'urlAccessToken' => $this->get(self::SETTING_ACCESS_TOKEN_URL), |
|
|
|
|
'accessTokenMethod' => $this->get(self::SETTING_ACCESS_TOKEN_METHOD) ?: GenericProvider::METHOD_POST, |
|
|
|
|
#'accessTokenResourceOwnerId' => $this->get(self::SETTING_ACCESS_TOKEN_RESOURCE_OWNER_ID) |
|
|
|
|
# ?: GenericProvider::ACCESS_TOKEN_RESOURCE_OWNER_ID, |
|
|
|
|
//'accessTokenResourceOwnerId' => $this->get(self::SETTING_ACCESS_TOKEN_RESOURCE_OWNER_ID) |
|
|
|
|
// ?: GenericProvider::ACCESS_TOKEN_RESOURCE_OWNER_ID, |
|
|
|
|
|
|
|
|
|
'urlResourceOwnerDetails' => $this->get(self::SETTING_RESOURCE_OWNER_DETAILS_URL), |
|
|
|
|
|
|
|
|
|
@ -150,15 +150,119 @@ class OAuth2 extends Plugin |
|
|
|
|
); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* @throws IdentityProviderException |
|
|
|
|
* |
|
|
|
|
* @return array user information, as returned by api_get_user_info(userId) |
|
|
|
|
* |
|
|
|
|
* @var AccessToken |
|
|
|
|
* @var GenericProvider |
|
|
|
|
*/ |
|
|
|
|
public function getUserInfo($provider, $accessToken) |
|
|
|
|
{ |
|
|
|
|
$url = $provider->getResourceOwnerDetailsUrl($accessToken); |
|
|
|
|
$request = $provider->getAuthenticatedRequest($provider::METHOD_GET, $url, $accessToken); |
|
|
|
|
$response = $provider->getParsedResponse($request); |
|
|
|
|
if (false === is_array($response)) { |
|
|
|
|
throw new UnexpectedValueException(get_lang('invalid_json_received_from_provider')); |
|
|
|
|
} |
|
|
|
|
$resourceOwnerId = $this->getValueByKey( |
|
|
|
|
$response, |
|
|
|
|
$this->get(self::SETTING_RESPONSE_RESOURCE_OWNER_ID) |
|
|
|
|
); |
|
|
|
|
if (empty($resourceOwnerId)) { |
|
|
|
|
throw new RuntimeException(get_lang('wrong_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) { |
|
|
|
|
// authenticated user not found in internal database |
|
|
|
|
if ('true' !== $this->get(self::SETTING_CREATE_NEW_USERS)) { |
|
|
|
|
throw new RuntimeException(get_lang('no_user_has_this_oauth_code')); |
|
|
|
|
} |
|
|
|
|
require_once __DIR__.'/../../../main/auth/external_login/functions.inc.php'; |
|
|
|
|
$userId = external_add_user( |
|
|
|
|
[ |
|
|
|
|
'firstname' => $this->getValueByKey($response, $this->get( |
|
|
|
|
self::SETTING_RESPONSE_RESOURCE_OWNER_FIRSTNAME |
|
|
|
|
), get_lang('DefaultFirstname')), |
|
|
|
|
'lastname' => $this->getValueByKey($response, $this->get( |
|
|
|
|
self::SETTING_RESPONSE_RESOURCE_OWNER_LASTNAME |
|
|
|
|
), get_lang('DefaultLastname')), |
|
|
|
|
'status' => $this->getValueByKey($response, $this->get( |
|
|
|
|
self::SETTING_RESPONSE_RESOURCE_OWNER_STATUS |
|
|
|
|
), STUDENT), |
|
|
|
|
'email' => $this->getValueByKey($response, $this->get( |
|
|
|
|
self::SETTING_RESPONSE_RESOURCE_OWNER_EMAIL |
|
|
|
|
), 'oauth2user_'.$resourceOwnerId.'@'.(gethostname() or 'localhost')), |
|
|
|
|
'username' => $this->getValueByKey($response, $this->get( |
|
|
|
|
self::SETTING_RESPONSE_RESOURCE_OWNER_USERNAME |
|
|
|
|
), 'oauth2user_'.$resourceOwnerId), |
|
|
|
|
'auth_source' => 'oauth2', |
|
|
|
|
] |
|
|
|
|
); |
|
|
|
|
if (false === $userId) { |
|
|
|
|
throw new RuntimeException(get_lang('FailedUserCreation')); |
|
|
|
|
} |
|
|
|
|
$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 { |
|
|
|
|
// 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); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
$userInfo = api_get_user_info($userId); |
|
|
|
|
if (empty($userInfo)) { |
|
|
|
|
throw new LogicException(get_lang('internal_error_cannot_get_user_info')); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return $userInfo; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public function getSignInURL() |
|
|
|
|
{ |
|
|
|
|
return api_get_path(WEB_PLUGIN_PATH).$this->get_name().'/src/callback.php'; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public function getLogoutUrl() |
|
|
|
|
{ |
|
|
|
|
return $this->get(self::SETTING_LOGOUT_URL); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* 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'), |
|
|
|
|
'' |
|
|
|
|
); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* 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 |
|
|
|
|
* This is a recursive function, allowing for more than one occurrence of the wild card character. |
|
|
|
|
* |
|
|
|
|
* @param array $data |
|
|
|
|
* @param string $key |
|
|
|
|
* @param array $default |
|
|
|
|
* @param array $default |
|
|
|
|
* |
|
|
|
|
* @return array |
|
|
|
|
*/ |
|
|
|
|
private function getValuesByKey(array $data, $key, $default = []) |
|
|
|
|
@ -169,6 +273,7 @@ class OAuth2 extends Plugin |
|
|
|
|
$pos = strpos($key, '*'); |
|
|
|
|
if ($pos === false) { |
|
|
|
|
$value = $this->getValueByKey($data, $key, null); |
|
|
|
|
|
|
|
|
|
return is_null($value) ? [] : [$value]; |
|
|
|
|
} |
|
|
|
|
$values = []; |
|
|
|
|
@ -178,11 +283,12 @@ class OAuth2 extends Plugin |
|
|
|
|
do { |
|
|
|
|
$newValues = $this->getValuesByKey( |
|
|
|
|
$data, |
|
|
|
|
$beginning . $index . $remaining |
|
|
|
|
$beginning.$index.$remaining |
|
|
|
|
); |
|
|
|
|
$values = array_merge($values, $newValues); |
|
|
|
|
$index ++; |
|
|
|
|
$index++; |
|
|
|
|
} while ($newValues); |
|
|
|
|
|
|
|
|
|
return $values; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@ -228,10 +334,11 @@ class OAuth2 extends Plugin |
|
|
|
|
} |
|
|
|
|
UserManager::getManager()->updateUser($user); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* 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 |
|
|
|
|
* if multi-URL is enabled and SETTING_RESPONSE_RESOURCE_OWNER_URLS defined. |
|
|
|
|
* |
|
|
|
|
* @param $userId integer |
|
|
|
|
* @param $response array |
|
|
|
|
@ -272,112 +379,4 @@ class OAuth2 extends Plugin |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* @return array user information, as returned by api_get_user_info(userId) |
|
|
|
|
* @throws IdentityProviderException |
|
|
|
|
* @var AccessToken $accessToken |
|
|
|
|
* @var GenericProvider $provider |
|
|
|
|
*/ |
|
|
|
|
public function getUserInfo($provider, $accessToken) |
|
|
|
|
{ |
|
|
|
|
$url = $provider->getResourceOwnerDetailsUrl($accessToken); |
|
|
|
|
$request = $provider->getAuthenticatedRequest($provider::METHOD_GET, $url, $accessToken); |
|
|
|
|
$response = $provider->getParsedResponse($request); |
|
|
|
|
if (false === is_array($response)) { |
|
|
|
|
throw new UnexpectedValueException( |
|
|
|
|
get_lang('invalid_json_received_from_provider') |
|
|
|
|
); |
|
|
|
|
} |
|
|
|
|
$resourceOwnerId = $this->getValueByKey( |
|
|
|
|
$response, |
|
|
|
|
$this->get(self::SETTING_RESPONSE_RESOURCE_OWNER_ID) |
|
|
|
|
); |
|
|
|
|
if (empty($resourceOwnerId)) { |
|
|
|
|
throw new RuntimeException( |
|
|
|
|
get_lang('wrong_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) { |
|
|
|
|
// authenticated user not found in internal database |
|
|
|
|
if ('true' !== $this->get(self::SETTING_CREATE_NEW_USERS)) { |
|
|
|
|
throw new RuntimeException(get_lang('no_user_has_this_oauth_code')); |
|
|
|
|
} |
|
|
|
|
require_once __DIR__.'/../../../main/auth/external_login/functions.inc.php'; |
|
|
|
|
$userId = external_add_user( |
|
|
|
|
[ |
|
|
|
|
'firstname' => $this->getValueByKey($response, $this->get( |
|
|
|
|
self::SETTING_RESPONSE_RESOURCE_OWNER_FIRSTNAME |
|
|
|
|
), get_lang('DefaultFirstname')), |
|
|
|
|
'lastname' => $this->getValueByKey($response, $this->get( |
|
|
|
|
self::SETTING_RESPONSE_RESOURCE_OWNER_LASTNAME |
|
|
|
|
), get_lang('DefaultLastname')), |
|
|
|
|
'status' => $this->getValueByKey($response, $this->get( |
|
|
|
|
self::SETTING_RESPONSE_RESOURCE_OWNER_STATUS |
|
|
|
|
), STUDENT), |
|
|
|
|
'email' => $this->getValueByKey($response, $this->get( |
|
|
|
|
self::SETTING_RESPONSE_RESOURCE_OWNER_EMAIL |
|
|
|
|
), 'oauth2user_'.$resourceOwnerId.'@'.(gethostname() or 'localhost')), |
|
|
|
|
'username' => $this->getValueByKey($response, $this->get( |
|
|
|
|
self::SETTING_RESPONSE_RESOURCE_OWNER_USERNAME |
|
|
|
|
), 'oauth2user_'.$resourceOwnerId), |
|
|
|
|
'auth_source' => 'oauth2', |
|
|
|
|
] |
|
|
|
|
); |
|
|
|
|
if (false === $userId) { |
|
|
|
|
throw new RuntimeException(get_lang('FailedUserCreation')); |
|
|
|
|
} |
|
|
|
|
$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 { |
|
|
|
|
// 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); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
$userInfo = api_get_user_info($userId); |
|
|
|
|
if (empty($userInfo)) { |
|
|
|
|
throw new LogicException( |
|
|
|
|
get_lang('internal_error_cannot_get_user_info') |
|
|
|
|
); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return $userInfo; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public function getSignInURL() |
|
|
|
|
{ |
|
|
|
|
return api_get_path(WEB_PLUGIN_PATH).$this->get_name().'/src/callback.php'; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public function getLogoutUrl() |
|
|
|
|
{ |
|
|
|
|
return $this->get(self::SETTING_LOGOUT_URL); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* 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'), |
|
|
|
|
'' |
|
|
|
|
); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|