CaptchaAdminTest.php 16.4 KB
Newer Older
1
2
<?php

3
namespace Drupal\Tests\captcha\Functional;
4

5
use Drupal\captcha\Entity\CaptchaPoint;
6
use Drupal\Core\Url;
7

8
9
10
11
12
/**
 * Tests CAPTCHA admin settings.
 *
 * @group captcha
 */
13
class CaptchaAdminTest extends CaptchaWebTestBase {
14

15
16
17
  /**
   * Test access to the admin pages.
   */
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
  public function testAdminAccess() {
    $this->drupalLogin($this->normalUser);
    $this->drupalGet(self::CAPTCHA_ADMIN_PATH);
    $this->assertText(t('Access denied'), 'Normal users should not be able to access the CAPTCHA admin pages', 'CAPTCHA');

    $this->drupalLogin($this->adminUser);
    $this->drupalGet(self::CAPTCHA_ADMIN_PATH);
    $this->assertNoText(t('Access denied'), 'Admin users should be able to access the CAPTCHA admin pages', 'CAPTCHA');
  }

  /**
   * Test the CAPTCHA point setting getter/setter.
   */
  public function testCaptchaPointSettingGetterAndSetter() {
    $comment_form_id = self::COMMENT_FORM_ID;
33
    captcha_set_form_id_setting($comment_form_id, 'test');
34
35
36
    /* @var CaptchaPoint $result */
    $result = captcha_get_form_id_setting($comment_form_id);
    $this->assertNotNull($result, 'CAPTCHA exists', 'CAPTCHA');
37
    $this->assertEqual($result->getCaptchaType(), 'test', 'CAPTCHA type: default', 'CAPTCHA');
38
39
    $result = captcha_get_form_id_setting($comment_form_id, TRUE);
    $this->assertNotNull($result, 'CAPTCHA exists', 'CAPTCHA');
40
    $this->assertEqual($result, 'test', 'Setting and symbolic getting CAPTCHA point: "test"', 'CAPTCHA');
41

42
    // Set to 'default'.
43
    captcha_set_form_id_setting($comment_form_id, 'default');
44
45
46
    $this->config('captcha.settings')
      ->set('default_challenge', 'foo/bar')
      ->save();
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
    $result = captcha_get_form_id_setting($comment_form_id);
    $this->assertNotNull($result, 'CAPTCHA exists', 'CAPTCHA');
    $this->assertEqual($result->getCaptchaType(), 'foo/bar', 'Setting and getting CAPTCHA point: default', 'CAPTCHA');
    $result = captcha_get_form_id_setting($comment_form_id, TRUE);
    $this->assertNotNull($result, 'Setting and symbolic getting CAPTCHA point: "default"', 'CAPTCHA');
    $this->assertEqual($result, 'foo/bar', 'Setting and symbolic getting CAPTCHA point: default', 'CAPTCHA');

    // Set to 'baz/boo'.
    captcha_set_form_id_setting($comment_form_id, 'baz/boo');
    $result = captcha_get_form_id_setting($comment_form_id);
    $this->assertNotNull($result, 'CAPTCHA exists', 'CAPTCHA');
    $this->assertEqual($result->getCaptchaType(), 'baz/boo', 'Setting and getting CAPTCHA point: baz/boo', 'CAPTCHA');
    $result = captcha_get_form_id_setting($comment_form_id, TRUE);
    $this->assertEqual($result, 'baz/boo', 'Setting and symbolic getting CAPTCHA point: "baz/boo"', 'CAPTCHA');

    // Set to NULL (which should delete the CAPTCHA point setting entry).
    captcha_set_form_id_setting($comment_form_id, NULL);
    $result = captcha_get_form_id_setting($comment_form_id);
    $this->assertNotNull($result, 'CAPTCHA exists', 'CAPTCHA');
    $this->assertEqual($result->getCaptchaType(), 'foo/bar', 'Setting and getting CAPTCHA point: NULL', 'CAPTCHA');
    $result = captcha_get_form_id_setting($comment_form_id, TRUE);
    $this->assertNotNull($result, 'CAPTCHA exists', 'CAPTCHA');

    // Set with object.
    $captcha_type = 'baba/fofo';
    captcha_set_form_id_setting($comment_form_id, $captcha_type);

    $result = captcha_get_form_id_setting($comment_form_id);
    $this->assertNotNull($result, 'Setting and getting CAPTCHA point: baba/fofo', 'CAPTCHA');
    // $this->assertEqual($result->module, 'baba', 'Setting and getting
77
    // CAPTCHA point: baba/fofo', 'CAPTCHA');.
78
79
80
81
    $this->assertEqual($result->getCaptchaType(), 'baba/fofo', 'Setting and getting CAPTCHA point: baba/fofo', 'CAPTCHA');
    $result = captcha_get_form_id_setting($comment_form_id, TRUE);
    $this->assertEqual($result, 'baba/fofo', 'Setting and symbolic getting CAPTCHA point: "baba/fofo"', 'CAPTCHA');
  }
82

83
84
85
86
87
88
89
  /**
   * Helper function for checking CAPTCHA setting of a form.
   *
   * @param string $form_id
   *   The form_id of the form to investigate.
   * @param string $challenge_type
   *   What the challenge type should be:
90
   *   NULL, 'default' or something like 'captcha/Math'.
91
92
93
94
95
   */
  protected function assertCaptchaSetting($form_id, $challenge_type) {
    $result = captcha_get_form_id_setting(self::COMMENT_FORM_ID, TRUE);
    $this->assertEqual($result, $challenge_type,
      t('Check CAPTCHA setting for form: expected: @expected, received: @received.',
96
        [
97
98
          '@expected' => var_export($challenge_type, TRUE),
          '@received' => var_export($result, TRUE),
99
100
        ]),
      'CAPTCHA');
101
102
103
104
105
106
107
108
109
  }

