Files
Phraseanet/lib/Alchemy/Phrasea/Authentication/Provider/Openid.php
Aina Sitraka 32aa1db879 PHRAS-4108 openid : add claims mapping and groups filtering (#4563)
* openid add group mapping

* add  migration patch for configuration injection
2024-12-12 15:46:04 +01:00

900 lines
37 KiB
PHP

<?php
/*
* This file is part of Phraseanet
*
* (c) 2005-2022 Alchemy
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Alchemy\Phrasea\Authentication\Provider;
use Alchemy\Phrasea\Authentication\Exception\NotAuthenticatedException;
use Alchemy\Phrasea\Authentication\Provider\Token\Identity;
use Alchemy\Phrasea\Authentication\Provider\Token\Token;
use Alchemy\Phrasea\Exception\InvalidArgumentException;
use Alchemy\Phrasea\Model\Entities\User;
use Alchemy\Phrasea\Model\Entities\UsrAuthProvider;
use Exception;
use Guzzle\Common\Exception\GuzzleException;
use Guzzle\Http\Client as Guzzle;
use Guzzle\Http\ClientInterface;
use RandomLibtest\Mocks\Random\Generator as RandomGenerator;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
use Symfony\Component\Routing\Generator\UrlGenerator;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
class Openid extends AbstractProvider
{
/**
* @var string|null
*/
private $iconUri;
/**
* @var Guzzle
*/
private $client;
/**
* @var array
*/
private $config;
private function debug($s = '')
{
static $lastfile = "?";
if(array_key_exists('debug', $this->config) && $this->config['debug'] === true) {
$bt = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
if ($bt[0]['file'] != $lastfile) {
file_put_contents('/var/alchemy/Phraseanet/logs/openid.log', sprintf("FILE %s \n", ($lastfile = $bt[0]['file'])), FILE_APPEND);
}
$s = sprintf("LINE (%d) : %s\n", $bt[0]['line'], $s);
file_put_contents('/var/alchemy/Phraseanet/logs/openid.log', $s, FILE_APPEND);
}
}
public function __construct(UrlGenerator $urlGenerator, SessionInterface $session, array $config, Guzzle $client)
{
parent::__construct($urlGenerator, $session);
$this->config = $config;
if(!array_key_exists('model-gpfx', $this->config)) {
$this->config['model-gpfx'] = '_G_';
}
if(!array_key_exists('model-upfx', $this->config)) {
$this->config['model-upfx'] = '_U_';
}
if(!array_key_exists('metamodel', $this->config)) {
$this->config['metamodel'] = '_metamodel';
}
if(!array_key_exists('auto-logout', $this->config)) {
$this->config['auto-logout'] = false;
}
if(!array_key_exists('auto-connect-idp-name', $this->config)) {
$this->config['auto-connect-idp-name'] = null;
}
$this->client = $client;
$this->iconUri = array_key_exists('icon-uri', $config) ? $config['icon-uri'] : null; // if not set, will fallback on default icon
}
/**
* {@inheritdoc}
*/
public static function create(UrlGenerator $generator, SessionInterface $session, array $options): AbstractProvider
{
foreach (['client-id', 'client-secret', 'base-url', 'realm-name'] as $parm) {
if (!isset($options[$parm]) || (trim($options[$parm]) == '')) {
throw new InvalidArgumentException(sprintf('Missing Phraseanet "%s" parameter in conf/authentification/providers', $parm));
}
}
$guzzle = new Guzzle();
$guzzle->setSslVerification(false, false, 0);
return new self($generator, $session, $options, $guzzle);
}
/**
* {@inheritdoc}
*/
public function getName(): string
{
return 'openid';
}
/**
* @param ClientInterface $client
*
* @return self
*/
public function setGuzzleClient(ClientInterface $client): self
{
$this->client = $client;
return $this;
}
/**
* @return ClientInterface
*/
public function getGuzzleClient()
{
return $this->client;
}
/**
* {@inheritdoc}
*/
public function authenticate(array $params = array()): RedirectResponse
{
$this->debug();
$this->session->invalidate(0);
/*
* for oauth2 the callback url(s) MUST be fully static. One CAN register multiple possible urls, like
* - one for phraseanet home : already static
* - one for phraseanet oauth api
* - ... ?
* api client may want to include static/variable params to be used for final redirect (eg. parade),
* we pass those in session
* lib/Alchemy/Phrasea/Controller/Api/OAuth2Controller::authorizeCallbackAction(...) will restore params
*/
$this->session->set($this->getId() . ".parms", array_merge(['providerId' => $this->getId()], $params));
$this->debug(sprintf("authenticate params saved : session[%s] = %s",
$this->getId() . ".parms",
var_export($params, true)
));
$params = ['providerId' => $this->getId()]; // the only required parm (constant)
$this->debug(sprintf("redirect_uri params (cleaned) = %s", var_export($params, true)));
$redirect_uri = $this->generator->generate(
'login_authentication_provider_callback',
$params,
UrlGeneratorInterface::ABSOLUTE_URL
);
$this->debug(sprintf("redirect_uri = %s", $redirect_uri));
$state = $this->createState();
$this->session->set($this->getId() . '.provider.state', $state);
$parms = [
'client_id' => $this->config['client-id'],
'state' => $state,
'scope' => 'openid',
'redirect_uri' => $redirect_uri,
'response_type' => "code"
];
if($this->config['auto-connect-idp-name']) {
$url = sprintf("%s/realms/%s/protocol/openid-connect/auth?kc_idp_hint=%s&%s",
$this->config['base-url'],
urlencode($this->config['realm-name']),
urlencode($this->config['auto-connect-idp-name']),
http_build_query($parms, '', '&')
);
} else {
$url = sprintf("%s/realms/%s/protocol/openid-connect/auth?%s",
$this->config['base-url'],
urlencode($this->config['realm-name']),
http_build_query($parms, '', '&')
);
}
$this->debug(sprintf("go to url = %s", $url));
return new RedirectResponse($url);
}
/**
* {@inheritdoc}
*/
public function logout()
{
$this->debug("logout ?");
if($this->config['auto-logout']) {
// too bad: getting the logout page is not enough...
// $url = "/logout";
// $guzzleRequest = $this->client->get($url);
// $response = $guzzleRequest->send();
// $this->debug($response->getBody());
// return null;
// ... we really need to redirect to it, which will prevent phr to redirect to his home
$url = sprintf("%s/realms/%s/protocol/openid-connect/logout",
$this->config['base-url'],
urlencode($this->config['realm-name'])
);
return new RedirectResponse($url);
}
return null;
}
public function logoutAndRedirect($redirect_uri)
{
$this->debug("logoutAndRedirect ?");
if($this->config['auto-logout']) {
$url = sprintf("%s/realms/%s/protocol/openid-connect/logout?post_logout_redirect_uri=%s&id_token_hint=%s",
$this->config['base-url'],
urlencode($this->config['realm-name']),
urlencode($redirect_uri),
$this->session->get($this->getId() . '.provider.id_token')
);
return new RedirectResponse($url);
}
return null;
}
/**
* {@inheritdoc}
*/
public function onCallback(Request $request)
{
$this->debug();
if (!$this->session->has($this->getId() . '.provider.state')) {
throw new NotAuthenticatedException('No state value in session ; CSRF try ?');
}
$this->debug();
if ($request->query->get('state') !== $this->session->remove($this->getId() . '.provider.state')) {
throw new NotAuthenticatedException('Invalid state value ; CSRF try ?');
}
$this->debug();
try {
$url = sprintf("%s/realms/%s/protocol/openid-connect/token",
$this->config['base-url'],
urlencode($this->config['realm-name'])
);
$guzzleRequest = $this->client->post($url);
$guzzleRequest->addPostFields([
'grant_type' => "authorization_code",
'code' => $request->query->get('code'),
'redirect_uri' => $this->generator->generate(
'login_authentication_provider_callback',
['providerId' => $this->getId()],
UrlGeneratorInterface::ABSOLUTE_URL
),
'client_id' => $this->config['client-id'],
'client_secret' => $this->config['client-secret'],
]);
$guzzleRequest->setHeader('Accept', 'application/json');
$this->debug();
$response = $guzzleRequest->send();
$this->debug();
}
catch (GuzzleException $e) {
$this->debug($e->getMessage());
throw new NotAuthenticatedException('Guzzle error while authentication', $e->getCode(), $e);
}
if (200 !== $response->getStatusCode()) {
$this->debug();
throw new NotAuthenticatedException('Error while getting access_token');
}
$this->debug();
$data = @json_decode($response->getBody(true), true);
$this->debug();
if (JSON_ERROR_NONE !== json_last_error()) {
$this->debug();
throw new NotAuthenticatedException('Error while decoding token response, unable to parse JSON.');
}
$this->debug(var_export($data, true));
$this->session->remove($this->getId() . '.provider.state');
$this->session->set($this->getId() . '.provider.access_token', $data['access_token']);
// id_token_hint used when logout
$this->session->set($this->getId() . '.provider.id_token', $data['id_token']);
$this->session->set('provider.token_info', $data);
try {
$this->debug();
$uri = sprintf("%s/realms/%s/protocol/openid-connect/userinfo",
$this->config['base-url'],
urlencode($this->config['realm-name'])
);
$request = $this->client->get($uri);
$request->setHeader('Authorization', 'Bearer '. $data['access_token']);
$this->debug();
$response = $request->send();
$this->debug();
}
catch (GuzzleException $e) {
$this->debug($e->getMessage());
throw new NotAuthenticatedException('Guzzle error while authentication', $e->getCode(), $e);
}
if (200 !== $response->getStatusCode()) {
$this->debug();
throw new NotAuthenticatedException('Error while retrieving user info, invalid status code.');
}
$this->debug();
$data = @json_decode($response->getBody(true), true);
$this->debug(var_export($data, true));
if (JSON_ERROR_NONE !== json_last_error()) {
$this->debug();
throw new NotAuthenticatedException('Error while retrieving user info, unable to parse JSON.');
}
$this->debug();
$usegroups = isset($this->config['usegroups']) ? $this->config['usegroups'] : false;
$idKey = isset($this->config['fieldmap']['id']) ? $this->config['fieldmap']['id'] : 'sub';
$loginKey = isset($this->config['fieldmap']['login']) ? $this->config['fieldmap']['login'] : 'email';
$firstnameKey = isset($this->config['fieldmap']['firstname']) ? $this->config['fieldmap']['firstname'] : 'given_name';
$lastnameKey = isset($this->config['fieldmap']['lastname']) ? $this->config['fieldmap']['lastname'] : 'family_name';
$emailKey = isset($this->config['fieldmap']['email']) ? $this->config['fieldmap']['email'] : 'email';
$groupsKey = isset($this->config['fieldmap']['groups']) ? $this->config['fieldmap']['groups'] : 'groups';
$distantUserId = $data['sub'];
if (!\Swift_Validate::email($data[$loginKey]) && isset($data['email'])) {
$loginKey = 'email';// login to be an email
}
$userUA = $this->CreateUser([
'id' => $data[$idKey],
'login' => $userName = $data[$loginKey],
'firstname' => isset($data[$firstnameKey]) ? $data[$firstnameKey] : '',
'lastname' => isset($data[$lastnameKey]) ? $data[$lastnameKey] : '' ,
'email' => isset($data[$emailKey]) ? $data[$emailKey] : '',
'_groups' => isset($data[$groupsKey]) && $usegroups ? $this->filterGroups($data[$groupsKey]) : ''
]);
$userAuthProviderRepository = $this->getUsrAuthProviderRepository();
$userAuthProvider = $userAuthProviderRepository
->findWithProviderAndId($this->getId(), $distantUserId);
if (!$userAuthProvider) {
$manager = $this->getEntityManager();
$usrAuthProvider = new UsrAuthProvider();
$usrAuthProvider->setDistantId($distantUserId);
$usrAuthProvider->setProvider($this->getId());
$usrAuthProvider->setUser($userUA);
try {
$manager->persist($usrAuthProvider);
$manager->flush();
}
catch (\Exception $e) {
// no-op
$this->debug();
}
}
$this->session->set($this->getId() . ".provider.id", $distantUserId);
$this->session->set($this->getId() . ".provider.username", $userName);
$this->debug(sprintf("session->set('%s', '%s')", $this->getId() . ".provider.id", $distantUserId));
$this->debug(sprintf("session->set('%s', '%s')", $this->getId() . ".provider.username", $userName));
}
/**
* {@inheritdoc}
*/
public function getToken(): Token
{
$this->debug();
$distantUserId = $this->session->get($this->getId() . '.provider.id');
$this->debug(sprintf("session->get('%s') ==> '%s')", $this->getId() . ".provider.id", $distantUserId));
if ('' === trim($distantUserId)) {
$this->debug();
throw new NotAuthenticatedException($this->getId() . ' has not authenticated');
}
$this->debug();
$token = new Token($this, $distantUserId);
$this->debug();
return $token;
}
/**
* {@inheritdoc}
*/
public function getIdentity(): Identity
{
$this->debug();
$identity = new Identity();
try {
$uri = sprintf("%s/realms/%s/protocol/openid-connect/userinfo",
$this->config['base-url'],
urlencode($this->config['realm-name'])
);
$request = $this->client->get($uri);
$request->setHeader('Authorization', 'Bearer '. $this->session->get($this->getId() . '.provider.access_token'));
$response = $request->send();
}
catch (GuzzleException $e) {
$this->debug();
throw new NotAuthenticatedException('Error while retrieving user info', $e->getCode(), $e);
}
if (200 !== $response->getStatusCode()) {
$this->debug();
throw new NotAuthenticatedException('Error while retrieving user info');
}
$data = @json_decode($response->getBody(true), true);
if (JSON_ERROR_NONE !== json_last_error()) {
$this->debug();
throw new NotAuthenticatedException('Error while parsing json');
}
$this->debug();
$identity->set(Identity::PROPERTY_EMAIL, isset($data['email']) ? $data['email'] : '');
$identity->set(Identity::PROPERTY_ID, $data['sub']);
$identity->set(Identity::PROPERTY_USERNAME, $data['preferred_username']);
$this->debug();
return $identity;
}
/**
* @param array $data
* @return User|null
* @throws Exception
*/
private function CreateUser(Array $data)
{
$userManipulator = $this->getUserManipulator();
$userRepository = $this->getUserRepository();
$ACLProvider = $this->getACLProvider();
$ret = null;
$login = trim($data['login']);
$this->debug(sprintf("login=%s \n", var_export($login, true)));
if ($login == "") {
$this->debug("login is empty, user not created \n");
}
/** @var User $userUA */
$userUA = $userRepository->findByLogin($login);
if (!$userUA) {
// need to create the user
$this->debug(sprintf("creating user \"%s\" \n", $login));
$tmp_email = str_replace(['.', '@'], ['_', '_'], $login) . "@nomail.eu";
$userUA = $userManipulator->createUser($login, 'user_tmp_pwd', $tmp_email, false);
if ($userUA) {
$this->debug(sprintf("found user \"%s\" with id=%s \n", $login, $userUA->getId()));
// if the id provider does NOT return groups, the new user will get "birth" privileges
if (!is_array($data['_groups']) && array_key_exists('birth-group', $this->config) && trim($this->config['birth-group']) !== '') {
$data['_groups'] = [trim($this->config['birth-group'])];
}
}
else {
$this->debug(sprintf("failed to create user \"%s\" \n", $login));
}
}
else {
// the user already exists
$this->debug(sprintf("found user \"%s\" with id=%s \n", $login, $userUA->getId()));
// if the id provider does return groups, then revoke privileges
if (is_array($data['_groups'])) {
$appbox = $this->getAppbox();
$all_base_ids = [];
foreach ($appbox->get_databoxes() as $databox) {
foreach ($databox->get_collections() as $collection) {
$all_base_ids[] = $collection->get_base_id();
}
}
$userACL = $ACLProvider->get($userUA);
$userACL->revoke_access_from_bases($all_base_ids)->revoke_unused_sbas_rights();
$this->debug(sprintf("revoked from=%s \n", var_export($all_base_ids, true)));
}
}
// here we should have a user
if ($userUA) {
$this->debug(sprintf("User id=%s \n", $userUA->getId()));
// apply groups
if (is_array($data['_groups'])) {
$userACL = $ACLProvider->get($userUA);
$models = [];
// change groups to models
foreach ($data['_groups'] as $grp) {
$models[] = ['name' => $this->config['model-gpfx'] . $grp, 'autocreate' => true];
}
// add "everyone-group"
if(array_key_exists('everyone-group', $this->config) && trim($this->config['everyone-group']) !== '') {
$models[] = ['name' => $this->config['model-gpfx'] . trim($this->config['everyone-group']), 'autocreate' => true];
}
// add a specific model for the user
$models[] = ['name' => $this->config['model-upfx'] . $login, 'autocreate' => false];
$this->debug(sprintf("models=%s \n", var_export($models, true)));
// if we need those (in case of creation of a model), they will be set only once
$metaModelUA = $metaModelBASES = $metaModelOwnerUA = null;
foreach ($models as $model) {
$this->debug(sprintf("searching model '%s' \n", $model['name']));
// we check if the model exits
$modelUA = $userRepository->findByLogin($model['name']);
if (!$modelUA) {
if ($model['autocreate'] == true) {
$this->debug(sprintf("model '%s' not found \n", $model['name']));
// the model does not exist, so create it
//
// if not already known, get the metamodel
if ($metaModelUA === null) {
$this->debug(sprintf("searching metamodel '%s'... \n", $this->config['metamodel']));
$metaModelUA = $userRepository->findByLogin($this->config['metamodel']);
if ($metaModelUA) {
$this->debug(sprintf("metaModelID=%s \n", print_r($metaModelUA->getId(), true)));
// metamodel found, get some infos...
// ... get acl
$metaModelACL = $ACLProvider->get($metaModelUA);
// ... then list of bases
$metaModelBASES = $metaModelACL->get_granted_base();
// ... in fact we simply need an array of base_ids, and base_id is the keys of the array, so switch
$metaModelBASES = array_keys($metaModelBASES);
if ($metaModelUA->isTemplate()) {
$metaModelOwnerUA = $metaModelUA->getTemplateOwner();
$this->debug(sprintf("metamodel is a model, owner_id=%s \n", print_r($metaModelOwnerUA->getId(), true)));
}
$this->debug(sprintf("metamodel granted on bases '%s' \n", print_r($metaModelBASES, true)));
}
else {
$this->debug("metamodel not found \n");
$metaModelUA = false; // don't search again
}
}
// now we can create the model only if we found the metamodel
if ($metaModelUA) {
$this->debug(sprintf("creating model '%s'... \n", $model['name']));
// create the model user...
$modelUA = $userManipulator->createUser($model['name'], 'model_pwd', null, false);
$this->debug(sprintf("model '%s' created with modelID=%s... \n", $model['name'], print_r($modelUA->getId(), true)));
if ($metaModelOwnerUA) {
$modelUA->setTemplateOwner($metaModelOwnerUA);
$this->debug(sprintf("model '%s' set as model, owner_id=%s... \n", $model['name'], print_r($metaModelOwnerUA->getId(), true)));
}
// ... then copy acl of every sbas
$modelACL = $ACLProvider->get($modelUA);
$modelACL->apply_model($metaModelUA, $metaModelBASES);
$this->debug(sprintf(" ... and granted on bases %s \n", print_r($metaModelBASES, true)));
}
}
}
else {
// the model already exists
$this->debug(sprintf("model '%s' already exists, id=%s \n", $model['name'], print_r($modelUA->getId(), true)));
}
// here we should have the model, except "user" models which are not automatically created
if ($modelUA) {
$this->debug(sprintf(" ... modelID=%s \n", print_r($modelUA->getId(), true)));
// here we have the model so get some infos about it
$modelACL = $ACLProvider->get($modelUA);
$modelBASES = $modelACL->get_granted_base();
// ... in fact we simply need an array of base_ids, and base_id is the keys of the array, so switch
$modelBASES = array_keys($modelBASES);
$this->debug(sprintf("model granted on bases '%s' \n", print_r($modelBASES, true)));
// ... then copy acl of every sbas
$userACL->apply_model($modelUA, $modelBASES);
$this->debug(sprintf("user '%s' granted on bases %s \n", $login, print_r($modelBASES, true)));
}
else {
$this->debug(sprintf("no model '%s' \n", $model['name']));
}
}
$userACL->inject_rights();
}
// now update infos of the user
if (!is_null($data['firstname']) && ($v = trim($data['firstname'])) != '') {
$userUA->setFirstName($v);
}
if (!is_null($data['firstname']) && ($v = trim($data['lastname'])) != '') {
$userUA->setLastName($v);
}
$mail = ""; // mail is a special case
try {
if (($v = trim($data['email'])) != '') {
$mail = $v;
}
}
catch (Exception $e) {
// no-op
}
if ($mail != $userUA->getEmail()) {
try {
$this->debug("unsetting former email of user");
$userManipulator->setEmail($userUA, null);
if ($mail != "") {
$this->debug(sprintf("setting email '%s' to user", $mail));
$dupUserUA = $userRepository->findByEmail($mail);
if ($dupUserUA == null) {
// ok we can set the mail
$userManipulator->setEmail($userUA, $mail);
$this->debug(sprintf("email '%s' set to user", $mail));
}
else {
$this->debug(sprintf("warning : another user (id=%s) already has email '%s', email not set", $dupUserUA->getId(), $mail));
}
}
}
catch (Exception $e) {
// no-op
$this->debug(var_export($e->getMessage(), true));
}
}
else {
$this->debug(sprintf("email '%s' does not change\n", $mail));
}
// yes we are logged !
if (isset($this->config['exclusive']) && $this->config['exclusive'] == true) {
// reset the password
// if it is an existing user, the user cannot login from the default phraseanet login
// cannot renew her password
/** @var RandomGenerator $randomGenerator */
$randomGenerator = $this->getRandomGenerator();
$password = $randomGenerator->generateString(16);
$userUA->setPassword($password);
$userUA->setCanRenewPassword(false);
}
$this->debug(sprintf("returning user id=%s", $userUA->getId()));
$ret = $userUA; // ->getId();
}
return $ret;
}
private function filterGroups($groups)
{
$this->debug(sprintf("filtering openid groups :\n%s", print_r($groups, true)));
$ret = [];
if ($this->config['groupmask']) {
$this->debug(sprintf("filtering groups with regexp : \"%s\"", $this->config['groupmask']));
foreach ($groups as $grp) {
$matches = [];
$retpreg = preg_match_all($this->config['groupmask'], $grp, $matches, PREG_SET_ORDER);
$this->debug(sprintf("preg_match('%s', '%s', ...)\n - returned %s \n - matches = %s "
, $this->config['groupmask'], $grp
, print_r($retpreg, true), print_r($matches, true)));
foreach ($matches as $match) {
if (count($match)>0 && isset($match[1]) && !array_key_exists($match[1], $ret)) {
$ret[] = $match[1];
}
}
}
} else {
$this->debug(sprintf("no groupmask defined, openid groups ignored"));
}
$this->debug(sprintf("filtered groups :\n%s", print_r($ret, true)));
return empty($ret) ? '' : $ret ;
}
/**
* {@inheritdoc}
*/
public function getIconURI()
{
return $this->iconUri ?: 'data:image/png;base64,'
. 'iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAAAXNSR0IArs4c6QAA'
. 'AJZlWElmTU0AKgAAAAgABQESAAMAAAABAAEAAAEaAAUAAAABAAAASgEbAAUAAAAB'
. 'AAAAUgExAAIAAAARAAAAWodpAAQAAAABAAAAbAAAAAAAAABIAAAAAQAAAEgAAAAB'
. 'QWRvYmUgSW1hZ2VSZWFkeQAAAAOgAQADAAAAAQABAACgAgAEAAAAAQAAADCgAwAE'
. 'AAAAAQAAADAAAAAAXukGzAAAAAlwSFlzAAALEwAACxMBAJqcGAAAActpVFh0WE1M'
. 'OmNvbS5hZG9iZS54bXAAAAAAADx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6'
. 'bWV0YS8iIHg6eG1wdGs9IlhNUCBDb3JlIDUuNC4wIj4KICAgPHJkZjpSREYgeG1s'
. 'bnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgt'
. 'bnMjIj4KICAgICAgPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIKICAgICAg'
. 'ICAgICAgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIgog'
. 'ICAgICAgICAgICB4bWxuczp0aWZmPSJodHRwOi8vbnMuYWRvYmUuY29tL3RpZmYv'
. 'MS4wLyI+CiAgICAgICAgIDx4bXA6Q3JlYXRvclRvb2w+QWRvYmUgSW1hZ2VSZWFk'
. 'eTwveG1wOkNyZWF0b3JUb29sPgogICAgICAgICA8dGlmZjpPcmllbnRhdGlvbj4x'
. 'PC90aWZmOk9yaWVudGF0aW9uPgogICAgICA8L3JkZjpEZXNjcmlwdGlvbj4KICAg'
. 'PC9yZGY6UkRGPgo8L3g6eG1wbWV0YT4KKS7NPQAADE5JREFUaAXtWQuMVcUZ/uY8'
. '7vvui+WNAYHa2oqKiw/SWnaJ0ipqH4ZHmjTVGMFao00LVm2Dl9YXtVETpVYSX6m2'
. 'tYhajU210V0KPqi7CALKrggUWeS9uOzufZzH9PvPvXdZ1t27rJCUpM5y7pyZM2fm'
. '+/7/m3/ODMAX6QsLHJcF1HG9XeJlraEaFsOUJrW3w1MKukTzk+tR4zzYvRHpeli9'
. '605E+YR2Wp+CVbC2o+9DtF1bd6EyodsTiV+qup1p8QpBG/SGdyLAn7A+dAqGgC92'
. '2H4vrubVqpcaWi+v0u1/qWjteC7+o+Jz8QbJGMXy8eTHNQfEok3zYU1ZBkdAtN2L'
. 'WkNhSVkE52VyQE4rB9XlCCe1HU76SDvu24hgQWx6+g1prxsptRq4xzM/uq0mHQ4m'
. 'ic45sAB3Dt2P8crF3VEbs0kA7Wm4nLIG/2yxs+Nq3+30/Xg5LoDlrc6tMv/o2t5t'
. 'agp2ypjiEVXHdz5HGrQH6mup8wZGFY67ewHisSrrNm25N5fFYBG4L3h4BdEHZIjh'
. 'ZTCiZBPyoGzPM8O+ClV7hpfx00rpO40OLBHwBUmpwc6PY9ZhoHOCr2ugywmy7ebk'
. '1eForCUZMm5TviHgxYLSXx58MWiKiVS+oAw+U9pwO+CYpo4aFbjDT2KzXosrCdwX'
. '8CIrGYtvHVOS7ksmDq2a5tVQ5015nV87YpoXcu+tLPfOFcyO4Toq7FnKJi9THFBI'
. 'gln0NFI84AceMEKFPOzBsHyJSa4RosxCgHcYr5kWFqjJWCc9HOv8KElAp1KGSqUC'
. 'VG1zx41zTf+eaNybY1IOncpxKQlDhVyDOZiLRGj/AokCATUq2S2hQEYkYZKAsrrJ'
. 'SkjVRpxRLAP4Hh6m+RdxfuwPiGjNsKu6G0tdz9Svq4rg3wSiu6446zdZL95cZlhz'
. 'utKm39FleBSBxcuAY0E7ZvdFIRzpX26DS/woF/8FubDrTiI5y++E63vUVxl+rFW8'
. 'Jdd4xo3SQsBrneoXZ4/RujuELrD+aO3aKZWrX3mx7MVbR+5PUxFjznJgdNjKygK0'
. 'JGVDy8vkpPwLeXfZIEhKSI1OHOWBQEbiAXneOwWVY1wDO2wk+NBYuGlH1Q2zxw4Z'
. '+34RU+9X+mS2uK4uqP/9jQtOX521R7Ze9XgmcuF8rbeut732PQwzFYyNR6wuHvhM'
. 'ueAJCURi9fzF4QsTujcQqJHwfV8ZLsFH6vw269HM3/wZX1u88tUp0nZxQ0OfWPus'
. 'REND0H+6eUv65YUL8MhjK+y1Y85U6rqlCI/+JvytG+HniEyXUTr8SiuQERI9iUCz'
. 'jVha/BxcQiRPKBgg+BkCX1cD7iew7CgyQ36Hf2RvMua8q+3vNjbhvcO7u6RZw8qV'
. 'R17pcdc3gdraoIkqLzOGTr4A5oZm/eoNP8ELqzag9fzvIzT3bsbDELzWZmivnCEk'
. 'XiCSnw9wC/OCYsh3JCR6EAkqE/TkGE7aA7CM/TAqb8Fa+ync1DwCl6zbhFzmE315'
. 'PI6kbfeNMd/zQPHWh9vRBb8qqSomfx2fPvES/nnNXXhrZxpdM3+FyPSFUAdb4B3c'
. 'zvBRDe2GSMQIPFL0Rn7iitXF9Jw3BqWnBHgHTL0TVvlV2Jb4E+7cWYOaxq1o3P8R'
. 'ZnJJt1RYpX0GnwE++wb8lJBgTXGSSBrhSWMRN018suRhtJ9xNk676jKMnf0oQh+8'
. 'DmfD01BVxJicSAIHKC9a3OYk5ut56wv4EZRLK2yPXxCJ6Thgz8WLe4bgF9tbEXYP'
. '4tJwhN8mUXQGSwRDk7w7QBqQgLwvWCSiaMeF52pEzpkEK5PBfxb8Fu3f+RZOmVmH'
. 'IRPPBdY9A6f1LRjDRwgTzgcSURJxyikXmtIl+MRQZBIL8fqB8bi7ZR9Wd6zDxYkw'
. '14Y40lrCfWAyGfaYUkl9BStMj24CIiz7/NTUlEL4vDPhrWvBtnkPYPPbu9BxzrUI'
. 'X7QYhrsb/r4dbDiMchkOn+WItQ/msFvwjv8g5q2pxMxVa5HrbMXFsRhysoYF4HsM'
. 'doy3x+YBdiZOkNQz97uylE0C9sgKpP/wGpoffxtDbrkEo+qeQGRXI5yWh2DTqKFh'
. '1+BDcxqe3ODhzub3MDySRV1ZElnKsYvytAyDKpM5kh9jML+lCdAF2hLI0rM+CnxQ'
. 'psA1AeiuHIwpwxmZNA4sfhIHp07C6DnTMGr6Q2gbpfD8riSuf+dDTsj9mFpVDicU'
. 'x6eeRogh1mT3RaMMBnixbUkJFRvJAMVBjs5ZEl3JTHU8rg0Edf4ERLv2YuNPF+Hl'
. 'lgTu2jgS1z/9Ciabh3B2sgxtXOAckj7K2MVOiwMOIi/tgV4d5X1wtCdk7Py0I3iq'
. 'OZMJ4f3E6Wg44zyM35eFjnTwa2oiDnQoSqULUUtmFu1W4F7sTfo5ilSvsfsrDkgg'
. 'b5ziMEc8EQAQCXFYWzM6cfStoTFYExqJXaEo/M4OxLkGHabOpW0mV4bOQ2Wo1mmU'
. 'l3XRaR6r5Tvu88A+QmdAAtI0T6KQsyDzTURgMjRavNtjVaMpPAbvW0lEKakK7eEQ'
. '32I4p7EJMhSGHbbY3kRbV4IS8lBV0U6CJCL9ayHy+dKABIrgi93LtknsblMuh1U5'
. '1tLq79pVjPUWyn1uhbnt4naFLfKLmBIP2CFuYLhz4TPLkpU4gkOHy+H5WZSV7UfY'
. '4qcue8xv5nqPWBy577wkAVGr2F3A5JcYDkO5uASw0RyB9+yh6DAiqCJwiUCOMrne'
. '5YVR8BcUtw0IhWBaNmyGS4tlk5cQcbwY2g9XgSdHiMV3wzA4Z7QQCfwSjD7QT0kC'
. '8nJgD5rcDERj4GN+L2w2hmK3iqOS0YTRnNtKERLtJzGNN6TBm7y2xTPiAYtX3gN5'
. 'AiY9Y/ESMo4zGumuEYhEdpPoTr4rpgt398GbflNJAlxeGPR4oqB91YY4tmEItusk'
. 'EqxNqiyxUkjEabIsH2vSWj45fFlVC8t24IGAgM3N2hHQliVE8gQsMjfMMNeUr9AA'
. 'p1COW9nHTpbZTYntpLAqSYCvR3IMjs1uzNmCpEGLqXKdpX19ZBUlUbAyTAHP3sTw'
. 'dJnPWc7NifRPYDKJ6QHOAd0tISFCAkeREDIMtWY164dzA7bXUZGPTTe7MxJ0NC34'
. '/cyPDPnZ1NAQjH5I2f+u74x+sM6NRGzfUSE/62R9VzvUvOdxLvgOL94Hl1fIpezz'
. 'YpikCXt6wBYZ2SQe5LyXeUFiIcmDujClplzbtlTFsHMiMVywNxadtFEA1tbenrdI'
. 'L7R9EkjRiLyMZ7aub9m4682vluvMz3Ke86lLzbiegHbcAHxAgkSCOgEuJLgiS+5K'
. 'nO9BIABbBNqTSKHOCnkk4cUrhlkk5bpdB5fErAnj//69metSPB1J9SOlfiVUJCH5'
. 'ptY37p844sKnckjfYZuheaZvWYTNnbySI4+8EeRzopA8TnlXCNADBqUik1gsbHJN'
. 'OKJ7kRClxAMiw7K8SDxpyzOd63rWsIxbl3/7lC3SXYoHDP2Bl+d9ekAeSEoRPDPF'
. 'vZK9ZfeqfZtb/zXfcbwpWS9Tz7MOHk15BiXk0BtaJNVTSq7j5CVEnUcI3gqkc8QD'
. 'VsjWlmU7oWjMSFQOpXCcd7WXu+jZS8fOEvDzHmm02YEqBV4w9usBeVhIugnBqZxR'
. 'U1NjNjXVN7F++lnjZszxffOeiG2NE9nwkE1O7oL/2DB9A27GDaKReKBCPCCTOIg6'
. 'gRdcy7StaEW17XV17PHSnYtWXH7qMhmvtr7eGrpvn142e4qD+VJTOpX0QK9X/aam'
. 'JmcWZjGspIz12199Jhfb8eWcm77d9XI8KOL88Hk25bvctPHIMSsEuCaTQDyQEK1P'
. 'qeR1PlR07ut0572VVYkvrbhsLMFrJeAb6urc5bNn59fQXgD6Kg6GQPD+cixn5ym/'
. 'trbW2rRpU65p68u/9nx9WtZN/5maN/j9Y9IjdIdLleXnQNQO+ZSQE0lWmNFEuamz'
. 'mecYhU9fcfm4mx/7xtDDgVy4pRHwfYEsVTdoAsXOGhoaZDBFWdlNW1/asebDF37g'
. 'ec60nJt5h9X8aHYNHz4PIC0nFosZcerc8Jz12vdmPH/FqVc+P3NCS01B58vmUy7/'
. '25QyxCNFDDOm/vCaCbh096Kf36d/u+aAnrq8ee/cl7ZdV3w+66/alOhSLJ80eYFE'
. 'MZ7G70gtffCBlduXYslmHlHkk+i8eH/S5jU184Jo1BNgIBdqq2fdSX6fMmbNmmWK'
. 'XMCV9CQH+wW8/18L/BeSV1YkHS6B9wAAAABJRU5ErkJggg==';
}
public function getAccessToken($byRefresh = false)
{
if ($byRefresh) {
$tokenInfo = $this->session->get('provider.token_info');
try {
$url = sprintf("%s/realms/%s/protocol/openid-connect/token/",
$this->config['base-url'],
urlencode($this->config['realm-name'])
);
$guzzleRequest = $this->client->post($url);
$guzzleRequest->addPostFields([
'grant_type' => "refresh_token",
'refresh_token' => $tokenInfo['refresh_token'],
'client_id' => $this->config['client-id'],
'client_secret' => $this->config['client-secret'],
]);
$guzzleRequest->setHeader('Accept', 'application/json');
$response = $guzzleRequest->send();
$this->debug();
}
catch (\Exception $e) {
$this->debug($e->getMessage());
throw new NotAuthenticatedException('Guzzle error while authentication', $e->getCode(), $e);
}
if (200 !== $response->getStatusCode()) {
$this->debug();
throw new NotAuthenticatedException('Error while retrieving user info, invalid status code.');
}
$data = @json_decode($response->getBody(true), true);
if (JSON_ERROR_NONE !== json_last_error()) {
$this->debug();
throw new NotAuthenticatedException('Error while decoding token response, unable to parse JSON.');
}
// override token information
$this->session->set('provider.token_info', $data);
$this->session->set($this->getId() . '.provider.access_token', $data['access_token']);
}
return $this->session->get($this->getId() . '.provider.access_token');
}
public function getUserName()
{
return $this->session->get($this->getId() . ".provider.username");
}
}