View source
<?php
namespace Drupal\cas\Service;
use Drupal\cas\Event\CasPostLoginEvent;
use Drupal\cas\Event\CasPreLoginEvent;
use Drupal\cas\Event\CasPreRegisterEvent;
use Drupal\externalauth\AuthmapInterface;
use Drupal\externalauth\Exception\ExternalAuthRegisterException;
use Drupal\cas\Exception\CasLoginException;
use Drupal\externalauth\ExternalAuthInterface;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\user\UserInterface;
use Psr\Log\LogLevel;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
use Drupal\Core\Database\Connection;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Drupal\cas\CasPropertyBag;
use Drupal\Component\Utility\Crypt;
class CasUserManager {
const EMAIL_ASSIGNMENT_STANDARD = 0;
const EMAIL_ASSIGNMENT_ATTRIBUTE = 1;
protected $externalAuth;
protected $authmap;
protected $settings;
protected $session;
protected $connection;
protected $casHelper;
protected $casProxyHelper;
protected $eventDispatcher;
protected $provider = 'cas';
public function __construct(ExternalAuthInterface $external_auth, AuthmapInterface $authmap, ConfigFactoryInterface $settings, SessionInterface $session, Connection $database_connection, EventDispatcherInterface $event_dispatcher, CasHelper $cas_helper, CasProxyHelper $cas_proxy_helper = NULL) {
$this->externalAuth = $external_auth;
$this->authmap = $authmap;
$this->settings = $settings;
$this->session = $session;
$this->connection = $database_connection;
$this->eventDispatcher = $event_dispatcher;
$this->casHelper = $cas_helper;
if (!$cas_proxy_helper) {
@trigger_error('Calling CasUserManager::__construct() without the $cas_proxy_helper argument is deprecated in cas:8.x-1.6 and the $cas_proxy_helper argument will be required in cas:8.x-1.10.', E_USER_DEPRECATED);
$cas_proxy_helper = \Drupal::service('cas.proxy_helper');
}
$this->casProxyHelper = $cas_proxy_helper;
}
public function register($authname, array $property_values = [], $local_username = NULL) {
if (!$local_username) {
@trigger_error('Calling CasUserManager::register() without the $local_username argument is deprecated in cas:8.x-1.6 and the $local_username argument will be required in cas:8.x-2.0.', E_USER_DEPRECATED);
$local_username = $authname;
}
$property_values['name'] = $local_username;
$property_values['pass'] = $this
->randomPassword();
try {
$user = $this->externalAuth
->register($authname, $this->provider, $property_values);
} catch (ExternalAuthRegisterException $e) {
throw new CasLoginException($e
->getMessage(), CasLoginException::USERNAME_ALREADY_EXISTS);
}
return $user;
}
public function login(CasPropertyBag $property_bag, $ticket) {
$account = $this->externalAuth
->load($property_bag
->getUsername(), $this->provider);
if ($account === FALSE) {
$config = $this->settings
->get('cas.settings');
if ($config
->get('user_accounts.auto_register') === TRUE) {
$this->casHelper
->log(LogLevel::DEBUG, 'Existing account not found for user, attempting to auto-register.');
$cas_pre_register_event = new CasPreRegisterEvent($property_bag);
$cas_pre_register_event
->setPropertyValue('mail', $this
->getEmailForNewAccount($property_bag));
$this->casHelper
->log(LogLevel::DEBUG, 'Dispatching EVENT_PRE_REGISTER.');
$this->eventDispatcher
->dispatch(CasHelper::EVENT_PRE_REGISTER, $cas_pre_register_event);
if ($cas_pre_register_event
->getAllowAutomaticRegistration()) {
$account = $this
->register($property_bag
->getUsername(), $cas_pre_register_event
->getPropertyValues(), $cas_pre_register_event
->getDrupalUsername());
}
else {
throw new CasLoginException("Cannot register user, an event listener denied access.", CasLoginException::SUBSCRIBER_DENIED_REG);
}
}
else {
throw new CasLoginException("Cannot login, local Drupal user account does not exist.", CasLoginException::NO_LOCAL_ACCOUNT);
}
}
if (!$account
->isActive()) {
throw new CasLoginException(sprintf('The username %s has not been activated or is blocked.', $account
->getAccountName()), CasLoginException::ACCOUNT_BLOCKED);
}
$pre_login_event = new CasPreLoginEvent($account, $property_bag);
$this->casHelper
->log(LogLevel::DEBUG, 'Dispatching EVENT_PRE_LOGIN.');
$this->eventDispatcher
->dispatch(CasHelper::EVENT_PRE_LOGIN, $pre_login_event);
$account
->save();
if (!$pre_login_event
->getAllowLogin()) {
$reason = $pre_login_event
->getCancelLoginReason();
throw (new CasLoginException('Cannot login, an event listener denied access.', CasLoginException::SUBSCRIBER_DENIED_LOGIN))
->setSubscriberCancelReason($reason);
}
$this->externalAuth
->userLoginFinalize($account, $property_bag
->getUsername(), $this->provider);
$this
->storeLoginSessionData($this->session
->getId(), $ticket);
$this->session
->set('is_cas_user', TRUE);
$this->session
->set('cas_username', $property_bag
->getOriginalUsername());
$postLoginEvent = new CasPostLoginEvent($account, $property_bag);
$this->casHelper
->log(LogLevel::DEBUG, 'Dispatching EVENT_POST_LOGIN.');
$this->eventDispatcher
->dispatch(CasHelper::EVENT_POST_LOGIN, $postLoginEvent);
if ($this->settings
->get('proxy.initialize') && $property_bag
->getPgt()) {
$this->casHelper
->log(LogLevel::DEBUG, "Storing PGT information for this session.");
$this->casProxyHelper
->storePgtSession($property_bag
->getPgt());
}
}
protected function storeLoginSessionData($session_id, $ticket) {
if ($this->settings
->get('cas.settings')
->get('logout.enable_single_logout') === TRUE) {
$this->connection
->insert('cas_login_data')
->fields([
'sid',
'plainsid',
'ticket',
'created',
], [
Crypt::hashBase64($session_id),
$session_id,
$ticket,
time(),
])
->execute();
}
}
public function getCasUsernameForAccount($uid) {
return $this->authmap
->get($uid, $this->provider);
}
public function getUidForCasUsername($cas_username) {
return $this->authmap
->getUid($cas_username, $this->provider);
}
public function setCasUsernameForAccount(UserInterface $account, $cas_username) {
$this->authmap
->save($account, $this->provider, $cas_username);
}
public function removeCasUsernameForAccount(UserInterface $account) {
$this->authmap
->delete($account
->id(), $this->provider);
}
protected function randomPassword() {
return \user_password(30);
}
public function getEmailForNewAccount(CasPropertyBag $cas_property_bag) {
$email_assignment_strategy = $this->settings
->get('cas.settings')
->get('user_accounts.email_assignment_strategy');
if ($email_assignment_strategy === self::EMAIL_ASSIGNMENT_STANDARD) {
return $cas_property_bag
->getUsername() . '@' . $this->settings
->get('cas.settings')
->get('user_accounts.email_hostname');
}
elseif ($email_assignment_strategy === self::EMAIL_ASSIGNMENT_ATTRIBUTE) {
$email_attribute = $this->settings
->get('cas.settings')
->get('user_accounts.email_attribute');
if (empty($email_attribute) || !array_key_exists($email_attribute, $cas_property_bag
->getAttributes())) {
throw new CasLoginException('Specified CAS email attribute does not exist.', CasLoginException::ATTRIBUTE_PARSING_ERROR);
}
$val = $cas_property_bag
->getAttributes()[$email_attribute];
if (empty($val)) {
throw new CasLoginException('Empty data found for CAS email attribute.', CasLoginException::ATTRIBUTE_PARSING_ERROR);
}
if (is_array($val) && count($val) !== 1) {
throw new CasLoginException('Specified CAS email attribute was formatted in an unexpected way.', CasLoginException::ATTRIBUTE_PARSING_ERROR);
}
if (is_array($val)) {
$val = $val[0];
}
return trim($val);
}
else {
throw new CasLoginException('Invalid email address assignment type for auto user registration specified in settings.');
}
}
}