protected function SamlService::doLogin in SAML Authentication 4.x
Same name and namespace in other branches
- 8.3 src/SamlService.php \Drupal\samlauth\SamlService::doLogin()
Logs a user in, creating / linking an account; synchronizes attributes.
Split off from acs() to... have at least some kind of split.
Parameters
string $unique_id: The unique ID (attribute value) contained in the SAML response.
\Drupal\Core\Session\AccountInterface|null $account: The existing user account derived from the unique ID, if any.
1 call to SamlService::doLogin()
- SamlService::acs in src/
SamlService.php - Processes a SAML response (Assertion Consumer Service).
File
- src/
SamlService.php, line 434
Class
- SamlService
- Governs communication between the SAML toolkit and the IdP / login behavior.
Namespace
Drupal\samlauthCode
protected function doLogin($unique_id, AccountInterface $account = NULL) {
$config = $this->configFactory
->get('samlauth.authentication');
$first_saml_login = FALSE;
if (!$account) {
$this->logger
->debug('No matching local users found for unique SAML ID @saml_id.', [
'@saml_id' => $unique_id,
]);
// Try to link an existing user: first through a custom event handler,
// then by name, then by email.
if ($config
->get('map_users')) {
$event = new SamlauthUserLinkEvent($this
->getAttributes());
$this->eventDispatcher
->dispatch(SamlauthEvents::USER_LINK, $event);
$account = $event
->getLinkedAccount();
if ($account) {
$this->logger
->info('Existing user @name (@uid) was newly matched to SAML login attributes; linking user and logging in.', [
'@name' => $account
->getAccountName(),
'@uid' => $account
->id(),
]);
}
}
// Linking by name / email: we also select accounts if they are blocked
// (and throw an exception later on) because 1) we don't want the
// selection to be dependent on the current account's state; 2) name and
// email are unique and would otherwise lead to another error while
// trying to create a new account with duplicate values.
if (!$account) {
$name = $this
->getAttributeByConfig('user_name_attribute');
if ($name && ($account_search = $this->entityTypeManager
->getStorage('user')
->loadByProperties([
'name' => $name,
]))) {
$account = current($account_search);
if ($config
->get('map_users_name')) {
$this->logger
->info('SAML login for name @name (as provided in a SAML attribute) matches existing Drupal account @uid; linking account and logging in.', [
'@name' => $name,
'@uid' => $account
->id(),
]);
}
else {
// We're not configured to link the account by name, but we still
// looked it up by name so we can give a better error message than
// the one caused by trying to save a new account with a duplicate
// name, later.
$this->logger
->warning('Denying login: SAML login for unique ID @saml_id matches existing Drupal account name @name and we are not configured to automatically link accounts.', [
'@saml_id' => $unique_id,
'@name' => $account
->getAccountName(),
]);
throw new UserVisibleException('A local user account with your login name already exists, and we are disallowed from linking it.');
}
}
}
if (!$account) {
$mail = $this
->getAttributeByConfig('user_mail_attribute');
if ($mail && ($account_search = $this->entityTypeManager
->getStorage('user')
->loadByProperties([
'mail' => $mail,
]))) {
$account = current($account_search);
if ($config
->get('map_users_mail')) {
$this->logger
->info('SAML login for email @mail (as provided in a SAML attribute) matches existing Drupal account @uid; linking account and logging in.', [
'@mail' => $mail,
'@uid' => $account
->id(),
]);
}
else {
// Treat duplicate email same as duplicate name above.
$this->logger
->warning('Denying login: SAML login for unique ID @saml_id matches existing Drupal account email @mail and we are not configured to automatically link the account.', [
'@saml_id' => $unique_id,
'@mail' => $account
->getEmail(),
]);
throw new UserVisibleException('A local user account with your login email address name already exists, and we are disallowed from linking it.');
}
}
}
if ($account) {
$this
->linkExistingAccount($unique_id, $account);
$first_saml_login = TRUE;
}
}
// If we haven't found an account to link, create one from the SAML
// attributes.
if (!$account) {
if ($config
->get('create_users')) {
// The register() call will save the account. We want to:
// - add values from the SAML response into the user account;
// - not save the account twice (because if the second save fails we do
// not want to end up with a user account in an undetermined state);
// - reuse code (i.e. call synchronizeUserAttributes() with its current
// signature, which is also done when an existing user logs in).
// Because of the third point, we are not passing the necessary SAML
// attributes into register()'s $account_data parameter, but we want to
// hook into the save operation of the user account object that is
// created by register(). It seems we can only do this by implementing
// hook_user_presave() - which calls our synchronizeUserAttributes().
$account_data = [
'name' => $this
->getAttributeByConfig('user_name_attribute'),
];
$account = $this->externalAuth
->register($unique_id, 'samlauth', $account_data);
$this->externalAuth
->userLoginFinalize($account, $unique_id, 'samlauth');
}
else {
throw new UserVisibleException('No existing user account matches the SAML ID provided. This authentication service is not configured to create new accounts.');
}
}
elseif ($account
->isBlocked()) {
throw new UserVisibleException('Requested account is blocked.');
}
else {
// Synchronize the user account with SAML attributes if needed.
$this
->synchronizeUserAttributes($account, FALSE, $first_saml_login);
$this->externalAuth
->userLoginFinalize($account, $unique_id, 'samlauth');
}
}