Skip to content

Commit

Permalink
PHRAS-4108 openid : add claims mapping and groups filtering (#4563)
Browse files Browse the repository at this point in the history
* openid add group mapping

* add  migration patch for configuration injection
  • Loading branch information
aynsix authored Dec 12, 2024
1 parent 4e9414b commit 32aa1db
Show file tree
Hide file tree
Showing 5 changed files with 103 additions and 17 deletions.
8 changes: 8 additions & 0 deletions config/configuration.sample.yml
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,14 @@ authentication:
debug: false
auto-logout: false
auto-connect-idp-name: null
groupmask: "/phraseanet_([^,]+)/i"
fieldmap:
id: sub
login: email
firstname: given_name
lastname: family_name
email: email
groups: group
registration-fields:
-
name: company
Expand Down
23 changes: 17 additions & 6 deletions doc/others/openid-sso.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,14 @@ authentication:
# logout with phraseanet and also logout with keycloak
auto-logout: true
auto-connect-idp-name: null
groupmask: "/cn=phraseanet_([^,]+),cn=users,ou=alchemy$/i"
fieldmap:
id: sub
login: email
firstname: given_name
lastname: family_name
email: email
groups: group

```

Expand All @@ -47,16 +55,19 @@ authentication:

set the 'Valid post logout redirect URIs' field with `https://{phraseanet-host}/login/logout/` eg: https://phraseanet.phrasea.local/login/logout/

- Choose a client > client scopes > '.... dedicated'

add a 'groups' mapper if not exist, > Add mapper > by configuration

- if not exist create a client scope with mapper type Group Membership
`Mapper type` => Group Membership
`Name` => groups
`Token Claim Name` => groups
`Name` => group
`Token Claim Name` => group
`Full group path` => off
`Add to userinfo` => on

- Add the created client scope to the client

Choose a client > client scopes > Add client scope > choose the scope



#### token expiration
- we can define token expiration in keycloak

Expand Down
56 changes: 46 additions & 10 deletions lib/Alchemy/Phrasea/Authentication/Provider/Openid.php
Original file line number Diff line number Diff line change
Expand Up @@ -343,20 +343,26 @@ public function onCallback(Request $request)

$this->debug();

$userName = $data['preferred_username'];
$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($userName) && isset($data['email'])) {
$userName = $data['email'];// login to be an email
if (!\Swift_Validate::email($data[$loginKey]) && isset($data['email'])) {
$loginKey = 'email';// login to be an email
}

$usegroups = isset($this->config['usegroups']) ? $this->config['usegroups'] : false;
$userUA = $this->CreateUser([
'id' => $distantUserId = $data['sub'],
'login' => $userName,
'firstname' => isset($data['given_name']) ? $data['given_name'] : '',
'lastname' => isset($data['family_name']) ? $data['family_name'] : '' ,
'email' => isset($data['email']) ? $data['email'] : '',
'_groups' => isset($data['groups']) && $usegroups ? $data['groups'] : ''
'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();
Expand Down Expand Up @@ -715,6 +721,36 @@ private function CreateUser(Array $data)
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 ;
}



/**
Expand Down
25 changes: 24 additions & 1 deletion lib/classes/patch/4111PHRAS4106.php
Original file line number Diff line number Diff line change
Expand Up @@ -50,13 +50,36 @@ public function apply(base $appbox, Application $app)
$conf = $app['conf'];
foreach ($app['conf']->get(['authentication', 'providers'], []) as $providerId => $data) {
if ($data['type'] === "openid") {
if(!isset($data['options']['usegroups'])) {
if (!isset($data['options']['usegroups'])) {
$data['options']['usegroups'] = false;

$providerConfig[$providerId] = $data;

$conf->merge(['authentication', 'providers'], $providerConfig);
}

if (!isset($data['options']['fieldmap'])) {
$data['options']['fieldmap'] = [
'id' => 'sub',
'login' => 'email',
'firstname' => 'given_name',
'lastname' => 'family_name',
'email' => 'email',
'groups' => 'group',
];

$providerConfig[$providerId] = $data;

$conf->merge(['authentication', 'providers'], $providerConfig);
}

if (!isset($data['options']['groupmask'])) {
$data['options']['groupmask'] = "/phraseanet_([^,]+)/i";

$providerConfig[$providerId] = $data;

$conf->merge(['authentication', 'providers'], $providerConfig);
}
}
}

Expand Down
8 changes: 8 additions & 0 deletions lib/conf.d/configuration.yml
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,14 @@ authentication:
debug: false
auto-logout: false
auto-connect-idp-name: null
groupmask: "/phraseanet_([^,]+)/i"
fieldmap:
id: sub
login: email
firstname: given_name
lastname: family_name
email: email
groups: group
registration-fields:
-
name: company
Expand Down

0 comments on commit 32aa1db

Please sign in to comment.