Upgraded drupal core with security updates
[yaffs-website] / web / core / modules / rest / tests / src / Functional / EntityResource / User / UserResourceTestBase.php
1 <?php
2
3 namespace Drupal\Tests\rest\Functional\EntityResource\User;
4
5 use Drupal\Core\Url;
6 use Drupal\Tests\rest\Functional\EntityResource\EntityResourceTestBase;
7 use Drupal\user\Entity\User;
8 use GuzzleHttp\RequestOptions;
9
10 abstract class UserResourceTestBase extends EntityResourceTestBase {
11
12   /**
13    * {@inheritdoc}
14    */
15   public static $modules = ['user'];
16
17   /**
18    * {@inheritdoc}
19    */
20   protected static $entityTypeId = 'user';
21
22   /**
23    * {@inheritdoc}
24    */
25   protected static $patchProtectedFieldNames = [
26     'changed',
27   ];
28
29   /**
30    * @var \Drupal\user\UserInterface
31    */
32   protected $entity;
33
34   /**
35    * {@inheritdoc}
36    */
37   protected static $labelFieldName = 'name';
38
39   /**
40    * {@inheritdoc}
41    */
42   protected static $firstCreatedEntityId = 4;
43
44   /**
45    * {@inheritdoc}
46    */
47   protected static $secondCreatedEntityId = 5;
48
49   /**
50    * {@inheritdoc}
51    */
52   protected function setUpAuthorization($method) {
53     switch ($method) {
54       case 'GET':
55         $this->grantPermissionsToTestedRole(['access user profiles']);
56         break;
57       case 'POST':
58       case 'PATCH':
59       case 'DELETE':
60         $this->grantPermissionsToTestedRole(['administer users']);
61         break;
62     }
63   }
64
65   /**
66    * {@inheritdoc}
67    */
68   protected function createEntity() {
69     // Create a "Llama" user.
70     $user = User::create(['created' => 123456789]);
71     $user->setUsername('Llama')
72       ->setChangedTime(123456789)
73       ->activate()
74       ->save();
75
76     return $user;
77   }
78
79   /**
80    * {@inheritdoc}
81    */
82   protected function getExpectedNormalizedEntity() {
83     return [
84       'uid' => [
85         ['value' => 3],
86       ],
87       'uuid' => [
88         ['value' => $this->entity->uuid()],
89       ],
90       'langcode' => [
91         [
92           'value' => 'en',
93         ],
94       ],
95       'name' => [
96         [
97           'value' => 'Llama',
98         ],
99       ],
100       'created' => [
101         [
102           'value' => 123456789,
103         ],
104       ],
105       'changed' => [
106         [
107           'value' => $this->entity->getChangedTime(),
108         ],
109       ],
110       'default_langcode' => [
111         [
112           'value' => TRUE,
113         ],
114       ],
115     ];
116   }
117
118   /**
119    * {@inheritdoc}
120    */
121   protected function getNormalizedPostEntity() {
122     return [
123       'name' => [
124         [
125           'value' => 'Dramallama ' . $this->randomMachineName(),
126         ],
127       ],
128     ];
129   }
130
131   /**
132    * Tests PATCHing security-sensitive base fields of the logged in account.
133    */
134   public function testPatchDxForSecuritySensitiveBaseFields() {
135     // The anonymous user is never allowed to modify itself.
136     if (!static::$auth) {
137       $this->markTestSkipped();
138     }
139
140     $this->initAuthentication();
141     $this->provisionEntityResource();
142
143     /** @var \Drupal\user\UserInterface $user */
144     $user = static::$auth ? $this->account : User::load(0);
145     // @todo Remove the array_diff_key() call in https://www.drupal.org/node/2821077.
146     $original_normalization = array_diff_key($this->serializer->normalize($user, static::$format), ['created' => TRUE, 'changed' => TRUE, 'name' => TRUE]);
147
148
149     // Since this test must be performed by the user that is being modified,
150     // we cannot use $this->getUrl().
151     $url = $user->toUrl()->setOption('query', ['_format' => static::$format]);
152     $request_options = [
153       RequestOptions::HEADERS => ['Content-Type' => static::$mimeType],
154     ];
155     $request_options = array_merge_recursive($request_options, $this->getAuthenticationRequestOptions('PATCH'));
156
157
158     // Test case 1: changing email.
159     $normalization = $original_normalization;
160     $normalization['mail'] = [['value' => 'new-email@example.com']];
161     $request_options[RequestOptions::BODY] = $this->serializer->encode($normalization, static::$format);
162
163
164     // DX: 422 when changing email without providing the password.
165     $response = $this->request('PATCH', $url, $request_options);
166     $this->assertResourceErrorResponse(422, "Unprocessable Entity: validation failed.\nmail: Your current password is missing or incorrect; it's required to change the Email.\n", $response);
167
168
169     $normalization['pass'] = [['existing' => 'wrong']];
170     $request_options[RequestOptions::BODY] = $this->serializer->encode($normalization, static::$format);
171
172     // DX: 422 when changing email while providing a wrong password.
173     $response = $this->request('PATCH', $url, $request_options);
174     $this->assertResourceErrorResponse(422, "Unprocessable Entity: validation failed.\nmail: Your current password is missing or incorrect; it's required to change the Email.\n", $response);
175
176
177     $normalization['pass'] = [['existing' => $this->account->passRaw]];
178     $request_options[RequestOptions::BODY] = $this->serializer->encode($normalization, static::$format);
179
180
181     // 200 for well-formed request.
182     $response = $this->request('PATCH', $url, $request_options);
183     $this->assertResourceResponse(200, FALSE, $response);
184
185
186     // Test case 2: changing password.
187     $normalization = $original_normalization;
188     $new_password = $this->randomString();
189     $normalization['pass'] = [['value' => $new_password]];
190     $request_options[RequestOptions::BODY] = $this->serializer->encode($normalization, static::$format);
191
192
193     // DX: 422 when changing password without providing the current password.
194     $response = $this->request('PATCH', $url, $request_options);
195     $this->assertResourceErrorResponse(422, "Unprocessable Entity: validation failed.\npass: Your current password is missing or incorrect; it's required to change the Password.\n", $response);
196
197
198     $normalization['pass'][0]['existing'] = $this->account->pass_raw;
199     $request_options[RequestOptions::BODY] = $this->serializer->encode($normalization, static::$format);
200
201
202     // 200 for well-formed request.
203     $response = $this->request('PATCH', $url, $request_options);
204     $this->assertResourceResponse(200, FALSE, $response);
205
206
207     // Verify that we can log in with the new password.
208     $this->assertRpcLogin($user->getAccountName(), $new_password);
209
210
211     // Update password in $this->account, prepare for future requests.
212     $this->account->passRaw = $new_password;
213     $this->initAuthentication();
214     $request_options = [
215       RequestOptions::HEADERS => ['Content-Type' => static::$mimeType],
216     ];
217     $request_options = array_merge_recursive($request_options, $this->getAuthenticationRequestOptions('PATCH'));
218
219
220     // Test case 3: changing name.
221     $normalization = $original_normalization;
222     $normalization['name'] = [['value' => 'Cooler Llama']];
223     $request_options[RequestOptions::BODY] = $this->serializer->encode($normalization, static::$format);
224
225
226     // DX: 403 when modifying username without required permission.
227     $response = $this->request('PATCH', $url, $request_options);
228     $this->assertResourceErrorResponse(403, "Access denied on updating field 'name'.", $response);
229
230
231     $this->grantPermissionsToTestedRole(['change own username']);
232
233
234     // 200 for well-formed request.
235     $response = $this->request('PATCH', $url, $request_options);
236     $this->assertResourceResponse(200, FALSE, $response);
237
238     // Verify that we can log in with the new username.
239     $this->assertRpcLogin('Cooler Llama', $new_password);
240   }
241
242   /**
243    * Verifies that logging in with the given username and password works.
244    *
245    * @param string $username
246    *   The username to log in with.
247    * @param string $password
248    *   The password to log in with.
249    */
250   protected function assertRpcLogin($username, $password) {
251     $request_body = [
252       'name' => $username,
253       'pass' => $password,
254     ];
255     $request_options = [
256       RequestOptions::HEADERS => [],
257       RequestOptions::BODY => $this->serializer->encode($request_body, 'json'),
258     ];
259     $response = $this->request('POST', Url::fromRoute('user.login.http')->setRouteParameter('_format', 'json'), $request_options);
260     $this->assertSame(200, $response->getStatusCode());
261   }
262
263   /**
264    * {@inheritdoc}
265    */
266   protected function getExpectedUnauthorizedAccessMessage($method) {
267     if ($this->config('rest.settings')->get('bc_entity_resource_permissions')) {
268       return parent::getExpectedUnauthorizedAccessMessage($method);
269     }
270
271     switch ($method) {
272       case 'GET':
273         return "The 'access user profiles' permission is required and the user must be active.";
274       case 'PATCH':
275         return "You are not authorized to update this user entity.";
276       case 'DELETE':
277         return 'You are not authorized to delete this user entity.';
278       default:
279         return parent::getExpectedUnauthorizedAccessMessage($method);
280     }
281   }
282
283 }