microspid-8.x-1.0-beta12/src/Service/MicrospidDrupalAuth.php

src/Service/MicrospidDrupalAuth.php
<?php

namespace Drupal\microspid\Service;

use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\user\UserInterface;
use Drupal\user\Entity\User;
use Drupal\Core\Session\AccountInterface;
use Psr\Log\LoggerInterface;
use Drupal\externalauth\ExternalAuthInterface;

/**
 * Service to link MicroSPiD authentication with Drupal users.
 */
class MicrospidDrupalAuth {

  /**
   * MicroSPiD Authentication helper.
   *
   * @var SpidPaswManager
   */
  protected $spid;

  /**
   * A configuration object.
   *
   * @var \Drupal\Core\Config\ImmutableConfig
   */
  protected $config;

  /**
   * The entity type manager.
   *
   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
   */
  protected $entityTypeManager;

  /**
   * A logger instance.
   *
   * @var \Psr\Log\LoggerInterface
   */
  protected $logger;

  /**
   * The ExternalAuth service.
   *
   * @var \Drupal\externalauth\ExternalAuth
   */
  protected $externalauth;

  /**
   * The currently logged in user.
   *
   * @var \Drupal\Core\Session\AccountInterface
   */
  protected $currentUser;

  /**
   * {@inheritdoc}
   *
   * @param SpidPaswManager $simplesaml_auth
   *   The SimpleSAML Authentication helper service.
   * @param ConfigFactoryInterface $config_factory
   *   The configuration factory.
   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
   *   The entity type manager.
   * @param LoggerInterface $logger
   *   A logger instance.
   * @param ExternalAuthInterface $externalauth
   *   The ExternalAuth service.
   * @param AccountInterface $account
   *   The currently logged in user.
   */
  public function __construct(SpidPaswManager $simplesaml_auth, ConfigFactoryInterface $config_factory, EntityTypeManagerInterface $entity_type_manager, LoggerInterface $logger, ExternalAuthInterface $externalauth, AccountInterface $account) {
    $this->spid = $simplesaml_auth;
    $this->config = $config_factory->get('microspid.settings');
    $this->entityTypeManager = $entity_type_manager;
    $this->logger = $logger;
    $this->externalauth = $externalauth;
    $this->currentUser = $account;
  }

  /**
   * Log in and optionally register a user based on the authname provided.
   *
   * @param string $authname
   *   The authentication name.
   *
   * @return \Drupal\Core\Entity\EntityInterface|null
   *   The logged in Drupal user.
   */
  public function externalLoginRegister($authname) {
    $account = $this->externalauth->login($authname, 'microspid');
    if (!$account) {
      $account = $this->externalRegister($authname);
    }

    if ($account) {
      // Determine if roles should be evaluated upon login.
      if ($this->config->get('role.eval_every_time')) {
        $this->roleMatchAdd($account);
      }
    }

    return $account;
  }