  /**
   * Testing of the CAPTCHA administration links.
   */
  public function testCaptchaAdminLinks() {
    $this->drupalLogin($this->adminUser);

    // Enable CAPTCHA administration links.
110
    $edit = [
111
      'administration_mode' => TRUE,
112
    ];
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133

    $this->drupalPostForm(self::CAPTCHA_ADMIN_PATH, $edit, t('Save configuration'));

    // Create a node with comments enabled.
    $node = $this->drupalCreateNode();

    // Go to node page.
    $this->drupalGet('node/' . $node->id());

    // Click the add new comment link.
    $this->clickLink(t('Add new comment'));
    $add_comment_url = $this->getUrl();

    // Remove fragment part from comment URL to avoid
    // problems with later asserts.
    $add_comment_url = strtok($add_comment_url, "#");

    // Click the CAPTCHA admin link to enable a challenge.
    $this->clickLink(t('Place a CAPTCHA here for untrusted users.'));

    // Enable Math CAPTCHA.
134
    $edit = ['captchaType' => 'captcha/Math'];
135
136
    $this->drupalPostForm($this->getUrl(), $edit, t('Save'));
    // Check if returned to original comment form.
137
    $this->assertUrl($add_comment_url, [],
138
139
140
141
      'After setting CAPTCHA with CAPTCHA admin links: should return to original form.', 'CAPTCHA');

    // Check if CAPTCHA was successfully enabled
    // (on CAPTCHA admin links fieldset).
142
    $this->assertText(t('CAPTCHA: challenge "@type" enabled', ['@type' => $edit['captchaType']]),
143
144
145
146
147
148
149
150
151
      'Enable a challenge through the CAPTCHA admin links', 'CAPTCHA');

    // Check if CAPTCHA was successfully enabled (through API).
    $this->assertCaptchaSetting(self::COMMENT_FORM_ID, 'captcha/Math');

    // Edit challenge type through CAPTCHA admin links.
    $this->clickLink(t('change'));

    // Enable Math CAPTCHA.
152
    $edit = ['captchaType' => 'default'];
153
154
155
156
157
158
159
160
161
162
163
    $this->drupalPostForm($this->getUrl(), $edit, t('Save'));

    // Check if returned to original comment form.
    $this->assertEqual($add_comment_url, $this->getUrl(),
      'After editing challenge type CAPTCHA admin links: should return to original form.', 'CAPTCHA');

    // Check if CAPTCHA was successfully changed
    // (on CAPTCHA admin links fieldset).
    // This is actually the same as the previous setting because
    // the captcha/Math is the default for the default challenge.
    // TODO Make sure the edit is a real change.
164
    $this->assertText(t('CAPTCHA: challenge "@type" enabled', ['@type' => $edit['captchaType']]),
165
166
167
168
169
      'Enable a challenge through the CAPTCHA admin links', 'CAPTCHA');
    // Check if CAPTCHA was successfully edited (through API).
    $this->assertCaptchaSetting(self::COMMENT_FORM_ID, 'default');

    // Disable challenge through CAPTCHA admin links.
170
171
    $this->drupalGet(Url::fromRoute('entity.captcha_point.disable', ['captcha_point' => self::COMMENT_FORM_ID]));
    $this->drupalPostForm(NULL, [], t('Disable'));
172

173
174
175
176
    // Check if returned to captcha point list.
    global $base_url;
    $this->assertEqual($base_url . '/admin/config/people/captcha/captcha-points', $this->getUrl(),
      'After disabling challenge in CAPTCHA admin: should return to captcha point list.', 'CAPTCHA');
177
178
179

    // Check if CAPTCHA was successfully disabled
    // (on CAPTCHA admin links fieldset).
180
    $this->assertRaw(t('Captcha point %form_id has been disabled.', ['%form_id' => self::COMMENT_FORM_ID]),
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
      'Disable challenge through the CAPTCHA admin links', 'CAPTCHA');
  }

