Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

EWPP-4886: Automatic corporate role mappings. #194

Open
wants to merge 1 commit into
base: EWPP-4912
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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