class CasAttributesSubscriber in CAS Attributes 8
Same name and namespace in other branches
- 2.x src/Subscriber/CasAttributesSubscriber.php \Drupal\cas_attributes\Subscriber\CasAttributesSubscriber
Provides a CasAttributesSubscriber.
Hierarchy
- class \Drupal\cas_attributes\Subscriber\CasAttributesSubscriber implements \Symfony\Component\EventDispatcher\EventSubscriberInterface
Expanded class hierarchy of CasAttributesSubscriber
1 file declares its use of CasAttributesSubscriber
- CasAttributesSubscriberTest.php in tests/
src/ Unit/ Subscriber/ CasAttributesSubscriberTest.php
1 string reference to 'CasAttributesSubscriber'
1 service uses CasAttributesSubscriber
File
- src/
Subscriber/ CasAttributesSubscriber.php, line 18
Namespace
Drupal\cas_attributes\SubscriberView source
class CasAttributesSubscriber implements EventSubscriberInterface {
/**
* Settings object for CAS attributes.
*
* @var \Drupal\Core\Config\Config
*/
protected $settings;
/**
* Token service.
*
* @var \Drupal\Core\Utility\Token
*/
protected $tokenService;
/**
* The request stack.
*
* @var \Symfony\Component\HttpFoundation\RequestStack
*/
protected $requestStack;
/**
* Constructor.
*
* @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
* The configuration factory to get module settings.
* @param \Drupal\Core\Utility\Token $token_service
* The token service for token replacement.
* @param \Symfony\Component\HttpFoundation\RequestStack $request_stack
* The request stack to get the current request.
*/
public function __construct(ConfigFactoryInterface $config_factory, Token $token_service, RequestStack $request_stack) {
$this->settings = $config_factory
->get('cas_attributes.settings');
$this->tokenService = $token_service;
$this->requestStack = $request_stack;
}
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents() {
$events[CasHelper::EVENT_PRE_REGISTER][] = [
'onPreRegister',
-1,
];
$events[CasHelper::EVENT_PRE_LOGIN][] = [
'onPreLogin',
20,
];
$events[CasHelper::EVENT_POST_LOGIN][] = [
'onPostLogin',
];
return $events;
}
/**
* Subscribe to the CasPreRegisterEvent.
*
* @param \Drupal\cas\Event\CasPreRegisterEvent $event
* The CasPreAuthEvent containing property information.
*/
public function onPreRegister(CasPreRegisterEvent $event) {
if ($this->settings
->get('field.sync_frequency') !== CasAttributesSettings::SYNC_FREQUENCY_NEVER) {
// Map fields.
$field_mappings = $this
->getFieldMappings($event
->getCasPropertyBag()
->getAttributes());
if (!empty($field_mappings)) {
$event
->setPropertyValues($field_mappings);
}
}
if ($this->settings
->get('role.sync_frequency') !== CasAttributesSettings::SYNC_FREQUENCY_NEVER) {
// Map roles.
$roleMappingResults = $this
->doRoleMapCheck($event
->getCasPropertyBag()
->getAttributes());
if (empty($roleMappingResults['add']) && $this->settings
->get('role.deny_registration_no_match')) {
$event
->setAllowAutomaticRegistration(FALSE);
}
else {
// Add/remove roles from the user.
$existingProperties = $event
->getPropertyValues();
$rolesForUser = [];
if (!empty($existingProperties['roles'])) {
$rolesForUser = $existingProperties['roles'];
}
$rolesForUser = array_diff($rolesForUser, $roleMappingResults['remove']);
$rolesForUser = array_merge($rolesForUser, $roleMappingResults['add']);
$rolesForUser = array_unique($rolesForUser);
$event
->setPropertyValue('roles', $rolesForUser);
}
}
}
/**
* Subscribe to the CasPreLoginEvent.
*
* @param \Drupal\cas\Event\CasPreLoginEvent $event
* The CasPreAuthEvent containing account and property information.
*/
public function onPreLogin(CasPreLoginEvent $event) {
$account = $event
->getAccount();
// Map fields.
if ($this->settings
->get('field.sync_frequency') === CasAttributesSettings::SYNC_FREQUENCY_EVERY_LOGIN) {
$field_mappings = $this
->getFieldMappings($event
->getCasPropertyBag()
->getAttributes());
if (!empty($field_mappings)) {
// If field already has data, only set new value if configured to
// overwrite existing data.
$overwrite = $this->settings
->get('field.overwrite');
foreach ($field_mappings as $field_name => $field_value) {
if ($overwrite || empty($account
->get($field_name))) {
$account
->set($field_name, $field_value);
}
}
}
}
// Map roles.
$roleMappingResults = $this
->doRoleMapCheck($event
->getCasPropertyBag()
->getAttributes());
if ($this->settings
->get('role.sync_frequency') === CasAttributesSettings::SYNC_FREQUENCY_EVERY_LOGIN) {
foreach ($roleMappingResults['remove'] as $rid) {
$account
->removeRole($rid);
}
foreach ($roleMappingResults['add'] as $rid) {
$account
->addRole($rid);
}
}
if (empty($roleMappingResults['add']) && $this->settings
->get('role.deny_login_no_match')) {
$event
->cancelLogin();
}
}
/**
* Map fields to the pre-defined CAS token values.
*
* @param array $casAttributes
* The list of CAS attributes for the user logging in.
*/
protected function getFieldMappings(array $casAttributes) {
$mappings = $this->settings
->get('field.mappings');
if (empty($mappings)) {
return [];
}
$field_data = [];
foreach ($mappings as $field_name => $attribute_token) {
$result = trim($this->tokenService
->replace($attribute_token, [
'cas_attributes' => $casAttributes,
], [
'clear' => TRUE,
]));
$result = html_entity_decode($result);
// Only update the fields if there is data to set.
if (!empty($result)) {
$field_data[$field_name] = $result;
}
}
return $field_data;
}
/**
* Determine which roles should be added/removed based on attributes.
*
* @param array $attributes
* The attributes associated with the user.
*
* @return array
* Array containing two keys:
* - add: the list of RIDs to add to the user
* - remove: the list of RIDs to remove from the user
*/
protected function doRoleMapCheck(array $attributes = NULL) {
$role_map = $this->settings
->get('role.mappings');
if (empty($role_map)) {
return [
'add' => [],
'remove' => [],
];
}
$rolesToAdd = [];
$rolesToRemove = [];
foreach ($role_map as $condition) {
// Attribute not found; don't map role.
if (!isset($attributes[$condition['attribute']])) {
continue;
}
$attributeValue = $attributes[$condition['attribute']];
if (!is_array($attributeValue)) {
$attributeValue = [
$attributeValue,
];
}
$valueToMatch = $condition['value'];
$matched = FALSE;
switch ($condition['method']) {
case 'exact_single':
$matched = $this
->checkRoleMatchExactSingle($attributeValue, $valueToMatch);
break;
case 'exact_any':
$matched = $this
->checkRoleMatchExactAny($attributeValue, $valueToMatch);
break;
case 'contains_any':
$matched = $this
->checkRoleMatchContainsAny($attributeValue, $valueToMatch);
break;
case 'regex_any':
$matched = $this
->checkRoleMatchRegexAny($attributeValue, $valueToMatch);
default:
}
if (isset($condition['negate']) && $condition['negate']) {
$matched = !$matched;
}
if ($matched) {
$rolesToAdd[] = $condition['rid'];
}
elseif ($condition['remove_without_match']) {
$rolesToRemove[] = $condition['rid'];
}
}
return [
'add' => $rolesToAdd,
'remove' => $rolesToRemove,
];
}
/**
* Check if attributes match using the 'exact_single' method.
*
* This method checks that the the attribute values match exactly.
* The attribute is expected to be a single value, and not a multi value
* attribute.
*
* @param array $attributeValue
* The actual attribute value.
* @param string $valueToMatch
* The attribute value to compare against.
*
* @return bool
* TRUE if there's a match, FALSE otherwise.
*/
protected function checkRoleMatchExactSingle(array $attributeValue, $valueToMatch) {
// The expectation for this method is that the attribute is not multi-value.
if (count($attributeValue) > 1) {
return FALSE;
}
$value = array_shift($attributeValue);
if ($value === $valueToMatch) {
return TRUE;
}
return FALSE;
}
/**
* Check if attributes match using the 'exact_any' method.
*
* This is the same as the 'exact_single' method, except it will check if any
* of the elements in a multi-value attribute match the expected value
* exactly.
*
* @param array $attributeValue
* The actual attribute value.
* @param string $valueToMatch
* The attribute value to compare against.
*
* @return bool
* TRUE if there's a match, FALSE otherwise.
*/
protected function checkRoleMatchExactAny(array $attributeValue, $valueToMatch) {
foreach ($attributeValue as $value) {
if ($value === $valueToMatch) {
return TRUE;
}
}
return FALSE;
}
/**
* Check if attributes match using the 'contains_any' method.
*
* Works by checking if any item in attribute value contains the value to
* match as a substring.
*
* @param array $attributeValue
* The actual attribute value.
* @param string $valueToMatch
* The attribute value to compare against.
*
* @return bool
* TRUE if there's a match, FALSE otherwise.
*/
protected function checkRoleMatchContainsAny(array $attributeValue, $valueToMatch) {
foreach ($attributeValue as $value) {
if (strpos($value, $valueToMatch) !== FALSE) {
return TRUE;
}
}
return FALSE;
}
/**
* Check if attribuets match using the 'regex_any' method.
*
* Each item in attribute array is checked with a regex.
*
* @param array $attributeValue
* The actual attribute value.
* @param string $regex
* The regular expression pattern.
*
* @return bool
* TRUE if there's a match, FALSE otherwise.
*/
protected function checkRoleMatchRegexAny(array $attributeValue, $regex) {
foreach ($attributeValue as $value) {
if (@preg_match($regex, $value)) {
return TRUE;
}
}
return FALSE;
}
/**
* Save attributes to user session if sitewide token support is enabled.
*
* @param \Drupal\cas\Event\CasPostLoginEvent $casPostLoginEvent
* The post login event from CAS.
*/
public function onPostLogin(CasPostLoginEvent $casPostLoginEvent) {
if ($this->settings
->get('sitewide_token_support')) {
$session = $this->requestStack
->getCurrentRequest()
->getSession();
$session
->set('cas_attributes', $casPostLoginEvent
->getCasPropertyBag()
->getAttributes());
}
}
}
Members
Name | Modifiers | Type | Description | Overrides |
---|---|---|---|---|
CasAttributesSubscriber:: |
protected | property | The request stack. | |
CasAttributesSubscriber:: |
protected | property | Settings object for CAS attributes. | |
CasAttributesSubscriber:: |
protected | property | Token service. | |
CasAttributesSubscriber:: |
protected | function | Check if attributes match using the 'contains_any' method. | |
CasAttributesSubscriber:: |
protected | function | Check if attributes match using the 'exact_any' method. | |
CasAttributesSubscriber:: |
protected | function | Check if attributes match using the 'exact_single' method. | |
CasAttributesSubscriber:: |
protected | function | Check if attribuets match using the 'regex_any' method. | |
CasAttributesSubscriber:: |
protected | function | Determine which roles should be added/removed based on attributes. | |
CasAttributesSubscriber:: |
protected | function | Map fields to the pre-defined CAS token values. | |
CasAttributesSubscriber:: |
public static | function | Returns an array of event names this subscriber wants to listen to. | |
CasAttributesSubscriber:: |
public | function | Save attributes to user session if sitewide token support is enabled. | |
CasAttributesSubscriber:: |
public | function | Subscribe to the CasPreLoginEvent. | |
CasAttributesSubscriber:: |
public | function | Subscribe to the CasPreRegisterEvent. | |
CasAttributesSubscriber:: |
public | function | Constructor. |