You are here

class DeployAuthenticatorSession in Deploy - Content Staging 7.3

Same name and namespace in other branches
  1. 7.2 plugins/DeployAuthenticatorSession.inc \DeployAuthenticatorSession

Authentication class that uses session authentication.

Hierarchy

Expanded class hierarchy of DeployAuthenticatorSession

2 string references to 'DeployAuthenticatorSession'
deploy_deploy_authenticators in ./deploy.deploy.inc
Implements hook_deploy_authentications().
deploy_example_deploy_endpoints_default in modules/deploy_example/deploy_example.deploy_endpoints.inc
Implements hook_deploy_endpoints_default().

File

plugins/DeployAuthenticatorSession.inc, line 11
Session based authentication plugin.

View source
class DeployAuthenticatorSession implements DeployAuthenticator {

  /**
   * Configuration options.
   *
   * @var array
   */
  public $config = array();

  /**
   * {@inheritdoc}
   */
  public function __construct(DeployService $service, array $config = array()) {
    $this->service = $service;
    $this->config += array(
      'debug' => FALSE,
      'username' => '',
      'password' => '',
    );
    if (!empty($config['username']) && !empty($config['password'])) {
      $config['username'] = _deploy_decrypt($config['username']);
      $config['password'] = _deploy_decrypt($config['password']);
    }
    $this->config = array_merge($this->config, $config);
  }

  /**
   * {@inheritdoc}
   */
  public function deploy(Traversable $iterator) {
    $verify_ssl = variable_get('deploy_verify_ssl', TRUE);
    $context_options = array(
      'ssl' => array(
        'verify_peer' => $verify_ssl,
        'verify_peer_name' => $verify_ssl,
        'allow_self_signed' => !$verify_ssl,
      ),
    );
    $context = stream_context_create($context_options);

    // Only authenticate if we've got a username and password. This allows for
    // deployment as an anonymous user. There are usecases for that.
    if (!empty($this->config['username'])) {

      // TODO: Consider making the resource/action path configurable. For now,
      // the default Services path is OK, since it covers 99% of the use cases.
      $login_url = $this->service->config['url'] . '/user/login';
      $logout_url = $this->service->config['url'] . '/user/logout';
      $options = array(
        'method' => 'POST',
        'headers' => array(
          'Content-Type' => 'application/json',
        ),
        'data' => drupal_json_encode(array(
          'username' => $this->config['username'],
          'password' => $this->config['password'],
        )),
        'context' => $context,
      );
      if ($this->config['debug']) {
        watchdog('deploy', 'Authentication request: %url <pre>@options</pre>', array(
          '%url' => $login_url,
          '@options' => print_r($options, TRUE),
        ), WATCHDOG_DEBUG);
      }

      // Login on the endpoint.
      $response = drupal_http_request($login_url, $options);
      if ($this->config['debug']) {
        watchdog('deploy', 'Authentication response: <pre>@response</pre>', array(
          '@response' => print_r($response, TRUE),
        ), WATCHDOG_DEBUG);
      }
      if (isset($response->error) || !in_array($response->code, array(
        200,
        304,
      ))) {
        throw new DeployAuthenticationException(t('Authentication error: @code @error', array(
          '@code' => $response->code,
          '@error' => $response->error,
        )));
      }
      $response_data = drupal_json_decode($response->data);
      if ($response_data['session_name'] && $response_data['sessid']) {
        $this->service->config['headers']['Cookie'] = $response_data['session_name'] . "=" . $response_data['sessid'] . ";";

        // We have to get a CSFR Token from the server and pass along with
        // each services call (introduced in Services 7.x-3.4
        // as per SA-CONTRIB-2013-051; https://drupal.org/node/2012982).
        $token = isset($response_data['token']) ? $response_data['token'] : NULL;

        // To get the token directly from the login request requires Services
        // 7.x-3.6 or newer, for older versions we need a separate request.
        if (!$token) {
          $user = NULL;
          $parts = parse_url($this->service->config['url']);
          $port = '';
          if (isset($parts['port'])) {
            $port = ':' . $parts['port'];
          }
          if (isset($parts['user'])) {
            $user = $parts['user'];
          }
          if (isset($parts['pass'])) {
            $pass = $parts['pass'];
          }
          $token_url = $parts['scheme'] . '://' . ($user ? $user . ':' . $pass . '@' : '') . $parts['host'] . $port . '/services/session/token';
          $options = array(
            'method' => 'GET',
            'headers' => $this->service->config['headers'],
            'context' => $context,
          );
          $token_response = drupal_http_request($token_url, $options);
          if (isset($token_response->error) || !in_array($token_response->code, array(
            200,
            304,
          ))) {
            throw new DeployAuthenticationException(t('Authentication error: @code @error', array(
              '@code' => $token_response->code,
              '@error' => t('Failed to retrieve CSRF token.'),
            )));
          }
          if ($this->config['debug']) {
            watchdog('deploy', 'Session CSRF Token response: @response', array(
              '@response' => print_r($token_response, TRUE),
            ), WATCHDOG_DEBUG);
          }
          $token = trim($token_response->data);
        }
        $this->service->config['headers']['X-CSRF-Token'] = $token;
      }
      else {
        throw new DeployAuthenticationException(t("No session was returned from the authentication request."));
      }
    }
    else {
      watchdog('deploy', 'Deployment authentication was done without user credentials.', array(), WATCHDOG_WARNING);
    }

    // Deploy the plan.
    $this->service
      ->deploy($iterator);

    // We only need to log out if we logged in and got a session cookie.
    if (!empty($this->service->config['headers']['Cookie'])) {

      // Log out, since we are done now.
      $options = array(
        'method' => 'POST',
        'headers' => array(
          'Cookie' => $response->headers['set-cookie'],
          'X-CSRF-Token' => $token,
        ),
        'context' => $context,
      );
      $response = drupal_http_request($logout_url, $options);
      if ($this->config['debug']) {
        watchdog('deploy', 'Logout response: <pre>@response</pre>', array(
          '@response' => print_r($response, TRUE),
        ), WATCHDOG_DEBUG);
      }
    }
  }

  /**
   * {@inheritdoc}
   */
  public function configForm(&$form_state) {
    return array(
      'username' => array(
        '#type' => 'textfield',
        '#title' => t('Username'),
        '#description' => t('Enter the username that you want to authenticate with on this endpoint.'),
        '#default_value' => $this->config['username'],
      ),
      'password' => array(
        '#type' => 'password',
        '#title' => t('Password'),
        '#description' => t('Enter the password that you want to authenticate with on this endpoint.'),
        '#default_value' => $this->config['password'],
      ),
    );
  }

}

Members

Namesort descending Modifiers Type Description Overrides
DeployAuthenticatorSession::$config public property Configuration options.
DeployAuthenticatorSession::configForm public function Defines the configuration form for the authenticator. Overrides DeployAuthenticator::configForm
DeployAuthenticatorSession::deploy public function Initiates an authenticated deployment. Overrides DeployAuthenticator::deploy
DeployAuthenticatorSession::__construct public function Constructor for a deploy authenticator. Overrides DeployAuthenticator::__construct