diff --git a/src/Controller/UsersController.php b/src/Controller/UsersController.php index a868820..426f4a3 100644 --- a/src/Controller/UsersController.php +++ b/src/Controller/UsersController.php @@ -16,6 +16,7 @@ class UsersController extends AppController public function index() { + $this->Users->updateMappers(); $currentUser = $this->ACL->getUser(); $conditions = []; if (empty($currentUser['role']['perm_admin'])) { diff --git a/src/Model/Behavior/AuthKeycloakBehavior.php b/src/Model/Behavior/AuthKeycloakBehavior.php index 08f35fe..8e86f74 100644 --- a/src/Model/Behavior/AuthKeycloakBehavior.php +++ b/src/Model/Behavior/AuthKeycloakBehavior.php @@ -176,7 +176,7 @@ class AuthKeycloakBehavior extends Behavior $users = [$user->toArray()]; $clientId = $this->getClientId(); - $changes = $this->syncUser($users, $clientId); + $changes = $this->syncUsers($users, $clientId); return $changes; } @@ -231,6 +231,7 @@ class AuthKeycloakBehavior extends Behavior public function syncWithKeycloak(): array { + $this->updateMappers(); $results = []; $data['Users'] = $this->_table->find()->contain(['Individuals', 'Organisations', 'Roles'])->select( [ @@ -316,7 +317,6 @@ class AuthKeycloakBehavior extends Behavior 'org_uuid' => $user['organisation']['uuid'] ] ]; - debug($change); $response = $this->restApiRequest('%s/admin/realms/%s/users/' . $keycloakUser['id'], $change, 'put'); if (!$response->isOk()) { $this->_table->auditLogs()->insert([ @@ -351,7 +351,6 @@ class AuthKeycloakBehavior extends Behavior 'org_uuid' => $user['organisation']['uuid'] ] ]; - debug($newUser); $response = $this->restApiRequest('%s/admin/realms/%s/users', $newUser, 'post'); if (!$response->isOk()) { $this->_table->auditLogs()->insert([ @@ -385,4 +384,55 @@ class AuthKeycloakBehavior extends Behavior { return str_replace('%', '%%', $input); } + + public function updateMappers(): bool + { + $clientId = $this->getClientId(); + $response = $this->restApiRequest('%s/admin/realms/%s/clients/' . $clientId . '/protocol-mappers/models?protocolMapper=oidc-usermodel-attribute-mapper', [], 'get'); + if ($response->isOk()) { + $mappers = json_decode($response->getStringBody(), true); + } else { + return false; + } + $enabledMappers = []; + $defaultMappers = [ + 'org_name' => 0, + 'org_uuid' => 0, + 'role_name' => 0, + 'role_uuid' => 0 + ]; + $mappersToEnable = explode(',', Configure::read('keycloak.user_meta_mapping')); + foreach ($mappers as $mapper) { + if ($mapper['protocolMapper'] !== 'oidc-usermodel-attribute-mapper') { + continue; + } + if (in_array($mapper['name'], array_keys($defaultMappers))) { + $defaultMappers[$mapper['name']] = 1; + continue; + } + $enabledMappers[$mapper['name']] = $mapper; + } + $payload = []; + foreach ($mappersToEnable as $mapperToEnable) { + $payload[] = [ + 'protocol' => 'openid-connect', + 'name' => $mapperToEnable, + 'protocolMapper' => 'oidc-usermodel-attribute-mapper', + 'config' => [ + 'id.token.claim' => true, + 'access.token.claim' => true, + 'userinfo.token.claim' => true, + 'user.attribute' => $mapperToEnable, + 'claim.name' => $mapperToEnable + ] + ]; + } + if (!empty($payload)) { + $response = $this->restApiRequest('%s/admin/realms/%s/clients/' . $clientId . '/protocol-mappers/add-models', $payload, 'post'); + if (!$response->isOk()) { + return false; + } + } + return true; + } } diff --git a/src/Model/Table/SettingProviders/CerebrateSettingsProvider.php b/src/Model/Table/SettingProviders/CerebrateSettingsProvider.php index 801885c..421f6b5 100644 --- a/src/Model/Table/SettingProviders/CerebrateSettingsProvider.php +++ b/src/Model/Table/SettingProviders/CerebrateSettingsProvider.php @@ -284,6 +284,14 @@ class CerebrateSettingsProvider extends BaseSettingsProvider 'description' => __('family_name mapped name in keycloak'), 'dependsOn' => 'keycloak.enabled' ], + 'keycloak.user_meta_mapping' => [ + 'name' => 'User Meta-field attribute mapping', + 'type' => 'string', + 'severity' => 'info', + 'default' => '', + 'description' => __('List of user metafields to push to keycloak as attributes. When using multiple templates, the attribute names have to be unique. Expects a comma separated list.'), + 'dependsOn' => 'keycloak.enabled' + ] ] ] ],