Skip to content

Commit

Permalink
EWPP-4886: Automatic corporate role mappings.
Browse files Browse the repository at this point in the history
  • Loading branch information
upchuk committed Nov 18, 2024
1 parent 6e40479 commit 50b79aa
Show file tree
Hide file tree
Showing 24 changed files with 1,547 additions and 11 deletions.
1 change: 0 additions & 1 deletion docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ services:
selenium:
image: selenium/standalone-chrome:4.1.3-20220405
environment:
- DISPLAY=:99
- SCREEN_WIDTH=1280
- SCREEN_HEIGHT=800
- VNC_NO_PASSWORD=1
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
langcode: en
status: true
dependencies:
config:
- field.storage.user.oe_corporate_roles_mappings
module:
- user
id: user.user.oe_corporate_roles_mappings
field_name: oe_corporate_roles_mappings
entity_type: user
bundle: user
label: 'Corporate roles mappings'
description: ''
required: false
translatable: false
default_value: { }
default_value_callback: ''
settings:
handler: 'default:corporate_roles_mapping'
handler_settings:
target_bundles: null
auto_create: false
field_type: entity_reference
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
langcode: en
status: true
dependencies:
module:
- oe_authentication_corporate_roles
- user
id: user.oe_corporate_roles_mappings
field_name: oe_corporate_roles_mappings
entity_type: user
type: entity_reference
settings:
target_type: corporate_roles_mapping
module: core
locked: false
cardinality: -1
translatable: true
indexes: { }
persist_with_no_fields: false
custom_storage: false
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
oe_authentication_corporate_roles.corporate_roles_mapping.*:
type: config_entity
label: Corporate roles mapping
mapping:
id:
type: string
label: ID
label:
type: label
label: Label
uuid:
type: string
matching_value_type:
type: string
value:
type: string
roles:
type: sequence
sequence:
type: string
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,5 @@ core_version_requirement: ^10
dependencies:
- cas:cas
- oe_authentication:oe_authentication
- oe_authentication:oe_authentication_user_fields
- drupal:field
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
entity.corporate_roles_mapping.add_form:
route_name: 'entity.corporate_roles_mapping.add_form'
title: 'Add corporate roles mapping'
appears_on:
- entity.corporate_roles_mapping.collection
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
entity.corporate_roles_mapping.overview:
title: Corporate role mappings
parent: user.admin_index
description: 'List of corporate roles mappings.'
route_name: entity.corporate_roles_mapping.collection
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
entity.corporate_roles_mapping.collection:
title: 'Corporate roles mappings'
route_name: entity.corporate_roles_mapping.collection
base_route: entity.user.collection
weight: 10
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@

declare(strict_types=1);

use Drupal\Core\Form\FormStateInterface;
use Drupal\oe_authentication_corporate_roles\Entity\CorporateRolesMapping;
use Drupal\user\Entity\Role;
use Drupal\user\UserInterface;

/**
Expand All @@ -32,8 +35,172 @@ function oe_authentication_corporate_roles_user_presave(UserInterface $user) {
$original_roles = $original->getRoles(TRUE);
$new_roles = $user->getRoles(TRUE);

if ($original_roles !== $new_roles) {
$user->set('oe_manual_roles', $user->getRoles(TRUE));
if ($original_roles === $new_roles) {
// If the roles are the same, we bail out.
return;
}

// Determine if there are any roles that have been assigned automatically to
// the user, and if there are, do not include them in the ones that are being
// set as manual.
$automatic_roles = [];
foreach ($user->get('oe_corporate_roles_mappings')->referencedEntities() as $mapping) {
$automatic_roles = array_merge($automatic_roles, $mapping->get('roles'));
}
$automatic_roles = array_unique(array_values($automatic_roles));
$new_roles = array_filter($new_roles, function ($role) use ($automatic_roles) {
return !in_array($role, $automatic_roles);
});

$user->set('oe_manual_roles', $new_roles);
}

/**
* Implements hook_ENTITY_TYPE_insert().
*
* When a new mapping is created, we need to find all the users that match it
* and update their roles to whatever it was mapped. We also store a reference
* to the mapping on the user entity.
*/
function oe_authentication_corporate_roles_corporate_roles_mapping_insert(CorporateRolesMapping $mapping) {
$users = \Drupal::service('oe_authentication_corporate_roles.mapping_lookup')->getMatchingUsers($mapping);

if (!$users) {
\Drupal::messenger()->addStatus(t('No users were found matching these conditions.'));

return;
}

foreach ($users as $user) {
$mapping->updateUserRoles($user);
$user->automatic_corporate_roles = TRUE;
$user->save();
}

\Drupal::messenger()->addStatus(t('@count users have had their roles updated.', ['@count' => count($users)]));
}

