diff --git a/core/modules/user/tests/src/Functional/UserLoginHttpTest.php b/core/modules/user/tests/src/Functional/UserLoginHttpTest.php index 5e5d8a5..cd143e3 100644 --- a/core/modules/user/tests/src/Functional/UserLoginHttpTest.php +++ b/core/modules/user/tests/src/Functional/UserLoginHttpTest.php @@ -2,11 +2,14 @@ namespace Drupal\Tests\user\Functional; +use Drupal\Core\Flood\DatabaseBackend; use Drupal\Core\Url; use Drupal\Tests\BrowserTestBase; use Drupal\user\Controller\UserAuthenticationController; use GuzzleHttp\Cookie\CookieJar; +use Psr\Http\Message\ResponseInterface; use Symfony\Component\Serializer\Encoder\JsonEncoder; +use Symfony\Component\Serializer\Encoder\XmlEncoder; use Symfony\Component\Serializer\Serializer; /** @@ -35,10 +38,9 @@ class UserLoginHttpTest extends BrowserTestBase { */ protected function setUp() { parent::setUp(); - $this->cookies = new CookieJar(); - - $this->serializer = new Serializer([], [new JsonEncoder()]); + $encoders = [new JsonEncoder(), new XmlEncoder()]; + $this->serializer = new Serializer([], $encoders); } /** @@ -82,106 +84,118 @@ protected function loginRequest($name, $pass, $format) { * Tests user session life cycle. */ public function testLogin() { - $account = $this->drupalCreateUser(); - $name = $account->getUsername(); - $pass = $account->passRaw; - $client = \Drupal::httpClient(); - - $formats = ['json']; - foreach ($formats as $format) { - - $user_login_status_url = Url::fromRoute('user.login_status.http'); - $user_login_status_url->setRouteParameter('_format', $format); - $user_login_status_url->setAbsolute(); - - $result = $client->post($user_login_status_url->toString()); - $this->assertEquals(200, $result->getStatusCode()); - $this->assertEquals(UserAuthenticationController::LOGGED_OUT, (string) $result->getBody()); - - // Flooded. - \Drupal::configFactory()->getEditable('user.flood') - ->set('user_limit', 3) - ->save(); - - $result = $this->loginRequest($name, 'wrong-pass', $format); - $this->assertEquals(400, $result->getStatusCode()); - $this->assertEquals('{"message":"Sorry, unrecognized username or password."}', (string) $result->getBody()); - - $result = $this->loginRequest($name, 'wrong-pass', $format); - $this->assertEquals(400, $result->getStatusCode()); - $this->assertEquals('{"message":"Sorry, unrecognized username or password."}', (string) $result->getBody()); - - $result = $this->loginRequest($name, 'wrong-pass', $format); - $this->assertEquals(400, $result->getStatusCode()); - $this->assertEquals('{"message":"Sorry, unrecognized username or password."}', (string) $result->getBody()); - - $result = $this->loginRequest($name, 'wrong-pass', $format); - $this->assertEquals(400, $result->getStatusCode()); - $this->assertEquals('{"message":"Blocked."}', (string) $result->getBody()); - - // After testing the flood control we can increase the limit. - \Drupal::configFactory()->getEditable('user.flood') - ->set('user_limit', 100) - ->save(); - - $result = $this->loginRequest(NULL, NULL, $format); - $this->assertEquals(400, $result->getStatusCode()); - $this->assertEquals('{"message":"Missing credentials."}', (string) $result->getBody()); - - $result = $this->loginRequest(NULL, $pass, $format); - $this->assertEquals(400, $result->getStatusCode()); - $this->assertEquals('{"message":"Missing credentials.name."}', (string) $result->getBody()); - - $result = $this->loginRequest($name, NULL, $format); - $this->assertEquals(400, $result->getStatusCode()); - $this->assertEquals('{"message":"Missing credentials.pass."}', (string) $result->getBody()); - - // Blocked. - $account - ->block() - ->save(); - - $result = $this->loginRequest($name, $pass, $format); - $this->assertEquals(400, $result->getStatusCode()); - $this->assertEquals('{"message":"The user has not been activated or is blocked."}', (string) $result->getBody()); - - $account - ->activate() - ->save(); - - $result = $this->loginRequest($name, 'garbage', $format); - $this->assertEquals(400, $result->getStatusCode()); - $this->assertEquals('{"message":"Sorry, unrecognized username or password."}', (string) $result->getBody()); - - $result = $this->loginRequest('garbage', $pass, $format); - $this->assertEquals(400, $result->getStatusCode()); - $this->assertEquals('{"message":"Sorry, unrecognized username or password."}', (string) $result->getBody()); - - $result = $this->loginRequest($name, $pass, $format); - $this->assertEquals(200, $result->getStatusCode()); - $result_data = $this->decode($result->getBody(), $format); - $this->assertEquals($name, $result_data['current_user']['name']); - - $result = $client->post($user_login_status_url->toString(), ['cookies' => $this->cookies]); - $this->assertEquals(200, $result->getStatusCode()); - $this->assertEquals(UserAuthenticationController::LOGGED_IN, (string) $result->getBody()); - - $user_logout_url = Url::fromRoute('user.logout.http')->setRouteParameter('_format', $format)->setAbsolute(); - $result = $client->post($user_logout_url->toString(), [ - 'headers' => [ - 'Accept' => "application/$format", - ], - 'http_errors' => FALSE, - 'cookies' => $this->cookies, - ]); - $this->assertEquals(204, $result->getStatusCode()); - - $result = $client->post($user_login_status_url->toString(), ['cookies' => $this->cookies]); - $this->assertEquals(200, $result->getStatusCode()); - $this->assertEquals(UserAuthenticationController::LOGGED_OUT, (string) $result->getBody()); + $serialization_enabled_options = [FALSE, TRUE]; + foreach ($serialization_enabled_options as $serialization_enabled_option) { + if ($serialization_enabled_option) { + /** @var \Drupal\Core\Extension\ModuleInstaller $module_installer */ + $module_installer = \Drupal::service('module_installer'); + $module_installer->install(['serialization']); + $formats = ['json', 'xml']; + } + else { + // Without the serialization module only JSON is supported. + $formats = ['json']; + } + foreach ($formats as $format) { + // Create new user for each iteration to reset flood. + $account = $this->drupalCreateUser(); + $name = $account->getUsername(); + $pass = $account->passRaw; + + $user_login_status_url = Url::fromRoute('user.login_status.http'); + $user_login_status_url->setRouteParameter('_format', $format); + $user_login_status_url->setAbsolute(); + + $response = $client->post($user_login_status_url->toString()); + $this->assertEquals(200, $response->getStatusCode()); + $this->assertEquals(UserAuthenticationController::LOGGED_OUT, (string) $response->getBody()); + + // Flooded. + \Drupal::configFactory()->getEditable('user.flood') + ->set('user_limit', 3) + ->save(); + + $response = $this->loginRequest($name, 'wrong-pass', $format); + $this->assertEquals(400, $response->getStatusCode()); + $this->assertEquals('Sorry, unrecognized username or password.', $this->getResultValue($response, 'message', $format)); + + $response = $this->loginRequest($name, 'wrong-pass', $format); + $this->assertEquals(400, $response->getStatusCode()); + $this->assertEquals('Sorry, unrecognized username or password.', $this->getResultValue($response, 'message', $format)); + + $response = $this->loginRequest($name, 'wrong-pass', $format); + $this->assertEquals(400, $response->getStatusCode()); + $this->assertEquals('Sorry, unrecognized username or password.', $this->getResultValue($response, 'message', $format)); + + $response = $this->loginRequest($name, 'wrong-pass', $format); + $this->assertEquals(400, $response->getStatusCode()); + $this->assertEquals('Blocked.', $this->getResultValue($response, 'message', $format)); + + // After testing the flood control we can increase the limit. + \Drupal::configFactory()->getEditable('user.flood') + ->set('user_limit', 100) + ->save(); + + $response = $this->loginRequest(NULL, NULL, $format); + $this->assertEquals(400, $response->getStatusCode()); + $this->assertEquals('Missing credentials.', $this->getResultValue($response, 'message', $format)); + + $response = $this->loginRequest(NULL, $pass, $format); + $this->assertEquals(400, $response->getStatusCode()); + $this->assertEquals('Missing credentials.name.', $this->getResultValue($response, 'message', $format)); + + $response = $this->loginRequest($name, NULL, $format); + $this->assertEquals(400, $response->getStatusCode()); + $this->assertEquals('Missing credentials.pass.', $this->getResultValue($response, 'message', $format)); + + // Blocked. + $account + ->block() + ->save(); + + $response = $this->loginRequest($name, $pass, $format); + $this->assertEquals(400, $response->getStatusCode()); + $this->assertEquals('The user has not been activated or is blocked.', $this->getResultValue($response, 'message', $format)); + + $account + ->activate() + ->save(); + + $response = $this->loginRequest($name, 'garbage', $format); + $this->assertEquals(400, $response->getStatusCode()); + $this->assertEquals('Sorry, unrecognized username or password.', $this->getResultValue($response, 'message', $format)); + + $response = $this->loginRequest('garbage', $pass, $format); + $this->assertEquals(400, $response->getStatusCode()); + $this->assertEquals('Sorry, unrecognized username or password.', $this->getResultValue($response, 'message', $format)); + + $response = $this->loginRequest($name, $pass, $format); + $this->assertEquals(200, $response->getStatusCode()); + $result_data = $this->decode($response->getBody(), $format); + $this->assertEquals($name, $result_data['current_user']['name']); + + $response = $client->post($user_login_status_url->toString(), ['cookies' => $this->cookies]); + $this->assertEquals(200, $response->getStatusCode()); + $this->assertEquals(UserAuthenticationController::LOGGED_IN, (string) $response->getBody()); + + $user_logout_url = Url::fromRoute('user.logout.http')->setRouteParameter('_format', $format)->setAbsolute(); + $response = $client->post($user_logout_url->toString(), [ + 'headers' => [ + 'Accept' => "application/$format", + ], + 'http_errors' => FALSE, + 'cookies' => $this->cookies, + ]); + $this->assertEquals(204, $response->getStatusCode()); + + $response = $client->post($user_login_status_url->toString(), ['cookies' => $this->cookies]); + $this->assertEquals(200, $response->getStatusCode()); + $this->assertEquals(UserAuthenticationController::LOGGED_OUT, (string) $response->getBody()); + + $this->resetFlood(); + } } - } /** @@ -191,6 +205,9 @@ public function testLogin() { * The data to be encoded. * @param string $format * The format to be encoded into. + * + * @return mixed + * Encoded data. */ protected function encode($data, $format) { return $this->serializer->encode($data, $format); @@ -200,12 +217,45 @@ protected function encode($data, $format) { * Decodes data for a request from a given format. * * @param mixed $data - * The data to be encoded. + * The data to be decoded. * @param string $format - * The format to be encoded into. + * The format to be decoded from. + * + * @return mixed + * Decoded data. */ protected function decode($data, $format) { return $this->serializer->decode($data, $format); } + /** + * Get a value for a given key from the response. + * + * @param \Psr\Http\Message\ResponseInterface $response + * The response object. + * @param string $key + * The key for the value. + * @param string $format + * The encoded format. + * + * @return mixed + * The value for the key. + */ + protected function getResultValue(ResponseInterface $response, $key, $format) { + $decoded = $this->decode((string) $response->getBody(), $format); + if (is_array($decoded)) { + return $decoded[$key]; + } + else { + return $decoded->{$key}; + } + } + + /** + * Reset all flood entries. + */ + protected function resetFlood() { + \Drupal::database()->delete(DatabaseBackend::TABLE_NAME)->execute(); + } + }