-
+ user->getProviderConfigValue('auto_create_atom_user', true)) { ?>
diff --git a/plugins/arDominionB5Plugin/modules/user/templates/listSuccess.mod_ext_auth.php b/plugins/arDominionB5Plugin/modules/user/templates/listSuccess.mod_ext_auth.php
index 2d95a4ca41..25951b09d3 100644
--- a/plugins/arDominionB5Plugin/modules/user/templates/listSuccess.mod_ext_auth.php
+++ b/plugins/arDominionB5Plugin/modules/user/templates/listSuccess.mod_ext_auth.php
@@ -80,7 +80,7 @@
$pager]); ?>
-
+user->getProviderConfigValue('auto_create_atom_user', true)) { ?>
'user', 'action' => 'add'], ['class' => 'btn atom-btn-outline-light']); ?>
diff --git a/plugins/arOidcPlugin/config/app.yml b/plugins/arOidcPlugin/config/app.yml
index a9a8f40615..d71929e332 100644
--- a/plugins/arOidcPlugin/config/app.yml
+++ b/plugins/arOidcPlugin/config/app.yml
@@ -20,10 +20,118 @@ all:
client_id: 'artefactual-atom'
client_secret: 'example-secret'
+ # Set to true if OIDC endpoint supports logout.
+ # Setting examples for tested OpenID providers:
+ # --------
+ # Keycloak via Dex:
+ # send_oidc_logout: false
+ # Keycloak direct:
+ # send_oidc_logout: true
+ send_oidc_logout: true
+
+ # Set to true if OIDC endpoint is configured to send refresh tokens.
+ enable_refresh_token_use: true
+
+ # OIDC server SSL certificate location for server validation.
+ # Accepts a filepath or false (to disable, e.g. for development).
+ # Examples
+ # --------
+ # Relative path to sf_root_dir: 'data/oidc/cert/mycert.pem'
+ # Absolute path: '/usr/var/certif/xxx.pem'
+ # Disable server validation: false
+ server_cert: false
+
+ # Settings for parsing OIDC groups into AtoM group membership.
+ # Set set_groups_from_attributes to true to enable.
+ set_groups_from_attributes: true
+ user_groups:
+ administrator:
+ attribute_value: 'atom-admin'
+ group_id: 100
+ editor:
+ attribute_value: 'atom-editor'
+ group_id: 101
+ contributor:
+ attribute_value: 'atom-contributor'
+ group_id: 102
+ translator:
+ attribute_value: 'atom-translator'
+ group_id: 103
+
+ scopes:
+ - 'openid'
+ # Use with Dex
+ # - 'offline_access'
+ - 'profile'
+ - 'email'
+ # Use with Dex
+ # - 'groups'
+
+ # Identify token which contains role claims. Options are 'access-token',
+ # 'id-token', 'verified-claims', or 'user-info'.
+ # 'set_groups_from_attributes' must be 'true' to enable.
+ roles_source: 'access-token'
+
+ # Identify the location of role claims within the token identified in
+ # `roles_source` above. This is an array containing the node path to
+ # locate the roles array in the OIDC token. By default this is found
+ # in Keycloak's access token under 'realm_access'/'roles'.
+ roles_path:
+ - 'realm_access'
+ - 'roles'
+
+ # Identify how IAM users are matched to users in AtoM. Two values are allowed:
+ # user_matching_source: oidc-email
+ # user_matching_source: oidc-username
+ # Using oidc-username will work without additional scopes being requested.
+ #
+ # Using oidc-email requires the 'email' scope to be set above in the
+ # 'scopes' setting. 'email' is an optional user setup field in Keycloak but
+ # MUST be set if matching to pre-existing AtoM user accounts is going to work.
+ user_matching_source: 'oidc-email'
+
+ # Activate or disable the automatic creation of AtoM user records from OIDC
+ # endpoint details. Allowed settings are:
+ #
+ # true (default): AtoM will automatically create a user record on first login.
+ #
+ # false: AtoM will not automatically create a user record on first login - AtoM
+ # user must be created in advance to successfully authenticate in AtoM.
+ auto_create_atom_user: true
+
+ # The following is an example secondary provider called 'sample_provider'. If
+ # uncommented, a second OIDC provider in the 'sample' Keycloak realm will be available.
#sample_provider:
#url: 'https://keycloak:8443/realms/sample'
#client_id: 'sample-atom'
#client_secret: 'example-secret'
+ #send_oidc_logout: true
+ #enable_refresh_token_use: true
+ #server_cert: false
+ #set_groups_from_attributes: true
+ #user_groups:
+ # administrator:
+ # attribute_value: 'atom-admin'
+ # group_id: 100
+ # editor:
+ # attribute_value: 'atom-editor'
+ # group_id: 101
+ # contributor:
+ # attribute_value: 'atom-contributor'
+ # group_id: 102
+ # translator:
+ # attribute_value: 'atom-translator'
+ # group_id: 103
+ #scopes:
+ # - 'openid'
+ # - 'profile'
+ # - 'email'
+ #roles_source: 'access-token'
+ #roles_path:
+ # - 'realm_access'
+ # - 'roles'
+ #user_matching_source: 'oidc-email'
+ #auto_create_atom_user: true
# Identifies the primary OIDC provider and corresponds to an entry in 'providers' above.
# The default provider name is 'primary'. If this setting is not defined, AtoM will default
@@ -46,15 +154,6 @@ all:
# NOTE: Always configure using SSL in production.
redirect_url: 'http://127.0.0.1:63001/index.php/oidc/login'
- # Set to true if OIDC endpoint supports logout.
- # Setting examples for tested OpenID providers:
- # --------
- # Keycloak via Dex:
- # send_oidc_logout: false
- # Keycloak direct:
- # send_oidc_logout: true
- send_oidc_logout: true
-
# OIDC logout requires a URL to redirect to. Use this setting to
# specify a page to redirect the user to on logout when
# 'send_oidc_logout' is 'true'. Localhost port 63001 (127.0.0.1:63001)
@@ -62,73 +161,3 @@ all:
# public IP and port.
# NOTE: Always configure using SSL in production.
logout_redirect_url: 'http://127.0.0.1:63001'
-
- # Set to true if OIDC endpoint is configured to send refresh tokens.
- enable_refresh_token_use: true
-
- # OIDC server SSL certificate location for server validation.
- # Accepts a filepath or false (to disable, e.g. for development).
- # Examples
- # --------
- # Relative path to sf_root_dir: 'data/oidc/cert/mycert.pem'
- # Absolute path: '/usr/var/certif/xxx.pem'
- # Disable server validation: false
- server_cert: false
-
- scopes:
- - 'openid'
- # Use with Dex
- # - 'offline_access'
- - 'profile'
- - 'email'
- # Use with Dex
- # - 'groups'
-
- # Settings for parsing OIDC groups into AtoM group membership.
- # Set set_groups_from_attributes to true to enable.
- set_groups_from_attributes: true
- user_groups:
- administrator:
- attribute_value: 'atom-admin'
- group_id: 100
- editor:
- attribute_value: 'atom-editor'
- group_id: 101
- contributor:
- attribute_value: 'atom-contributor'
- group_id: 102
- translator:
- attribute_value: 'atom-translator'
- group_id: 103
-
- # Identify token which contains role claims. Options are 'access-token',
- # 'id-token', 'verified-claims', or 'user-info'.
- # 'set_groups_from_attributes' must be 'true' to enable.
- roles_source: 'access-token'
-
- # Identify the location of role claims within the token identified in
- # `roles_source` above. This is an array containing the node path to
- # locate the roles array in the OIDC token. By default this is found
- # in Keycloak's access token under 'realm_access'/'roles'.
- roles_path:
- - 'realm_access'
- - 'roles'
-
- # Identify how IAM users are matched to users in AtoM. Two values are allowed:
- # user_matching_source: oidc-email
- # user_matching_source: oidc-username
- # Using oidc-username will work without additional scopes being requested.
- #
- # Using oidc-email requires the 'email' scope to be set above in the
- # 'scopes' setting. 'email' is an optional user setup field in Keycloak but
- # MUST be set if matching to pre-existing AtoM user accounts is going to work.
- user_matching_source: 'oidc-email'
-
- # Activate or disable the automatic creation of AtoM user records from OIDC
- # endpoint details. Allowed settings are:
- #
- # true (default): AtoM will automatically create a user record on first login.
- #
- # false: AtoM will not automatically create a user record on first login - AtoM
- # user must be created in advance to successfully authenticate in AtoM.
- auto_create_atom_user: true
diff --git a/plugins/arOidcPlugin/lib/arOidc.class.php b/plugins/arOidcPlugin/lib/arOidc.class.php
index 0a43c097fa..f9d450032e 100644
--- a/plugins/arOidcPlugin/lib/arOidc.class.php
+++ b/plugins/arOidcPlugin/lib/arOidc.class.php
@@ -39,16 +39,6 @@ public static function getOidcInstance()
$oidc = new OpenIDConnectClient();
- // Validate requested scopes.
- $scopesArray = sfConfig::get('app_oidc_scopes', []);
- $validScopes = self::validateScopes($scopesArray);
- // Add scopes only if the array is not empty
- if (!empty($validScopes)) {
- $oidc->addScope($validScopes);
- } else {
- throw new Exception('No valid scopes found in app_oidc_scopes.');
- }
-
// Validate redirect URL.
$redirectUrl = sfConfig::get('app_oidc_redirect_url', '');
if (empty($redirectUrl)) {
@@ -56,20 +46,6 @@ public static function getOidcInstance()
}
$oidc->setRedirectURL($redirectUrl);
- // Validate the server SSL certificate according to configuration.
- $certPath = sfConfig::get('app_oidc_server_cert', false);
- if (0 === !strpos($certPath, '/')) {
- $certPath = sfConfig::get('sf_root_dir').DIRECTORY_SEPARATOR.$certPath;
- }
-
- if (file_exists($certPath)) {
- $oidc->setCertPath($certPath);
- } elseif (false === $certPath) {
- // OIDC server SSL certificate disabled.
- } else {
- throw new Exception('Invalid SSL certificate settings. Please review the app_oidc_server_cert parameter in plugin app.yml.');
- }
-
self::$oidcIsInitialized = true;
return $oidc;
diff --git a/plugins/arOidcPlugin/lib/oidcUser.class.php b/plugins/arOidcPlugin/lib/oidcUser.class.php
index 79bd598f6f..e2dc8d507d 100644
--- a/plugins/arOidcPlugin/lib/oidcUser.class.php
+++ b/plugins/arOidcPlugin/lib/oidcUser.class.php
@@ -55,13 +55,17 @@ public function authenticate($username = null, $password = null): bool
$providerId = $this->getSessionProviderId();
// Validate and set provider ID in session.
if (null !== $providerId = $this->validateProviderId($providerId, true)) {
- // Set Provider details in OIDC client.
+ // Set provider details in OIDC client.
$result = $this->setOidcProviderDetails($providerId);
}
if (null === $providerId || !isset($result) || false === $result) {
return $authenticated;
}
+ // Set provider server cert.
+ $this->setServerCert($this->getProviderConfigValue('server_cert', false));
+ $this->setOidcScopes($this->getProviderConfigValue('scopes', []));
+
if (isset($_REQUEST['code'])) {
$this->logger->info('OIDC request "code" is set.');
}
@@ -79,7 +83,7 @@ public function authenticate($username = null, $password = null): bool
$expiryTime = $this->oidcClient->getVerifiedClaims('exp');
$this->setAttribute('oidc-expiry', $expiryTime);
- if (true == sfConfig::get('app_oidc_enable_refresh_token_use', false)) {
+ if (true == $this->getProviderConfigValue('enable_refresh_token_use', false)) {
$this->setAttribute('oidc-refresh', $this->oidcClient->getRefreshToken());
}
}
@@ -91,7 +95,7 @@ public function authenticate($username = null, $password = null): bool
if ($authenticateResult) {
// Validate user source setting.
- $userMatchingSource = sfConfig::get('app_oidc_user_matching_source', '');
+ $userMatchingSource = $this->getProviderConfigValue('user_matching_source', '');
if (!arOidc::validateUserMatchingSource($userMatchingSource)) {
$this->logger->err('OIDC user matching source is configured but is not set properly. Unable to match OIDC users to AtoM users.');
$this->logout();
@@ -113,7 +117,7 @@ public function authenticate($username = null, $password = null): bool
$user = QubitUser::getOne($criteria);
}
- $autoCreateUser = sfConfig::get('app_oidc_auto_create_atom_user', true);
+ $autoCreateUser = $this->getProviderConfigValue('auto_create_atom_user', true);
if (!is_bool($autoCreateUser)) {
$this->logger->err('OIDC auto_create_atom_user is configured but is not set properly - value should be of type bool. Unable to match OIDC users to AtoM users.');
$this->logout();
@@ -148,7 +152,7 @@ public function authenticate($username = null, $password = null): bool
// Parse OIDC group claims into group memberships. If enabled, we perform this
// check each time a user authenticates so that changes made on the OIDC
// server are applied in AtoM on the next login.
- $setGroupsFromClaims = sfConfig::get('app_oidc_set_groups_from_attributes', false);
+ $setGroupsFromClaims = $this->getProviderConfigValue('set_groups_from_attributes', false);
if (!is_bool($setGroupsFromClaims)) {
$this->logger->err('OIDC set_groups_from_attributes is configured but is not set properly - value should be of type bool. Unable to complete authentication.');
$this->logout();
@@ -156,8 +160,8 @@ public function authenticate($username = null, $password = null): bool
return $authenticated;
}
if (true == $setGroupsFromClaims) {
- $rolesPath = sfConfig::get('app_oidc_roles_path', []);
- $rolesSource = sfConfig::get('app_oidc_roles_source', '');
+ $rolesPath = $this->getProviderConfigValue('roles_path', []);
+ $rolesSource = $this->getProviderConfigValue('roles_source', '');
// Validate Settings.
if (!arOidc::validateRolesSource($rolesSource) || empty($rolesPath)) {
@@ -202,7 +206,7 @@ public function isAuthenticated(): bool
{
$authenticated = parent::isAuthenticated();
- if (false == sfConfig::get('app_oidc_enable_refresh_token_use', false) || false === $authenticated) {
+ if (false == $this->getProviderConfigValue('enable_refresh_token_use', false) || false === $authenticated) {
return $authenticated;
}
@@ -225,6 +229,10 @@ public function isAuthenticated(): bool
$providerId = $this->getSessionProviderId();
// Set provider details in the OIDC client using provider id.
if (true === $this->setOidcProviderDetails($providerId)) {
+ // Set provider server cert.
+ $this->setServerCert($this->getProviderConfigValue('server_cert', false));
+ $this->setOidcScopes($this->getProviderConfigValue('scopes', []));
+
$refreshResult = $this->oidcClient->refreshToken($refreshToken);
}
@@ -267,15 +275,20 @@ public function logout(): void
$this->unsetAttributes();
$this->signOut();
- if (true == sfConfig::get('app_oidc_send_oidc_logout', false) && !empty($idToken)) {
+ if (true == $this->getProviderConfigValue('send_oidc_logout', false) && !empty($idToken)) {
$logoutRedirectUrl = sfConfig::get('app_oidc_logout_redirect_url', '');
if (empty($logoutRedirectUrl)) {
$logoutRedirectUrl = null;
$this->logger->err('Setting "app_oidc_logout_redirect_url" invalid. Unable to redirect on sign out.');
}
+ // Set provider server cert.
+ $this->setServerCert($this->getProviderConfigValue('server_cert', false));
+ $this->setOidcScopes($this->getProviderConfigValue('scopes', []));
+
// Get saved session provider id.
$providerId = $this->getSessionProviderId();
+ // Unset session provider Id.
$this->setSessionProviderId();
// Set provider details in the OIDC client using provider id.
@@ -292,6 +305,36 @@ public function logout(): void
}
}
+ // Get provider specific config vals from app.yml.
+ public function getProviderConfigValue(string $configVariableName = '', $default = null)
+ {
+ // Get saved session provider id.
+ $providerId = $this->getSessionProviderId();
+
+ // Get OIDC provider list. If none are configured this is an error.
+ $providers = sfConfig::get('app_oidc_providers', []);
+ if (empty($providers)) {
+ $this->logger->err('OIDC providers not found in app.yml - check plugin configuration. Unable to authenticate using OIDC.');
+
+ return $default;
+ }
+
+ // Get provider from list.
+ if (!empty($providerId)
+ && isset($providers[$providerId])
+ ) {
+ $provider = $providers[$providerId];
+ }
+
+ // Get config var from $provider array.
+ if (isset($provider[$configVariableName])
+ ) {
+ return $provider[$configVariableName];
+ }
+
+ return $default;
+ }
+
// Parse the query params from a URL. If a param matches the provider ID selector
// then return the value.
public function parseProviderIdFromURL(string $url): ?string
@@ -434,6 +477,42 @@ protected function getTokenContents($tokenType)
return null;
}
+ /**
+ * Set provider server cert.
+ *
+ * @param mixed $certPath
+ */
+ protected function setServerCert($certPath = false)
+ {
+ // Validate the server SSL certificate according to configuration.
+ if (0 === !strpos($certPath, '/')) {
+ $certPath = sfConfig::get('sf_root_dir').DIRECTORY_SEPARATOR.$certPath;
+ }
+
+ if (file_exists($certPath)) {
+ $this->oidcClient->setCertPath($certPath);
+ } elseif (false === $certPath) {
+ // OIDC server SSL certificate disabled.
+ } else {
+ throw new Exception('Invalid OIDC SSL certificate settings. Please review the app_oidc_server_cert parameter in plugin app.yml.');
+ }
+ }
+
+ /**
+ * Set provider OIDC scopes from config.
+ */
+ protected function setOidcScopes(array $scopes = []): void
+ {
+ // Validate requested scopes.
+ $validScopes = arOidc::validateScopes($scopes);
+ // Add scopes only if the array is not empty
+ if (!empty($validScopes)) {
+ $this->oidcClient->addScope($validScopes);
+ } else {
+ throw new Exception('No valid OIDC scopes found in app_oidc_scopes.');
+ }
+ }
+
/**
* Parse group claims for role info returned by OIDC server. Returns
* array containing assigned roles. Claims are searched based on nodes
@@ -494,7 +573,7 @@ protected function setGroupsFromOidcGroups($user, array $groups)
// Add the user to AclUserGroups based on the presence of expected OIDC
// group values as set in app_oidc_user_groups.
- $userGroups = sfConfig::get('app_oidc_user_groups');
+ $userGroups = $this->getProviderConfigValue('user_groups', []);
foreach ($userGroups as $item) {
if (null !== $group = QubitAclGroup::getById($item['group_id'])) {
$expectedValue = $item['attribute_value'];
diff --git a/plugins/arOidcPlugin/modules/user/actions/editAction.class.php b/plugins/arOidcPlugin/modules/user/actions/editAction.class.php
index db9ccc3235..af57443c00 100644
--- a/plugins/arOidcPlugin/modules/user/actions/editAction.class.php
+++ b/plugins/arOidcPlugin/modules/user/actions/editAction.class.php
@@ -104,7 +104,7 @@ protected function earlyExecute()
{
$this->form->getValidatorSchema()->setOption('allow_extra_fields', true);
- if (false === sfConfig::get('app_oidc_auto_create_atom_user', true)) {
+ if (false === sfContext::getinstance()->user->getProviderConfigValue('auto_create_atom_user', true)) {
$this->form->getValidatorSchema()->setPostValidator(
new sfValidatorCallback(['callback' => [$this, 'exists']])
);
@@ -136,7 +136,7 @@ protected function addField($name)
{
switch ($name) {
case 'username':
- if (false === sfConfig::get('app_oidc_auto_create_atom_user', true)) {
+ if (false === sfContext::getinstance()->user->getProviderConfigValue('auto_create_atom_user', true)) {
$this->form->setDefault('username', $this->resource->username);
$this->form->setValidator('username', new sfValidatorString(['required' => true]));
$this->form->setWidget('username', new sfWidgetFormInput());
@@ -145,7 +145,7 @@ protected function addField($name)
break;
case 'email':
- if (false === sfConfig::get('app_oidc_auto_create_atom_user', true)) {
+ if (false === sfContext::getinstance()->user->getProviderConfigValue('auto_create_atom_user', true)) {
$this->form->setDefault('email', $this->resource->email);
$this->form->setValidator('email', new sfValidatorEmail(['required' => true]));
$this->form->setWidget('email', new sfWidgetFormInput());
@@ -315,7 +315,7 @@ protected function processField($field)
case 'username':
case 'email':
- if (false === sfConfig::get('app_oidc_auto_create_atom_user', true)) {
+ if (false === sfContext::getinstance()->user->getProviderConfigValue('auto_create_atom_user', true)) {
$this->resource[$name] = $this->form->getValue($name);
}
diff --git a/plugins/arOidcPlugin/modules/user/templates/_showActions.php b/plugins/arOidcPlugin/modules/user/templates/_showActions.php
index 435ed65751..76d94d209f 100644
--- a/plugins/arOidcPlugin/modules/user/templates/_showActions.php
+++ b/plugins/arOidcPlugin/modules/user/templates/_showActions.php
@@ -10,7 +10,7 @@