/**
* Implements hook_ENTITY_TYPE_update().
*
* When a mapping is updated, we need to do multiple steps:
* - we need to load all the users that reference the mapping and remove their
* roles. This is because the mapping may have changed and the conditions don't
* apply anymore.
* - we need to load all the users that match the conditions and set the roles.
* The same as we do in insert hook.
*/
function oe_authentication_corporate_roles_corporate_roles_mapping_update(CorporateRolesMapping $mapping) {
// Find all the users that are already mapped and clear their roles.
$users = \Drupal::service('oe_authentication_corporate_roles.mapping_lookup')->getUsersWithMapping($mapping);
// We need to use the original to know what roles it had before it was
// changed.
$original = $mapping->original;
foreach ($users as $user) {
// Remove the mapping roles.
$original->removeMappingRoles($user);
// We also need to remove the reference to the current mapping.
$original->removeMappingReference($user);
}

// Now, find the users that match.
$matched_users = \Drupal::service('oe_authentication_corporate_roles.mapping_lookup')->getMatchingUsers($mapping);

// Save all the users from the previous array that are not found in this
// list of matched users. This is because for those, the conditions no longer
// match and they need to be saved as-is, after having their roles cleared.
foreach ($users as $user) {
if (!isset($matched_users[$user->id()])) {
$user->automatic_corporate_roles = TRUE;
$user->save();
}
}

foreach ($matched_users as $user) {
$mapping->updateUserRoles($user);
$user->automatic_corporate_roles = TRUE;
$user->save();
}

\Drupal::messenger()->addStatus(t('@count users have had their roles updated.', ['@count' => count($matched_users)]));
}

/**
* Implements hook_ENTITY_TYPE_delete().
*/
function oe_authentication_corporate_roles_corporate_roles_mapping_delete(CorporateRolesMapping $mapping) {
// When we delete a mapping, ensure we load all the users that are mapped
// and clear their roles.
$ids = \Drupal::entityTypeManager()->getStorage('user')->getQuery()
->condition('oe_corporate_roles_mappings', $mapping->id())
->accessCheck(FALSE)
->execute();

if (!$ids) {
\Drupal::messenger()->addStatus(t('No users were updated for with the deletion of this mapping.'));
}

$users = \Drupal::entityTypeManager()->getStorage('user')->loadMultiple($ids);

foreach ($users as $user) {
// Remove the mapping roles.
$mapping->removeMappingRoles($user);
// Remove the reference to the current mapping.
$mapping->removeMappingReference($user);
$user->automatic_corporate_roles = TRUE;
$user->save();
}

\Drupal::messenger()->addStatus(t('@count users have had their roles updated.', ['@count' => count($users)]));
}

