Chris@0: setRouteParameter('_format', static::$format); Chris@0: Chris@0: $request_body = [ Chris@0: 'name' => $this->account->name->value, Chris@0: 'pass' => $this->account->passRaw, Chris@0: ]; Chris@0: Chris@14: $request_options[RequestOptions::BODY] = $this->serializer->encode($request_body, static::$format); Chris@0: $request_options[RequestOptions::HEADERS] = [ Chris@0: 'Content-Type' => static::$mimeType, Chris@0: ]; Chris@0: $response = $this->request('POST', $user_login_url, $request_options); Chris@0: Chris@0: // Parse and store the session cookie. Chris@0: $this->sessionCookie = explode(';', $response->getHeader('Set-Cookie')[0], 2)[0]; Chris@0: Chris@0: // Parse and store the CSRF token and logout token. Chris@0: $data = $this->serializer->decode((string) $response->getBody(), static::$format); Chris@0: $this->csrfToken = $data['csrf_token']; Chris@0: $this->logoutToken = $data['logout_token']; Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: protected function getAuthenticationRequestOptions($method) { Chris@0: $request_options[RequestOptions::HEADERS]['Cookie'] = $this->sessionCookie; Chris@0: // @see https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html Chris@0: if (!in_array($method, ['HEAD', 'GET', 'OPTIONS', 'TRACE'])) { Chris@0: $request_options[RequestOptions::HEADERS]['X-CSRF-Token'] = $this->csrfToken; Chris@0: } Chris@0: return $request_options; Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@14: protected function assertResponseWhenMissingAuthentication($method, ResponseInterface $response) { Chris@0: // Requests needing cookie authentication but missing it results in a 403 Chris@0: // response. The cookie authentication mechanism sets no response message. Chris@14: // Hence, effectively, this is just the 403 response that one gets as the Chris@14: // anonymous user trying to access a certain REST resource. Chris@14: // @see \Drupal\user\Authentication\Provider\Cookie Chris@0: // @todo https://www.drupal.org/node/2847623 Chris@14: if ($method === 'GET') { Chris@17: $expected_cookie_403_cacheability = $this->getExpectedUnauthorizedAccessCacheability() Chris@17: // @see \Drupal\Core\EventSubscriber\AnonymousUserResponseSubscriber::onRespond() Chris@17: ->addCacheableDependency($this->getExpectedUnauthorizedEntityAccessCacheability(FALSE)); Chris@14: // - \Drupal\Core\EventSubscriber\AnonymousUserResponseSubscriber applies Chris@14: // to cacheable anonymous responses: it updates their cacheability. Chris@14: // - A 403 response to a GET request is cacheable. Chris@14: // Therefore we must update our cacheability expectations accordingly. Chris@14: if (in_array('user.permissions', $expected_cookie_403_cacheability->getCacheContexts(), TRUE)) { Chris@14: $expected_cookie_403_cacheability->addCacheTags(['config:user.role.anonymous']); Chris@14: } Chris@14: // @todo Fix \Drupal\block\BlockAccessControlHandler::mergeCacheabilityFromConditions() in https://www.drupal.org/node/2867881 Chris@14: if (static::$entityTypeId === 'block') { Chris@14: $expected_cookie_403_cacheability->setCacheTags(str_replace('user:2', 'user:0', $expected_cookie_403_cacheability->getCacheTags())); Chris@14: } Chris@17: $this->assertResourceErrorResponse(403, FALSE, $response, $expected_cookie_403_cacheability->getCacheTags(), $expected_cookie_403_cacheability->getCacheContexts(), 'MISS', FALSE); Chris@14: } Chris@14: else { Chris@14: $this->assertResourceErrorResponse(403, FALSE, $response); Chris@14: } Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: protected function assertAuthenticationEdgeCases($method, Url $url, array $request_options) { Chris@0: // X-CSRF-Token request header is unnecessary for safe and side effect-free Chris@0: // HTTP methods. No need for additional assertions. Chris@0: // @see https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html Chris@0: if (in_array($method, ['HEAD', 'GET', 'OPTIONS', 'TRACE'])) { Chris@0: return; Chris@0: } Chris@0: Chris@0: unset($request_options[RequestOptions::HEADERS]['X-CSRF-Token']); Chris@0: Chris@0: // DX: 403 when missing X-CSRF-Token request header. Chris@0: $response = $this->request($method, $url, $request_options); Chris@0: $this->assertResourceErrorResponse(403, 'X-CSRF-Token request header is missing', $response); Chris@0: Chris@0: $request_options[RequestOptions::HEADERS]['X-CSRF-Token'] = 'this-is-not-the-token-you-are-looking-for'; Chris@0: Chris@0: // DX: 403 when invalid X-CSRF-Token request header. Chris@0: $response = $this->request($method, $url, $request_options); Chris@0: $this->assertResourceErrorResponse(403, 'X-CSRF-Token request header is invalid', $response); Chris@0: Chris@0: $request_options[RequestOptions::HEADERS]['X-CSRF-Token'] = $this->csrfToken; Chris@0: } Chris@0: Chris@0: }