  /**
   * Test untrusted user posting.
   */
  public function testUntrustedUserPosting() {
    // Set CAPTCHA on comment form.
    captcha_set_form_id_setting(self::COMMENT_FORM_ID, 'captcha/Math');

    // Create a node with comments enabled.
    $node = $this->drupalCreateNode();

    // Log in as normal (untrusted) user.
    $this->drupalLogin($this->normalUser);

    // Go to node page and click the "add comment" link.
    $this->drupalGet('node/' . $node->id());
    $this->clickLink(t('Add new comment'));
    $add_comment_url = $this->getUrl();

    // Check if CAPTCHA is visible on form.
    $this->assertCaptchaPresence(TRUE);
    // Try to post a comment with wrong answer.
    $edit = $this->getCommentFormValues();
    $edit['captcha_response'] = 'xx';
    $this->drupalPostForm($add_comment_url, $edit, t('Preview'));
    $this->assertText(self::CAPTCHA_WRONG_RESPONSE_ERROR_MESSAGE,
      'wrong CAPTCHA should block form submission.', 'CAPTCHA');
  }

  /**
   * Test XSS vulnerability on CAPTCHA description.
   */
  public function testXssOnCaptchaDescription() {
    // Set CAPTCHA on user register form.
    captcha_set_form_id_setting('user_register', 'captcha/Math');

219
    // Put JavaScript snippet in CAPTCHA description.
220
221
    $this->drupalLogin($this->adminUser);
    $xss = '<script type="text/javascript">alert("xss")</script>';
222
    $edit = ['description' => $xss];
223
224
    $this->drupalPostForm(self::CAPTCHA_ADMIN_PATH, $edit, 'Save configuration');

225
    // Visit user register form and check if JavaScript snippet is there.
226
227
    $this->drupalLogout();
    $this->drupalGet('user/register');
228
    $this->assertNoRaw($xss, 'JavaScript should not be allowed in CAPTCHA description.', 'CAPTCHA');
229
230
231
232
233
234
235
236
237
238
239
  }

