View source
<?php
namespace Drupal\samlauth_user_roles\EventSubscriber;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\samlauth\Event\SamlauthEvents;
use Drupal\samlauth\Event\SamlauthUserSyncEvent;
use Drupal\user\UserInterface;
use Psr\Log\LoggerInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
class UserRolesEventSubscriber implements EventSubscriberInterface {
const CONFIG_OBJECT_NAME = 'samlauth_user_roles.mapping';
protected $configFactory;
protected $entityTypeManager;
protected $logger;
public function __construct(ConfigFactoryInterface $config_factory, LoggerInterface $logger, EntityTypeManagerInterface $entity_type_manager) {
$this->configFactory = $config_factory;
$this->logger = $logger;
$this->entityTypeManager = $entity_type_manager;
}
public static function getSubscribedEvents() {
$events[SamlauthEvents::USER_SYNC][] = [
'onUserSync',
];
return $events;
}
public function onUserSync(SamlauthUserSyncEvent $event) {
$config = $this->configFactory
->get(static::CONFIG_OBJECT_NAME);
if ($config
->get('only_first_login') && !$event
->isFirstLogin()) {
return;
}
$valid_roles = $this->entityTypeManager
->getStorage('user_role')
->loadMultiple();
unset($valid_roles[UserInterface::ANONYMOUS_ROLE]);
unset($valid_roles[UserInterface::AUTHENTICATED_ROLE]);
$account = $event
->getAccount();
$changed_role_ids = $account_role_ids = $account
->getRoles();
$role_names = $config
->get('unassign_roles');
if ($role_names) {
if (is_array($role_names)) {
$changed_role_ids = array_diff($changed_role_ids, $this
->getRoleIds($role_names, $valid_roles, 'unassign_roles'));
}
else {
$this->logger
->warning('Invalid %name configuration value; skipping role unassignment.', [
'%name' => 'unassign_roles',
]);
}
}
$role_names = $config
->get('default_roles');
if ($role_names) {
if (is_array($role_names)) {
$changed_role_ids = array_unique(array_merge($changed_role_ids, $this
->getRoleIds($role_names, $valid_roles, 'default_roles')));
}
else {
$this->logger
->warning('Invalid %name configuration value; skipping part of role assignment.', [
'%name' => 'default_roles',
]);
}
}
$attribute_name = $config
->get('saml_attribute');
$value_map = $config
->get('value_map');
if ($attribute_name) {
if ($value_map && is_array($value_map)) {
$separator = $config
->get('saml_attribute_separator');
$idp_role_values = [];
$attributes = $event
->getAttributes();
if (isset($attributes[$attribute_name])) {
if (!is_array($attributes[$attribute_name])) {
if (is_string($attributes[$attribute_name])) {
$attributes[$attribute_name] = [
$attributes[$attribute_name],
];
}
else {
$this->logger
->warning('%name attribute is not an array of values; this points to a coding error.', [
'%name' => $attribute_name,
]);
}
}
if (is_array($attributes[$attribute_name])) {
foreach ($attributes[$attribute_name] as $attribute_value) {
if ($attribute_value != NULL) {
if (!is_string($attribute_value)) {
$this->logger
->warning('%name attribute contains a (or multiple) non-string value(s); this points to a coding error.', [
'%name' => $attribute_name,
]);
}
if ($separator) {
$idp_role_values = array_merge($idp_role_values, explode($separator, $attribute_value));
}
else {
$idp_role_values[] = $attribute_value;
}
}
}
}
}
if ($idp_role_values) {
foreach (array_map('trim', $idp_role_values) as $idp_role_value) {
foreach ($value_map as $mapping) {
if (isset($mapping['attribute_value'])) {
if ($idp_role_value === $mapping['attribute_value']) {
if (isset($mapping['role_machine_name'])) {
if (isset($valid_roles[$mapping['role_machine_name']])) {
$changed_role_ids[] = $valid_roles[$mapping['role_machine_name']]
->id();
}
else {
$this->logger
->warning('Unknown/invalid role %role in %name configuration value; (partially?) skipping role assignment.', [
'%name' => 'value_map',
'%role' => $mapping['role_machine_name'],
]);
}
}
else {
$this->logger
->warning('%subname not present in %name configuration value; (partially?) skipping role assignment.', [
'%name' => 'value_map',
'%subname' => 'role_machine_name',
]);
}
}
}
else {
$this->logger
->warning('%subname not present in %name configuration value; role assignment may be partially skipped.', [
'%name' => 'value_map',
'%subname' => 'attribute_value',
]);
}
}
}
$changed_role_ids = array_unique($changed_role_ids);
}
}
elseif (!is_array($value_map)) {
$this->logger
->warning('%name is not an array; skipping role mapping.', [
'%name' => 'value_map',
]);
}
else {
$this->logger
->warning('%name is not configured; skipping role mapping.', [
'%name' => 'value_map',
]);
}
}
elseif ($value_map && trim($value_map)) {
$this->logger
->warning('%name is not configured; skipping role mapping.', [
'%name' => 'saml_attribute',
]);
}
sort($account_role_ids);
sort($changed_role_ids);
if ($changed_role_ids != $account_role_ids) {
foreach (array_diff($account_role_ids, $changed_role_ids) as $role_id) {
$account
->removeRole($role_id);
}
foreach (array_diff($changed_role_ids, $account_role_ids) as $role_id) {
$account
->addRole($role_id);
}
$event
->markAccountChanged();
}
}
protected function getRoleIds(array $role_names, array $valid_roles_by_name, $config_log_name) {
$role_ids = [];
foreach ($role_names as $role_name) {
if (isset($valid_roles_by_name[$role_name])) {
$role_ids[] = $valid_roles_by_name[$role_name]
->id();
}
else {
$this->logger
->warning('Unknown/invalid role %role in %name configuration value; skipping part of role (un)assignment.', [
'%name' => $config_log_name,
'%role' => $role_name,
]);
}
}
return $role_ids;
}
}