Upgraded drupal core with security updates
[yaffs-website] / web / core / modules / rest / tests / src / Functional / CookieResourceTestTrait.php
1 <?php
2
3 namespace Drupal\Tests\rest\Functional;
4
5 use Drupal\Core\Url;
6 use GuzzleHttp\RequestOptions;
7 use Psr\Http\Message\ResponseInterface;
8
9 /**
10  * Trait for ResourceTestBase subclasses testing $auth=cookie.
11  *
12  * Characteristics:
13  * - After performing a valid "log in" request, the server responds with a 2xx
14  *   status code and a 'Set-Cookie' response header. This cookie is what
15  *   continues to identify the user in subsequent requests.
16  * - When accessing a URI that requires authentication without being
17  *   authenticated, a standard 403 response must be sent.
18  * - Because of the reliance on cookies, and the fact that user agents send
19  *   cookies with every request, this is vulnerable to CSRF attacks. To mitigate
20  *   this, the response for the "log in" request contains a CSRF token that must
21  *   be sent with every unsafe (POST/PATCH/DELETE) HTTP request.
22  */
23 trait CookieResourceTestTrait {
24
25   /**
26    * The session cookie.
27    *
28    * @see ::initAuthentication
29    *
30    * @var string
31    */
32   protected $sessionCookie;
33
34   /**
35    * The CSRF token.
36    *
37    * @see ::initAuthentication
38    *
39    * @var string
40    */
41   protected $csrfToken;
42
43   /**
44    * The logout token.
45    *
46    * @see ::initAuthentication
47    *
48    * @var string
49    */
50   protected $logoutToken;
51
52   /**
53    * {@inheritdoc}
54    */
55   protected function initAuthentication() {
56     $user_login_url = Url::fromRoute('user.login.http')
57       ->setRouteParameter('_format', static::$format);
58
59     $request_body = [
60       'name' => $this->account->name->value,
61       'pass' => $this->account->passRaw,
62     ];
63
64     $request_options[RequestOptions::BODY] = $this->serializer->encode($request_body, 'json');
65     $request_options[RequestOptions::HEADERS] = [
66       'Content-Type' => static::$mimeType,
67     ];
68     $response = $this->request('POST', $user_login_url, $request_options);
69
70     // Parse and store the session cookie.
71     $this->sessionCookie = explode(';', $response->getHeader('Set-Cookie')[0], 2)[0];
72
73     // Parse and store the CSRF token and logout token.
74     $data = $this->serializer->decode((string)$response->getBody(), static::$format);
75     $this->csrfToken = $data['csrf_token'];
76     $this->logoutToken = $data['logout_token'];
77   }
78
79   /**
80    * {@inheritdoc}
81    */
82   protected function getAuthenticationRequestOptions($method) {
83     $request_options[RequestOptions::HEADERS]['Cookie'] = $this->sessionCookie;
84     // @see https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html
85     if (!in_array($method, ['HEAD', 'GET', 'OPTIONS', 'TRACE'])) {
86       $request_options[RequestOptions::HEADERS]['X-CSRF-Token'] = $this->csrfToken;
87     }
88     return $request_options;
89   }
90
91   /**
92    * {@inheritdoc}
93    */
94   protected function assertResponseWhenMissingAuthentication(ResponseInterface $response) {
95     // Requests needing cookie authentication but missing it results in a 403
96     // response. The cookie authentication mechanism sets no response message.
97     // @todo https://www.drupal.org/node/2847623
98     $this->assertResourceErrorResponse(403, FALSE, $response);
99   }
100
101   /**
102    * {@inheritdoc}
103    */
104   protected function assertAuthenticationEdgeCases($method, Url $url, array $request_options) {
105     // X-CSRF-Token request header is unnecessary for safe and side effect-free
106     // HTTP methods. No need for additional assertions.
107     // @see https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html
108     if (in_array($method, ['HEAD', 'GET', 'OPTIONS', 'TRACE'])) {
109       return;
110     }
111
112
113     unset($request_options[RequestOptions::HEADERS]['X-CSRF-Token']);
114
115
116     // DX: 403 when missing X-CSRF-Token request header.
117     $response = $this->request($method, $url, $request_options);
118     $this->assertResourceErrorResponse(403, 'X-CSRF-Token request header is missing', $response);
119
120
121     $request_options[RequestOptions::HEADERS]['X-CSRF-Token'] = 'this-is-not-the-token-you-are-looking-for';
122
123
124     // DX: 403 when invalid X-CSRF-Token request header.
125     $response = $this->request($method, $url, $request_options);
126     $this->assertResourceErrorResponse(403, 'X-CSRF-Token request header is invalid', $response);
127
128
129     $request_options[RequestOptions::HEADERS]['X-CSRF-Token'] = $this->csrfToken;
130   }
131
132 }