  /**
   * Test the CAPTCHA placement clearing.
   */
  public function testCaptchaPlacementCacheClearing() {
    // Set CAPTCHA on user register form.
    captcha_set_form_id_setting('user_register_form', 'captcha/Math');
    // Visit user register form to fill the CAPTCHA placement cache.
    $this->drupalGet('user/register');
    // Check if there is CAPTCHA placement cache.
240
241
    $placement_map = $this->container->get('cache.default')
      ->get('captcha_placement_map_cache');
242
243
244
    $this->assertNotNull($placement_map, 'CAPTCHA placement cache should be set.');
    // Clear the cache.
    $this->drupalLogin($this->adminUser);
245
    $this->drupalPostForm(self::CAPTCHA_ADMIN_PATH, [], t('Clear the CAPTCHA placement cache'));
246
    // Check that the placement cache is unset.
247
248
    $placement_map = $this->container->get('cache.default')
      ->get('captcha_placement_map_cache');
249
250
251
252
253
254
255
256
257
258
    $this->assertFalse($placement_map, 'CAPTCHA placement cache should be unset after cache clear.');
  }

  /**
   * Helper function to get CAPTCHA point setting straight from the database.
   *
   * @param string $form_id
   *   Form machine ID.
   *
   * @return \Drupal\captcha\Entity\CaptchaPoint
259
   *   CaptchaPoint with mysql query result.
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
   */
  protected function getCaptchaPointSettingFromDatabase($form_id) {
    $ids = \Drupal::entityQuery('captcha_point')
      ->condition('formId', $form_id)
      ->execute();
    return $ids ? CaptchaPoint::load(reset($ids)) : NULL;
  }

  /**
   * Method for testing the CAPTCHA point administration.
   */
  public function testCaptchaPointAdministration() {
    // Generate CAPTCHA point data:
    // Drupal form ID should consist of lowercase alphanumerics and underscore).
    $captcha_point_form_id = 'form_' . strtolower($this->randomMachineName(32));
    // The Math CAPTCHA by the CAPTCHA module is always available,
    // so let's use it.
    $captcha_point_module = 'captcha';
    $captcha_point_type = 'Math';

    // Log in as admin.
    $this->drupalLogin($this->adminUser);
282
    $label = 'TEST';
283
284

    // Try and set CAPTCHA point without the #required label. Should fail.
285
    $form_values = [
286
287
      'formId' => $captcha_point_form_id,
      'captchaType' => $captcha_point_module . '/' . $captcha_point_type,
288
    ];
289
    $this->drupalPostForm(self::CAPTCHA_ADMIN_PATH . '/captcha-points/add', $form_values, t('Save'));
290
291
292
293
294
    $this->assertText(t('Form ID field is required.'));

    // Set CAPTCHA point through admin/user/captcha/captcha/captcha_point.
    $form_values['label'] = $label;
    $this->drupalPostForm(self::CAPTCHA_ADMIN_PATH . '/captcha-points/add', $form_values, t('Save'));
295
    $this->assertRaw(t('Captcha Point for %label form was created.', ['%label' => $captcha_point_form_id]));
296
297
298
299
300
301
302
303

    // Check in database.
    /* @var CaptchaPoint result */
    $result = $this->getCaptchaPointSettingFromDatabase($captcha_point_form_id);
    $this->assertEqual($result->captchaType, $captcha_point_module . '/' . $captcha_point_type,
      'Enabled CAPTCHA point should have module and type set');

    // Disable CAPTCHA point again.
304
305
    $this->drupalPostForm(self::CAPTCHA_ADMIN_PATH . '/captcha-points/' . $captcha_point_form_id . '/disable', [], t('Disable'));
    $this->assertRaw(t('Captcha point %label has been disabled.', ['%label' => $label]), 'Disabling of CAPTCHA point');
306
307
308
309
310

    // Check in database.
    $result = $this->getCaptchaPointSettingFromDatabase($captcha_point_form_id);

    // Set CAPTCHA point via admin/user/captcha/captcha/captcha_point/$form_id.
311
    $form_values = [
312
      'captchaType' => $captcha_point_module . '/' . $captcha_point_type,
313
    ];
314
    $this->drupalPostForm(self::CAPTCHA_ADMIN_PATH . '/captcha-points/' . $captcha_point_form_id, $form_values, t('Save'));
315
    $this->assertRaw(t('Captcha Point for %form_id form was updated.', ['%form_id' => $captcha_point_form_id]), 'Saving of CAPTCHA point settings');
316
317
318
319
320
321
322

    // Check in database.
    $result = $this->getCaptchaPointSettingFromDatabase($captcha_point_form_id);
    $this->assertEqual($result->captchaType, $captcha_point_module . '/' . $captcha_point_type,
      'Enabled CAPTCHA point should have module and type set');

    // Delete CAPTCHA point.
323
324
    $this->drupalPostForm(self::CAPTCHA_ADMIN_PATH . '/captcha-points/' . $captcha_point_form_id . '/delete', [], t('Delete'));
    $this->assertRaw(t('Captcha point %label has been deleted.', ['%label' => $label]),
325
      'Deleting of CAPTCHA point');
326
327
328
329
330
331
332
333
334
335
336
337
338

    $result = $this->getCaptchaPointSettingFromDatabase($captcha_point_form_id);
    $this->assertFalse($result, 'Deleted CAPTCHA point should be in database');
  }