  /**
   * Registers a user locally as one authenticated by the SimpleSAML IdP.
   *
   * @param string $authname
   *   The authentication name.
   *
   * @return \Drupal\Core\Entity\EntityInterface|bool
   *   The registered Drupal user.
   *
   * @throws \Exception
   *   An ExternalAuth exception.
   */
  public function externalRegister($authname) {
    $account = FALSE;

    // It's possible that a user with their username set to this authname
    // already exists in the Drupal database.
    $existing_user = $this->entityTypeManager->getStorage('user')->loadByProperties(['name' => $authname]);
    $existing_user = $existing_user ? reset($existing_user) : FALSE;

    // First we check the admin settings for MicroSPiD and find out if we
    // are allowed to register users.
    if (!$this->config->get('register_users') && !$existing_user) {

      // We're not allowed to register new users on the site through simpleSAML.
      // We let the user know about this and redirect to the user/login page.
      drupal_set_message(t("We are sorry. While you have successfully authenticated, you are not yet entitled to access this site. Please ask the site administrator to provision access for you."));
      $this->spid->logout(base_path());

      return FALSE;
    }

    if ($existing_user) {
      // If auto-enable SAML is activated, link this user to SAML.
      if ($this->config->get('autoenablesaml')) {
        if ($this->config->get('debug')) {
          $this->logger->debug('Linking authname %authname to existing Drupal user with ID %id because "Automatically enable SAML authentication for existing users upon successful login" setting is activated.', [
            '%authname' => $authname,
            '%id' => $existing_user->id(),
          ]);
        }
        $this->externalauth->linkExistingAccount($authname, 'microspid', $existing_user);
        $account = $existing_user;
      }
      else {
        if ($this->config->get('debug')) {
          $this->logger->debug('A local Drupal user with username %authname already exists. Aborting the creation of a SAML-enabled Drupal user.', [
            '%authname' => $authname,
          ]);
        }
        // User is not permitted to login to Drupal via SAML.
        // Log out of SAML and redirect to the front page.
        drupal_set_message(t('We are sorry, your user account is not SAML enabled.'));
        $this->spid->logout(base_path());
        return FALSE;
      }
    }
    else {
      // If auto-enable SAML is activated, take more action to find an existing
      // user.
      if ($this->config->get('autoenablesaml')) {
        // Allow other modules to decide if there is an existing Drupal user,
        // based on the supplied SAML atttributes.
        $attributes = $this->spid->getAttributes();
        foreach (\Drupal::moduleHandler()->getImplementations('microspid_existing_user') as $module) {
          $return_value = \Drupal::moduleHandler()->invoke($module, 'microspid_existing_user', [$attributes]);
          if ($return_value instanceof UserInterface) {
            $account = $return_value;
            if ($this->config->get('debug')) {
              $this->logger->debug('Linking authname %authname to existing Drupal user with ID %id because "Automatically enable SAML authentication for existing users upon successful login" setting is activated.', [
                '%authname' => $authname,
                '%id' => $account->id(),
              ]);
            }
            $this->externalauth->linkExistingAccount($authname, 'microspid', $account);
          }
        }
      }
    }

    if (!$account) {
      // Create the new user.
      try {
        $_SESSION['spiduser'] = '__creating__';
        $account = $this->externalauth->register($authname, 'microspid');
        unset($_SESSION['spiduser']);
      }
      catch (\Exception $ex) {
        watchdog_exception('microspid', $ex);
        drupal_set_message(t('Error registering user: An account with this username already exists.'), 'error');
      }
    }

    if ($account) {
      $this->synchronizeUserAttributes($account, TRUE);
      return $this->externalauth->userLoginFinalize($account, $authname, 'microspid');
    }
  }

  /**
   * Synchronizes user data if enabled.
   *
   * @param \Drupal\Core\Session\AccountInterface $account
   *   The Drupal account to synchronize attributes on.
   * @param bool $force
   *   Define whether to force syncing of the user attributes, regardless of
   *   MicroSPiD settings.
   */
  public function synchronizeUserAttributes(AccountInterface $account, $force = FALSE) {
    $do_save = FALSE;
    $sync_mail = $force || $this->config->get('sync.mail');
	// TEMP PASW never sync user name
    $sync_user_name = FALSE; //$force || $this->config->get('sync.user_name');

    if ($sync_user_name) {
      $do_save = TRUE;
      $name = $this->spid->getDefaultName();
      if ($name) {
        $existing = FALSE;
        $account_search = $this->entityTypeManager->getStorage('user')->loadByProperties(['name' => $name]);
        if ($existing_account = reset($account_search)) {
          if ($this->currentUser->id() != $existing_account->id()) {
            $existing = TRUE;
            $this->logger->critical("Error on synchronizing name attribute: an account with the username %username already exists.", ['%username' => $name]);
            drupal_set_message(t('Error synchronizing username: an account with this username already exists.'), 'error');
          }
        }

        if (!$existing) {
          $account->setUsername($name);
        }
      }
      else {
        $this->logger->critical("Error on synchronizing name attribute: no username available for Drupal user %id.", ['%id' => $account->id()]);
        drupal_set_message(t('Error synchronizing username: no username is provided by SAML.'), 'error');
      }
    }

    if ($sync_mail) {
	  $do_save = TRUE;
      $mail = $this->spid->getDefaultEmail();
      if ($mail) {
        $account->setEmail($mail);
      }
      else {
        $this->logger->critical("Error on synchronizing mail attribute: no email address available for Drupal user %id.", ['%id' => $account->id()]);
        drupal_set_message(t('Error synchronizing mail: no email address is provided by SAML.'), 'error');
      }
    }

    $fields_cfg_names = ['firstname' => 'name','lastname' => 'familyName','place' => 'placeOfBirth','prov' => 'countyOfBirth','date' => 'dateOfBirth','cf' => 'fiscalNumber'];
    foreach($fields_cfg_names as $cfg_name => $spid_name) {
      $field = $this->config->get($cfg_name);
      if ($field != '') {
        $spid_value = $this->spid->getAttribute($spid_name);
        if ($spid_value) {
          $do_save = TRUE;
          if ($spid_name == 'fiscalNumber') $spid_value = substr($spid_value, 6);
          $account->set($field, $spid_value);
        }
      }
    }

    if ($do_save) {
      $account->save();
    }
  }

