You are here

public function UserFieldsEventSubscriber::onUserLink in SAML Authentication 4.x

Same name and namespace in other branches
  1. 8.3 modules/samlauth_user_fields/src/EventSubscriber/UserFieldsEventSubscriber.php \Drupal\samlauth_user_fields\EventSubscriber\UserFieldsEventSubscriber::onUserLink()

Tries to link an existing user based on SAML attribute values.

Parameters

\Drupal\samlauth\Event\SamlauthUserLinkEvent $event: The event being dispatched.

File

modules/samlauth_user_fields/src/EventSubscriber/UserFieldsEventSubscriber.php, line 95

Class

UserFieldsEventSubscriber
Synchronizes SAML attributes into user fields / links new users during login.

Namespace

Drupal\samlauth_user_fields\EventSubscriber

Code

public function onUserLink(SamlauthUserLinkEvent $event) {
  $match_expressions = $this
    ->getMatchExpressions($event
    ->getAttributes());
  $config = $this->configFactory
    ->get(static::CONFIG_OBJECT_NAME);
  foreach ($match_expressions as $match_expression) {
    $query = $this->entityTypeManager
      ->getStorage('user')
      ->getQuery();
    if ($config
      ->get('ignore_blocked')) {
      $query
        ->condition('status', 1);
    }
    foreach ($match_expression as $field_name => $value) {
      $query
        ->condition($field_name, $value);
    }
    $results = $query
      ->execute();

    // @todo we should figure out what we want to do with users that are
    //   already 'linked' in the authmap table. Maybe we want to exclude
    //   them from the query results; maybe we want to include them and
    //   (optionally) give an error if we encounter them. At this point, we
    //   include them without error. The main module will just "link" this
    //   user, which will silently fail (because of the existing link) and
    //   be repeated on the next login. This is consistent with existing
    //   behavior for name/email. I may want to wait with refining this
    //   behavior, until the behavior of ExternalAuth::linkExistingAccount()
    //   is clear and stable. (IMHO it currently is not / I think there are
    //   outstanding issues which will influence its behavior.)
    // @todo when we change that, change "existing (local|Drupal)? user" to
    //   "existing non-linked (local|Drupal)? user" in descriptions.
    $count = count($results);
    if ($count) {
      if ($count > 1) {
        $query = [];
        foreach ($match_expression as $field_name => $value) {
          $query[] = "{$field_name}={$value}";
        }
        if ($config
          ->get('ignore_blocked')) {
          $query[] = "status=1";
        }
        if (!$config
          ->get('link_first_user')) {
          $this->logger
            ->error("Denying login because SAML data match is ambiguous: @count matching users (@uids) found for @query", [
            '@count' => $count,
            '@uids' => implode(',', $results),
            '@query' => implode(',', $query),
          ]);
          throw new UserVisibleException('It is unclear which user should be logged in. Please contact an administrator.');
        }
        $this->logger
          ->notice("Selecting first of @count matching users to link (@uids) for @query", [
          '@count' => $count,
          '@uids' => implode(',', $results),
          '@query' => implode(',', $query),
        ]);
      }
      $account = $this->entityTypeManager
        ->getStorage('user')
        ->load(reset($results));
      if (!$account) {
        throw new \RuntimeException('Found user %uid to link on login, but it cannot be loaded.');
      }
      $event
        ->setLinkedAccount($account);
      break;
    }
  }
}