  /**
   * Method for testing the CAPTCHA point administration.
   */
  public function testCaptchaPointAdministrationByNonAdmin() {
    // First add a CAPTCHA point (as admin).
    $captcha_point_form_id = 'form_' . strtolower($this->randomMachineName(32));
    $captcha_point_module = 'captcha';
    $captcha_point_type = 'Math';
339
    $label = 'TEST_2';
340
341
342

    $this->drupalLogin($this->adminUser);

343
    $form_values = [
344
      'label' => $label,
345
346
      'formId' => $captcha_point_form_id,
      'captchaType' => $captcha_point_module . '/' . $captcha_point_type,
347
    ];
348
    $this->drupalPostForm(self::CAPTCHA_ADMIN_PATH . '/captcha-points/add', $form_values, 'Save');
349
    $this->assertRaw(t('Captcha Point for %form_id form was created.', ['%form_id' => $captcha_point_form_id]));
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376

    // Switch from admin to non-admin.
    $this->drupalLogin($this->normalUser);

    // Try to set CAPTCHA point
    // through admin/user/captcha/captcha/captcha_point.
    $this->drupalGet(self::CAPTCHA_ADMIN_PATH . '/captcha-points');
    $this->assertText(t('You are not authorized to access this page.'),
      'Non admin should not be able to set a CAPTCHA point');

    // Try to disable the CAPTCHA point.
    $this->drupalGet(self::CAPTCHA_ADMIN_PATH . '/captcha-points/' . $captcha_point_form_id . '/disable');
    $this->assertText(t('You are not authorized to access this page.'),
      'Non admin should not be able to disable a CAPTCHA point');

    // Try to delete the CAPTCHA point.
    $this->drupalGet(self::CAPTCHA_ADMIN_PATH . '/captcha-points/' . $captcha_point_form_id . '/delete');
    $this->assertText(t('You are not authorized to access this page.'),
      'Non admin should not be able to delete a CAPTCHA point');

    // Switch from nonadmin to admin again.
    $this->drupalLogin($this->adminUser);

    // Check if original CAPTCHA point still exists in database.
    $result = $this->getCaptchaPointSettingFromDatabase($captcha_point_form_id);
    $this->assertEqual($result->captchaType, $captcha_point_module . '/' . $captcha_point_type, 'Enabled CAPTCHA point should have module and type set');

377
    // Delete captcha point.
378
379
    $this->drupalPostForm(self::CAPTCHA_ADMIN_PATH . '/captcha-points/' . $captcha_point_form_id . '/delete', [], 'Delete');
    $this->assertRaw(t('Captcha point %label has been deleted.', ['%label' => $label]), 'Disabling of CAPTCHA point');
380
  }
381

382
}