  /**
   * Adds roles to user accounts.
   *
   * @param UserInterface $account
   *   The Drupal user to add roles to.
   */
  public function roleMatchAdd(UserInterface $account) {
    // Get matching roles based on retrieved MicroSPiD attributes.
    $matching_roles = $this->getMatchingRoles();

    if ($matching_roles) {
      foreach ($matching_roles as $role_id) {
        if ($this->config->get('debug')) {
          $this->logger->debug('Adding role %role to user %name', [
            '%role' => $role_id,
            '%name' => $account->getAccountName(),
          ]);
        }
        $account->addRole($role_id);
      }
      $account->save();
    }
  }

  /**
   * Get matching user roles to assign to user.
   *
   * Matching roles are based on retrieved MicroSPiD attributes.
   *
   * @return array
   *   List of matching roles to assign to user.
   */
  public function getMatchingRoles() {
    $roles = [];
    // Obtain the role map stored. The role map is a concatenated string of
    // rules which, when SimpleSAML attributes on the user match, will add
    // roles to the user.
    // The full role map string, when mapped to the variables below, presents
    // itself thus:
    // $role_id:$key,$op,$value;$key,$op,$value;|$role_id:$key,$op,$value etc.
    if ($rolemap = $this->config->get('role.population')) {

      foreach (explode('|', $rolemap) as $rolerule) {
        list($role_id, $role_eval) = explode(':', $rolerule, 2);

        foreach (explode(';', $role_eval) as $role_eval_part) {
          if ($this->evalRoleRule($role_eval_part)) {
            $roles[$role_id] = $role_id;
          }
        }
      }
    }

    $attributes = $this->spid->getAttributes();
    \Drupal::modulehandler()->alter('microspid_user_roles', $roles, $attributes);
    return $roles;
  }

  /**
   * Determines whether a role should be added to an account.
   *
   * @param string $role_eval_part
   *   Part of the role evaluation rule.
   *
   * @return bool
   *   Whether a role should be added to the Drupal account.
   */
  protected function evalRoleRule($role_eval_part) {
    list($key, $op, $value) = explode(',', $role_eval_part);
    $attributes = $this->spid->getAttributes();

    if ($this->config->get('debug')) {
      $this->logger->debug('Evaluate rule (key=%key,operator=%op,value=%val', [
        '%key' => $key,
        '%op' => $op,
        '%val' => $value,
      ]);
    }

    if (!isset($attributes[$key])) {
      return FALSE;
    }
    $attribute = $attributes[$key];
    
    // A '=' requires the $value exactly matches the $attribute, A '@='
    // requires the portion after a '@' in the $attribute to match the
    // $value and a '~=' allows the value to match any part of any
    // element in the $attribute array.
    switch ($op) {
      case '=':
        return ($value == $attribute);

      case '@=':
        list($before, $after) = explode('@', $attribute);
        return ($after == $value);

      case '~=':
        if ($value == '*') {
          return TRUE;
        }
        return strpos($attribute, $value) !== FALSE;
    }
  }

}

Главная | Обратная связь

drupal hosting | друпал хостинг | it patrol .inc