oidc-1.0.0-alpha2/oidc.module
oidc.module
<?php
/**
* @file
* Main module file.
*/
use Drupal\Component\Utility\Xss;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Field\BaseFieldDefinition;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Render\BubbleableMetadata;
use Drupal\Core\Render\Element;
use Drupal\Core\Session\AccountInterface;
use Drupal\oidc\OpenidConnectRealm\OpenidConnectRealmConfigurableInterface;
use Drupal\oidc\Plugin\EntityReferenceSelection\UserSelection;
use Drupal\oidc\Plugin\Field\DisplayNameFieldItemList;
use Drupal\oidc\Plugin\LanguageNegotiation\LanguageNegotiationUrl;
use Drupal\oidc\Plugin\OpenidConnectRealm\GenericOpenidConnectRealm;
use Drupal\oidc\Plugin\Validation\Constraint\UserMailUnique;
use Drupal\user\UserInterface;
/**
* Implements hook_language_negotiation_info_alter().
*/
function oidc_language_negotiation_info_alter(array &$negotiation_info): void {
if (isset($negotiation_info['language-url'])) {
$negotiation_info['language-url']['class'] = LanguageNegotiationUrl::class;
}
}
/**
* Implements hook_validation_constraint_alter().
*/
function oidc_validation_constraint_alter(array &$definitions): void {
$definitions['UserMailUnique']['class'] = UserMailUnique::class;
}
/**
* Implements hook_entity_base_field_info().
*/
function oidc_entity_base_field_info(EntityTypeInterface $entity_type): array {
if ($entity_type->id() !== 'user') {
return [];
}
$fields = [];
$fields['given_name'] = BaseFieldDefinition::create('string')
->setLabel(t('Given name'))
->setCardinality(1)
->setDisplayConfigurable('view', TRUE);
$fields['family_name'] = BaseFieldDefinition::create('string')
->setLabel(t('Family name'))
->setCardinality(1)
->setDisplayConfigurable('view', TRUE);
$fields['display_name'] = BaseFieldDefinition::create('string')
->setLabel(t('Display name'))
->setComputed(TRUE)
->setCardinality(1)
->setClass(DisplayNameFieldItemList::class)
->setDisplayOptions('view', [
'label' => 'hidden',
'weight' => 20,
])
->setDisplayConfigurable('view', TRUE);
return $fields;
}
/**
* Implements hook_entity_reference_selection_alter().
*/
function oidc_entity_reference_selection_alter(array &$definitions): void {
if (isset($definitions['default:user'])) {
$definitions['default:user']['class'] = UserSelection::class;
}
if (isset($definitions['og:user']) && Drupal::service('module_handler')->moduleExists('og')) {
$definitions['og:user']['class'] = 'Drupal\oidc\Plugin\EntityReferenceSelection\OgUserSelection';
}
}
/**
* Implements hook_cron().
*/
function oidc_cron(): void {
/** @var \Drupal\oidc\OpenidConnectRealm\OpenidConnectRealmManagerInterface $realm_manager */
$realm_manager = Drupal::service('plugin.manager.openid_connect_realm');
foreach ($realm_manager->getDefinitions() as $plugin_id => $definition) {
$plugin = $realm_manager->loadInstance($plugin_id);
if ($plugin->isEnabled()) {
$plugin->updateJwks();
}
}
}
/**
* Implements hook_token_info().
*/
function oidc_token_info(): array {
$oidc = [];
$oidc['realm'] = [
'name' => t('Realm'),
'description' => t('The realm plugin ID.'),
];
$oidc['claim'] = [
'name' => t('Claim'),
'description' => t('Any of the claims retrieved during authentication.'),
];
$user = [];
$user['given-name'] = [
'name' => t('Given name'),
'description' => t('Given name of the user.'),
];
$user['family-name'] = [
'name' => t('Family name'),
'description' => t('Family name of the user.'),
];
$user['family-name-abbr'] = [
'name' => t('Family name abbriviation'),
'description' => t("Abbriviation of the user's family name."),
];
return [
'types' => [
'oidc' => [
'name' => t('OpenID Connect'),
'description' => t('Tokens related to the active OpenID Connect session.'),
],
],
'tokens' => [
'oidc' => $oidc,
'user' => $user,
],
];
}
/**
* Implements hook_tokens().
*/
function oidc_tokens($type, array $tokens, array $data, array $options, BubbleableMetadata $bubbleable_metadata): array {
$replacements = [];
if ($type === 'oidc') {
/** @var \Drupal\oidc\OpenidConnectSessionInterface $session */
$session = Drupal::service('oidc.openid_connect_session');
// The realm token.
if (isset($tokens['realm'])) {
$bubbleable_metadata->addCacheContexts(['user.openid_connect_realm']);
if ($session->isAuthenticated()) {
$replacements[$tokens['realm']] = $session->getRealmPluginId();
}
}
// Any claim tokens.
$claim_tokens = Drupal::token()->findWithPrefix($tokens, 'claim');
if (!empty($claim_tokens)) {
$bubbleable_metadata->addCacheContexts(['session']);
if ($session->isAuthenticated()) {
$json_web_tokens = $session->getJsonWebTokens();
foreach ($claim_tokens as $name => $original) {
$claim = $json_web_tokens->getClaim($name);
if (is_bool($claim)) {
$replacements[$original] = $claim ? '1' : '0';
}
elseif (is_string($claim) || is_numeric($claim)) {
$replacements[$original] = (string) $claim;
}
}
}
}
}
elseif ($type === 'user' && !empty($data['user'])) {
$account = $data['user'];
// User name tokens.
foreach ($tokens as $name => $original) {
switch ($name) {
case 'given-name':
$replacements[$original] = $account->get('given_name')->value;
break;
case 'family-name':
$replacements[$original] = $account->get('family_name')->value;
break;
case 'family-name-abbr':
$family_name = $account->get('family_name')->value;
if ($family_name && preg_match_all('/(?:^|\s+)([a-zA-Z]).*?/', $family_name, $matches)) {
$family_name = mb_strtoupper(implode('.', $matches[1])) . '.';
}
$replacements[$original] = $family_name;
break;
}
}
}
return $replacements;
}
/**
* Implements hook_tokens_alter().
*/
function oidc_tokens_alter(array &$replacements, array $context, BubbleableMetadata $bubbleable_metadata): void {
if ($context['type'] === 'user' && !empty($context['data']['user'])) {
$tokens = $context['tokens'];
if (isset($tokens['display-name'])) {
$bubbleable_metadata->addCacheableDependency(Drupal::config('oidc.provider'));
}
}
}
/**
* Implements hook_module_preuninstall().
*/
function oidc_module_preuninstall(string $module): void {
/** @var \Drupal\oidc\OpenidConnectRealm\OpenidConnectRealmManagerInterface $realm_manager */
$realm_manager = Drupal::service('plugin.manager.openid_connect_realm');
/** @var \Drupal\externalauth\AuthmapInterface $authmap */
$authmap = Drupal::service('externalauth.authmap');
foreach ($realm_manager->getAll($module) as $plugin_id) {
// Clear the storage or json web key set.
$plugin = $realm_manager->loadInstance($plugin_id);
if ($plugin instanceof GenericOpenidConnectRealm) {
$plugin->clearStorage();
}
else {
$plugin->clearJwks();
}
// Delete the instance configuration.
if ($plugin instanceof OpenidConnectRealmConfigurableInterface) {
$realm_manager->deleteInstance($plugin_id);
}
// Delete all authmap entries.
$authmap->deleteProvider('oidc:' . $plugin_id);
}
}
/**
* Implements hook_user_format_name_alter().
*/
function oidc_user_format_name_alter(string &$name, AccountInterface $account): void {
static $name_cache = [];
if ($account->isAnonymous()) {
return;
}
// Use the cached name if available.
$uid = $account->id();
if (array_key_exists($uid, $name_cache)) {
if (isset($name_cache[$uid])) {
$name = $name_cache[$uid];
}
return;
}
// Create a default cache entry.
$name_cache[$uid] = NULL;
// Get the authmap.
$authmap = Drupal::service('externalauth.authmap')->getAll($account->id());
if (count($authmap) !== 1) {
return;
}
// Ensure the provider is an OpenID Connect realm.
$provider = key($authmap);
if (strpos($provider, 'oidc:') !== 0) {
return;
}
/** @var \Drupal\oidc\OpenidConnectRealm\OpenidConnectRealmManager $realm_manager */
$realm_manager = Drupal::service('plugin.manager.openid_connect_realm');
$plugin_id = mb_substr($provider, 5);
if (!$realm_manager->hasDefinition($plugin_id)) {
return;
}
// Get the display name format.
$display_name = $realm_manager
->loadInstance($plugin_id)
->getDisplayNameFormat();
if ($display_name === '[user:account-name]') {
return;
}
// Load the full user entity.
if (!$account instanceof UserInterface) {
$account = Drupal::entityTypeManager()
->getStorage('user')
->load($account->id());
}
// Replace the tokens.
$display_name = Drupal::token()->replace($display_name, [
'user' => $account,
'clear' => TRUE,
]);
// Set it if not empty.
if (trim($display_name) !== '') {
$name = $display_name;
$name_cache[$uid] = $display_name;
}
}
/**
* Implements hook_form_FORM_ID_alter().
*/
function oidc_form_user_admin_settings_alter(array &$form, FormStateInterface $form_state, $form_id): void {
if (!Drupal::config('oidc.settings')->get('disable_user_routes')) {
return;
}
$form['email_pending_approval']['#access'] = FALSE;
$form['email_pending_approval_admin']['#access'] = FALSE;
$form['email_no_approval_required']['#access'] = FALSE;
$form['email_activated']['#access'] = FALSE;
$element = &$form['registration_cancellation'];
$element['#title'] = t('Cancellation');
foreach (Element::children($element) as $key) {
if ($key === 'user_cancel_method') {
continue;
}
$element[$key]['#access'] = FALSE;
}
}
/**
* Implements hook_form_FORM_ID_alter().
*/
function oidc_form_user_form_alter(array &$form, FormStateInterface $form_state, string $form_id): void {
$user = $form_state->getFormObject()->getEntity();
if (isset($form['account']) && $user->id() && $user->get('pass')->value === NULL) {
$form['account']['name']['#default_value'] = $user->get('name')->value;
$form['account']['name']['#access'] = FALSE;
$form['account']['mail']['#default_value'] = $user->get('mail')->value;
$form['account']['mail']['#access'] = FALSE;
$form['account']['pass']['#access'] = FALSE;
$form['account']['current_pass']['#access'] = FALSE;
}
}
/**
* Route title callback.
*
* @param \Drupal\user\UserInterface $user
* The user account.
*
* @return string|array
* The user account name as a render array or an empty string if $user is NULL.
*/
function oidc_user_title(?UserInterface $user = NULL): string|array {
return $user ? ['#markup' => $user->getDisplayName(), '#allowed_tags' => Xss::getHtmlTagList()] : '';
}