/**
* Implements hook_form_FORM_ID_alter().
*/
function oe_authentication_corporate_roles_form_user_form_alter(array &$form, FormStateInterface $form_state) {
if (!isset($form['account']['roles']['#access'])) {
return;
}

if (!$form['account']['roles']['#access']) {
return;
}

/** @var \Drupal\user\UserInterface $user */
$user = $form_state->getBuildInfo()['callback_object']->getEntity();

$automatic_roles = [];
$mapping_links = [];
foreach ($user->get('oe_corporate_roles_mappings')->referencedEntities() as $mapping) {
$automatic_roles = array_merge($automatic_roles, $mapping->get('roles'));
$mapping_link = $mapping->toLink(rel: 'edit-form')->toRenderable();
$mapping_links[] = $mapping_link;
}
$automatic_roles = array_unique(array_values($automatic_roles));
if (!$automatic_roles) {
return;
}

$roles = Role::loadMultiple($automatic_roles);
$labels = [];
foreach ($roles as $role) {
$labels[] = $role->label();
}

$mappings_list = [
'#theme' => 'item_list',
'#items' => $mapping_links,
];

$roles_list = [
'#theme' => 'item_list',
'#items' => $labels,
];

$message = t('<p>The following roles:</p>@roles<p>have been assigned to the user automatically via the Corporate role mapping(s):</p> @mappings <p>If you manually remove those roles, they will be added back the next time the user logs in.</p>', [
'@roles' => \Drupal::service('renderer')->renderRoot($roles_list),
'@mappings' => \Drupal::service('renderer')->renderRoot($mappings_list),
]);

\Drupal::messenger()->addWarning($message);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
manage corporate roles:
title: 'Administer corporate roles mapping'
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
entity.corporate_roles_mapping.collection:
path: '/admin/people/corporate-roles-mapping'
defaults:
_entity_list: 'corporate_roles_mapping'
_title: 'Corporate roles mapping configuration'
requirements:
_permission: 'manage corporate roles'

entity.corporate_roles_mapping.add_form:
path: '/admin/people/corporate_roles_mapping/add'
defaults:
_entity_form: 'corporate_roles_mapping.add'
_title: 'Add a corporate roles mapping'
requirements:
_permission: 'manage corporate roles'

entity.corporate_roles_mapping.edit_form:
path: '/admin/people/corporate-roles-mapping/{corporate_roles_mapping}'
defaults:
_entity_form: 'corporate_roles_mapping.edit'
_title: 'Edit a corporate roles mapping'
requirements:
_permission: 'manage corporate roles'

entity.corporate_roles_mapping.delete_form:
path: '/admin/people/corporate-roles-mapping/{corporate_roles_mapping}/delete'
defaults:
_entity_form: 'corporate_roles_mapping.delete'
_title: 'Delete a corporate roles mapping'
requirements:
_permission: 'manage corporate roles'
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
services:
oe_authentication_corporate_roles.mapping_lookup:
class: Drupal\oe_authentication_corporate_roles\CorporateRolesMappingLookup
arguments: ['@entity_type.manager']
oe_authentication_corporate_roles.event_subscriber:
class: Drupal\oe_authentication_corporate_roles\EventSubscriber\EuLoginSubscriber
arguments: ['@oe_authentication_corporate_roles.mapping_lookup']
tags:
- { name: event_subscriber }
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?php

declare(strict_types=1);

namespace Drupal\oe_authentication_corporate_roles;

use Drupal\Core\Config\Entity\ConfigEntityInterface;

/**
* Provides an interface defining a corporate roles mapping entity type.
*/
interface CorporateRolesMappingInterface extends ConfigEntityInterface {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?php

declare(strict_types=1);

namespace Drupal\oe_authentication_corporate_roles;

use Drupal\Core\Config\Entity\ConfigEntityListBuilder;
use Drupal\Core\Entity\EntityInterface;

/**
* Provides a listing of corporate roles mappings.
*/
final class CorporateRolesMappingListBuilder extends ConfigEntityListBuilder {

/**
* {@inheritdoc}
*/
public function buildHeader(): array {
$header['label'] = $this->t('Label');
$header['id'] = $this->t('Machine name');
$header['matching_value_type'] = $this->t('Matching type');
$header['value'] = $this->t('Value');
return $header + parent::buildHeader();
}

/**
* {@inheritdoc}
*/
public function buildRow(EntityInterface $entity): array {
/** @var \Drupal\oe_authentication_corporate_roles\CorporateRolesMappingInterface $entity */
$row['label'] = $entity->label();
$row['id'] = $entity->id();
$row['matching_value_type'] = $entity->get('matching_value_type');
$row['value'] = $entity->get('value');
return $row + parent::buildRow($entity);
}

}
Loading

0 comments on commit 50b79aa

Please sign in to comment.