Mercurial > hg > isophonics-drupal-site
comparison core/modules/rest/tests/src/Functional/CookieResourceTestTrait.php @ 0:4c8ae668cc8c
Initial import (non-working)
author | Chris Cannam |
---|---|
date | Wed, 29 Nov 2017 16:09:58 +0000 |
parents | |
children | 7a779792577d |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:4c8ae668cc8c |
---|---|
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 } |