3 namespace Drupal\Tests\basic_auth\Functional;
5 use Drupal\Component\Utility\SafeMarkup;
7 use Drupal\Tests\basic_auth\Traits\BasicAuthTestTrait;
8 use Drupal\language\Entity\ConfigurableLanguage;
9 use Drupal\Tests\BrowserTestBase;
12 * Tests for BasicAuth authentication provider.
16 class BasicAuthTest extends BrowserTestBase {
18 use BasicAuthTestTrait;
21 * Modules installed for all tests.
25 public static $modules = ['basic_auth', 'router_test', 'locale', 'basic_auth_test'];
28 * Test http basic authentication.
30 public function testBasicAuth() {
31 // Enable page caching.
32 $config = $this->config('system.performance');
33 $config->set('cache.page.max_age', 300);
36 $account = $this->drupalCreateUser();
37 $url = Url::fromRoute('router_test.11');
39 $this->basicAuthGet($url, $account->getUsername(), $account->pass_raw);
40 $this->assertText($account->getUsername(), 'Account name is displayed.');
41 $this->assertResponse('200', 'HTTP response is OK');
42 $this->mink->resetSessions();
43 $this->assertFalse($this->drupalGetHeader('X-Drupal-Cache'));
44 $this->assertIdentical(strpos($this->drupalGetHeader('Cache-Control'), 'public'), FALSE, 'Cache-Control is not set to public');
46 $this->basicAuthGet($url, $account->getUsername(), $this->randomMachineName());
47 $this->assertNoText($account->getUsername(), 'Bad basic auth credentials do not authenticate the user.');
48 $this->assertResponse('403', 'Access is not granted.');
49 $this->mink->resetSessions();
51 $this->drupalGet($url);
52 $this->assertEqual($this->drupalGetHeader('WWW-Authenticate'), SafeMarkup::format('Basic realm="@realm"', ['@realm' => \Drupal::config('system.site')->get('name')]));
53 $this->assertResponse('401', 'Not authenticated on the route that allows only basic_auth. Prompt to authenticate received.');
55 $this->drupalGet('admin');
56 $this->assertResponse('403', 'No authentication prompt for routes not explicitly defining authentication providers.');
58 $account = $this->drupalCreateUser(['access administration pages']);
60 $this->basicAuthGet(Url::fromRoute('system.admin'), $account->getUsername(), $account->pass_raw);
61 $this->assertNoLink('Log out', 'User is not logged in');
62 $this->assertResponse('403', 'No basic authentication for routes not explicitly defining authentication providers.');
63 $this->mink->resetSessions();
65 // Ensure that pages already in the page cache aren't returned from page
66 // cache if basic auth credentials are provided.
67 $url = Url::fromRoute('router_test.10');
68 $this->drupalGet($url);
69 $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'MISS');
70 $this->basicAuthGet($url, $account->getUsername(), $account->pass_raw);
71 $this->assertFalse($this->drupalGetHeader('X-Drupal-Cache'));
72 $this->assertIdentical(strpos($this->drupalGetHeader('Cache-Control'), 'public'), FALSE, 'No page cache response when requesting a cached page with basic auth credentials.');
76 * Test the global login flood control.
78 public function testGlobalLoginFloodControl() {
79 $this->config('user.flood')
81 // Set a high per-user limit out so that it is not relevant in the test.
82 ->set('user_limit', 4000)
85 $user = $this->drupalCreateUser([]);
86 $incorrect_user = clone $user;
87 $incorrect_user->pass_raw .= 'incorrect';
88 $url = Url::fromRoute('router_test.11');
90 // Try 2 failed logins.
91 for ($i = 0; $i < 2; $i++) {
92 $this->basicAuthGet($url, $incorrect_user->getUsername(), $incorrect_user->pass_raw);
95 // IP limit has reached to its limit. Even valid user credentials will fail.
96 $this->basicAuthGet($url, $user->getUsername(), $user->pass_raw);
97 $this->assertResponse('403', 'Access is blocked because of IP based flood prevention.');
101 * Test the per-user login flood control.
103 public function testPerUserLoginFloodControl() {
104 $this->config('user.flood')
105 // Set a high global limit out so that it is not relevant in the test.
106 ->set('ip_limit', 4000)
107 ->set('user_limit', 2)
110 $user = $this->drupalCreateUser([]);
111 $incorrect_user = clone $user;
112 $incorrect_user->pass_raw .= 'incorrect';
113 $user2 = $this->drupalCreateUser([]);
114 $url = Url::fromRoute('router_test.11');
116 // Try a failed login.
117 $this->basicAuthGet($url, $incorrect_user->getUsername(), $incorrect_user->pass_raw);
119 // A successful login will reset the per-user flood control count.
120 $this->basicAuthGet($url, $user->getUsername(), $user->pass_raw);
121 $this->assertResponse('200', 'Per user flood prevention gets reset on a successful login.');
123 // Try 2 failed logins for a user. They will trigger flood control.
124 for ($i = 0; $i < 2; $i++) {
125 $this->basicAuthGet($url, $incorrect_user->getUsername(), $incorrect_user->pass_raw);
128 // Now the user account is blocked.
129 $this->basicAuthGet($url, $user->getUsername(), $user->pass_raw);
130 $this->assertResponse('403', 'The user account is blocked due to per user flood prevention.');
132 // Try one successful attempt for a different user, it should not trigger
133 // any flood control.
134 $this->basicAuthGet($url, $user2->getUsername(), $user2->pass_raw);
135 $this->assertResponse('200', 'Per user flood prevention does not block access for other users.');
139 * Tests compatibility with locale/UI translation.
141 public function testLocale() {
142 ConfigurableLanguage::createFromLangcode('de')->save();
143 $this->config('system.site')->set('default_langcode', 'de')->save();
145 $account = $this->drupalCreateUser();
146 $url = Url::fromRoute('router_test.11');
148 $this->basicAuthGet($url, $account->getUsername(), $account->pass_raw);
149 $this->assertText($account->getUsername(), 'Account name is displayed.');
150 $this->assertResponse('200', 'HTTP response is OK');
154 * Tests if a comprehensive message is displayed when the route is denied.
156 public function testUnauthorizedErrorMessage() {
157 $account = $this->drupalCreateUser();
158 $url = Url::fromRoute('router_test.11');
160 // Case when no credentials are passed.
161 $this->drupalGet($url);
162 $this->assertResponse('401', 'The user is blocked when no credentials are passed.');
163 $this->assertNoText('Exception', "No raw exception is displayed on the page.");
164 $this->assertText('Please log in to access this page.', "A user friendly access unauthorized message is displayed.");
166 // Case when empty credentials are passed.
167 $this->basicAuthGet($url, NULL, NULL);
168 $this->assertResponse('403', 'The user is blocked when empty credentials are passed.');
169 $this->assertText('Access denied', "A user friendly access denied message is displayed");
171 // Case when wrong credentials are passed.
172 $this->basicAuthGet($url, $account->getUsername(), $this->randomMachineName());
173 $this->assertResponse('403', 'The user is blocked when wrong credentials are passed.');
174 $this->assertText('Access denied', "A user friendly access denied message is displayed");
176 // Case when correct credentials but hasn't access to the route.
177 $url = Url::fromRoute('router_test.15');
178 $this->basicAuthGet($url, $account->getUsername(), $account->pass_raw);
179 $this->assertResponse('403', 'The used authentication method is not allowed on this route.');
180 $this->assertText('Access denied', "A user friendly access denied message is displayed");
184 * Tests if the controller is called before authentication.
186 * @see https://www.drupal.org/node/2817727
188 public function testControllerNotCalledBeforeAuth() {
189 $this->drupalGet('/basic_auth_test/state/modify');
190 $this->assertResponse(401);
191 $this->drupalGet('/basic_auth_test/state/read');
192 $this->assertResponse(200);
193 $this->assertRaw('nope');
195 $account = $this->drupalCreateUser();
196 $this->basicAuthGet('/basic_auth_test/state/modify', $account->getUsername(), $account->pass_raw);
197 $this->assertResponse(200);
198 $this->assertRaw('Done');
200 $this->mink->resetSessions();
201 $this->drupalGet('/basic_auth_test/state/read');
202 $this->assertResponse(200);
203 $this->assertRaw('yep');