You are here

RestfulCsrfTokenTestCase.test in RESTful 7.2

Same filename and directory in other branches
  1. 7 tests/RestfulCsrfTokenTestCase.test

Contains RestfulCsrfTokenTestCase

File

tests/RestfulCsrfTokenTestCase.test
View source
<?php

/**
 * @file
 * Contains RestfulCsrfTokenTestCase
 */
use Drupal\restful\Http\RequestInterface;
use Drupal\restful\Http\Request;
class RestfulCsrfTokenTestCase extends RestfulCurlBaseTestCase {

  /**
   * Write operations.
   *
   * @var string[]
   */
  protected $writeOperations = array(
    RequestInterface::METHOD_POST,
    RequestInterface::METHOD_PUT,
    RequestInterface::METHOD_PATCH,
    RequestInterface::METHOD_DELETE,
  );

  /**
   * Holds the current value of the $_SERVER super global.
   *
   * @var array
   */
  protected $originalServer;

  /**
   * {@inheritdoc}
   */
  public static function getInfo() {
    return array(
      'name' => 'CSRF token',
      'description' => 'Test the validation of a CSRF token for write operations.',
      'group' => 'RESTful',
    );
  }

  /**
   * {@inheritdoc}
   */
  public function setUp() {
    parent::setUp('restful_example');
  }

  /**
   * Test the validation of a CSRF token for authenticated users.
   */
  public function testCsrfToken() {
    global $user;
    $permissions = array(
      'create article content',
      'edit any article content',
      'delete any article content',
    );
    $account = $this
      ->drupalCreateUser($permissions);
    $this
      ->drupalLogin($account);
    $user = $account;

    // Check CSRF is not checked for read operations.
    $this
      ->checkCsrfRequest(array(
      RequestInterface::METHOD_GET,
    ), FALSE);
    $this
      ->checkCsrfRequest($this->writeOperations, TRUE);
  }

  /**
   * Test the validation of a CSRF token for anonymous users.
   */
  public function testCsrfTokenAnon() {
    global $user;
    $user = drupal_anonymous_user();

    // Allow anonymous user to CRUD article content.
    $permissions = array(
      'create article content' => TRUE,
      'edit any article content' => TRUE,
      'delete any article content' => TRUE,
    );
    user_role_change_permissions(DRUPAL_ANONYMOUS_RID, $permissions);
    $this
      ->checkCsrfRequest($this->writeOperations, FALSE, FALSE);
  }

  /**
   * Perform requests without, with invalid and with valid CSRF tokens.
   *
   * @param array $methods
   *   Array with HTTP method names.
   * @param bool $csrf_required
   *   Indicate if CSRF is required for the request, thus errors would be set if
   *   no CSRF or invalid one is sent with the request.
   * @param bool $auth_user
   *   Determine if a user should be created and logged in. Defaults to TRUE.
   */
  protected function checkCsrfRequest($methods = array(), $csrf_required, $auth_user = TRUE) {
    $params['@role'] = $auth_user ? 'authenticated' : 'anonymous';
    foreach ($methods as $method) {
      $request = Request::isReadMethod($method) ? array() : array(
        'label' => $this
          ->randomName(),
      );
      $params['@method'] = $method;

      // No CSRF token.
      $result = $this
        ->httpRequest($this
        ->getPath($method), $method, $request, array(
        'Content-Type' => 'application/x-www-form-urlencoded',
      ), FALSE);
      if ($csrf_required) {
        $params['@code'] = 400;
        $this
          ->assertEqual($result['code'], $params['@code'], format_string('@code on @method without CSRF token for @role user.', $params));
      }
      else {
        $this
          ->assertTrue($result['code'] >= 200 && $result['code'] <= 204, format_string('@method without CSRF token for @role user is allowed.', $params));
      }

      // Invalid CSRF token.
      $result = $this
        ->httpRequest($this
        ->getPath($method), $method, $request, array(
        'Content-Type' => 'application/x-www-form-urlencoded',
        'X-CSRF-Token' => 'invalidToken',
      ), FALSE);
      if ($csrf_required) {
        $params['@code'] = 403;
        $this
          ->assertEqual($result['code'], $params['@code'], format_string('@code on @method with invalid CSRF token for @role user.', $params));
      }
      else {
        $this
          ->assertTrue($result['code'] >= 200 && $result['code'] <= 204, format_string('@method with invalid CSRF token for @role user is allowed.', $params));
      }

      // Valid CSRF token.
      $result = $this
        ->httpRequest($this
        ->getPath($method), $method, $request, array(
        'Content-Type' => 'application/x-www-form-urlencoded',
      ));
      $this
        ->assertTrue($result['code'] >= 200 && $result['code'] <= 204, format_string('@method allowed with CSRF token for @role user.', $params));
    }
  }

  /**
   * Get the path for the request.
   *
   * For non "POST" methods, a new node is created.
   *
   * @param $method
   *   The HTTP method.
   *
   * @return string
   *   The path for the request.
   */
  protected function getPath($method) {
    if ($method == RequestInterface::METHOD_POST) {
      return 'api/v1.0/articles';
    }
    $settings = array(
      'type' => 'article',
      'title' => $this
        ->randomString(),
    );
    $node = $this
      ->drupalCreateNode($settings);
    return 'api/v1.0/articles/' . $node->nid;
  }

}

Classes