PHRAS-4108 openid : add claims mapping and groups filtering (#4563)

* openid add group mapping

* add  migration patch for configuration injection
This commit is contained in:
Aina Sitraka
2024-12-12 17:46:04 +03:00
committed by GitHub
parent 4e9414ba6e
commit 32aa1db879
5 changed files with 103 additions and 17 deletions

View File

@@ -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

View File

@@ -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
```
@@ -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

View File

@@ -343,20 +343,26 @@ class Openid extends AbstractProvider
$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();
@@ -715,6 +721,36 @@ class Openid extends AbstractProvider
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 ;
}
/**

View File

@@ -50,13 +50,36 @@ class patch_4111PHRAS4106 implements patchInterface
$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);
}
}
}

View File

@@ -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