Projet

Général

Profil

Paste
Télécharger (43,8 ko) Statistiques
| Branche: | Révision:

root / drupal7 / sites / all / modules / captcha / captcha.test @ 41cc1b08

1
<?php
2

    
3
/**
4
 * @file
5
 * Tests for CAPTCHA module.
6
 */
7

    
8
// TODO: write test for CAPTCHAs on admin pages
9
// TODO: test for default challenge type
10
// TODO: test about placement (comment form, node forms, log in form, etc)
11
// TODO: test if captcha_cron does it work right
12
// TODO: test custom CAPTCHA validation stuff
13
// TODO: test if entry on status report (Already X blocked form submissions) works
14
// TODO: test space ignoring validation of image CAPTCHA
15

    
16
// TODO: refactor the 'comment_body[' . LANGUAGE_NONE . '][0][value]' stuff
17

    
18
// Some constants for better reuse.
19
define('CAPTCHA_WRONG_RESPONSE_ERROR_MESSAGE',
20
  'The answer you entered for the CAPTCHA was not correct.');
21

    
22
define('CAPTCHA_SESSION_REUSE_ATTACK_ERROR_MESSAGE',
23
  'CAPTCHA session reuse attack detected.');
24

    
25
define('CAPTCHA_UNKNOWN_CSID_ERROR_MESSAGE',
26
  'CAPTCHA validation error: unknown CAPTCHA session ID. Contact the site administrator if this problem persists.');
27

    
28

    
29

    
30
/**
31
 * Base class for CAPTCHA tests.
32
 *
33
 * Provides common setup stuff and various helper functions
34
 */
35
abstract class CaptchaBaseWebTestCase extends DrupalWebTestCase {
36

    
37
  /**
38
   * User with various administrative permissions.
39
   * @var Drupal user
40
   */
41
  protected $admin_user;
42

    
43
  /**
44
   * Normal visitor with limited permissions
45
   * @var Drupal user;
46
   */
47
  protected $normal_user;
48

    
49
  /**
50
   * Form ID of comment form on standard (page) node
51
   * @var string
52
   */
53
  const COMMENT_FORM_ID = 'comment_node_page_form';
54

    
55
  /**
56
   * Drupal path of the (general) CAPTCHA admin page
57
   */
58
  const CAPTCHA_ADMIN_PATH = 'admin/config/people/captcha';
59

    
60

    
61
  function setUp() {
62
    // Load two modules: the captcha module itself and the comment module for testing anonymous comments.
63
    parent::setUp('captcha', 'comment');
64
    module_load_include('inc', 'captcha');
65

    
66
    // Create a normal user.
67
    $permissions = array(
68
      'access comments', 'post comments', 'skip comment approval',
69
      'access content', 'create page content', 'edit own page content',
70
    );
71
    $this->normal_user = $this->drupalCreateUser($permissions);
72

    
73
    // Create an admin user.
74
    $permissions[] = 'administer CAPTCHA settings';
75
    $permissions[] = 'skip CAPTCHA';
76
    $permissions[] = 'administer permissions';
77
    $permissions[] = 'administer content types';
78
    $this->admin_user = $this->drupalCreateUser($permissions);
79

    
80
    // Put comments on page nodes on a separate page (default in D7: below post).
81
    variable_set('comment_form_location_page', COMMENT_FORM_SEPARATE_PAGE);
82

    
83
  }
84

    
85
  /**
86
   * Assert that the response is accepted:
87
   * no "unknown CSID" message, no "CSID reuse attack detection" message,
88
   * no "wrong answer" message.
89
   */
90
  protected function assertCaptchaResponseAccepted() {
91
    // There should be no error message about unknown CAPTCHA session ID.
92
    $this->assertNoText(t(CAPTCHA_UNKNOWN_CSID_ERROR_MESSAGE),
93
      'CAPTCHA response should be accepted (known CSID).',
94
      'CAPTCHA');
95
    // There should be no error message about CSID reuse attack.
96
    $this->assertNoText(t(CAPTCHA_SESSION_REUSE_ATTACK_ERROR_MESSAGE),
97
      'CAPTCHA response should be accepted (no CAPTCHA session reuse attack detection).',
98
      'CAPTCHA');
99
    // There should be no error message about wrong response.
100
    $this->assertNoText(t(CAPTCHA_WRONG_RESPONSE_ERROR_MESSAGE),
101
      'CAPTCHA response should be accepted (correct response).',
102
      'CAPTCHA');
103
  }
104

    
105
  /**
106
   * Assert that there is a CAPTCHA on the form or not.
107
   * @param bool $presence whether there should be a CAPTCHA or not.
108
   */
109
  protected function assertCaptchaPresence($presence) {
110
    if ($presence) {
111
      $this->assertText(_captcha_get_description(),
112
        'There should be a CAPTCHA on the form.', 'CAPTCHA');
113
    }
114
    else {
115
      $this->assertNoText(_captcha_get_description(),
116
        'There should be no CAPTCHA on the form.', 'CAPTCHA');
117
    }
118
  }
119

    
120
  /**
121
   * Helper function to create a node with comments enabled.
122
   *
123
   * @return
124
   *   Created node object.
125
   */
126
  protected function createNodeWithCommentsEnabled($type='page') {
127
    $node_settings = array(
128
      'type' => $type,
129
      'comment' => COMMENT_NODE_OPEN,
130
    );
131
    $node = $this->drupalCreateNode($node_settings);
132
    return $node;
133
  }
134

    
135
  /**
136
   * Helper function to generate a form values array for comment forms
137
   */
138
  protected function getCommentFormValues() {
139
    $edit = array(
140
      'subject' => 'comment_subject ' . $this->randomName(32),
141
      'comment_body[' . LANGUAGE_NONE . '][0][value]' => 'comment_body ' . $this->randomName(256),
142
    );
143
    return $edit;
144
  }
145

    
146
  /**
147
   * Helper function to generate a form values array for node forms
148
   */
149
  protected function getNodeFormValues() {
150
    $edit = array(
151
      'title' => 'node_title ' . $this->randomName(32),
152
      'body[' . LANGUAGE_NONE . '][0][value]' => 'node_body ' . $this->randomName(256),
153
    );
154
    return $edit;
155
  }
156

    
157

    
158
  /**
159
   * Get the CAPTCHA session id from the current form in the browser.
160
   */
161
  protected function getCaptchaSidFromForm() {
162
    $elements = $this->xpath('//input[@name="captcha_sid"]');
163
    $captcha_sid = (int) $elements[0]['value'];
164
    return $captcha_sid;
165
  }
166
  /**
167
   * Get the CAPTCHA token from the current form in the browser.
168
   */
169
  protected function getCaptchaTokenFromForm() {
170
    $elements = $this->xpath('//input[@name="captcha_token"]');
171
    $captcha_token = (int) $elements[0]['value'];
172
    return $captcha_token;
173
  }
174

    
175
  /**
176
   * Get the solution of the math CAPTCHA from the current form in the browser.
177
   */
178
  protected function getMathCaptchaSolutionFromForm() {
179
    // Get the math challenge.
180
    $elements = $this->xpath('//div[@class="form-item form-type-textfield form-item-captcha-response"]/span[@class="field-prefix"]');
181
    $challenge = (string) $elements[0];
182
    // Extract terms and operator from challenge.
183
    $matches = array();
184
    $ret = preg_match('/\\s*(\\d+)\\s*(-|\\+)\\s*(\\d+)\\s*=\\s*/', $challenge, $matches);
185
    // Solve the challenge
186
    $a = (int) $matches[1];
187
    $b = (int) $matches[3];
188
    $solution = $matches[2] == '-' ? $a - $b : $a + $b;
189
    return $solution;
190
  }
191

    
192
  /**
193
   * Helper function to allow comment posting for anonymous users.
194
   */
195
  protected function allowCommentPostingForAnonymousVisitors() {
196
    // Log in as admin.
197
    $this->drupalLogin($this->admin_user);
198
    // Post user permissions form
199
    $edit = array(
200
      '1[access comments]' => true,
201
      '1[post comments]' => true,
202
      '1[skip comment approval]' => true,
203
    );
204
    $this->drupalPost('admin/people/permissions', $edit, 'Save permissions');
205
    $this->assertText('The changes have been saved.');
206
    // Log admin out
207
    $this->drupalLogout();
208
  }
209

    
210
}
211

    
212

    
213

    
214
class CaptchaTestCase extends CaptchaBaseWebTestCase {
215

    
216
  public static function getInfo() {
217
    return array(
218
      'name' => t('General CAPTCHA functionality'),
219
      'description' => t('Testing of the basic CAPTCHA functionality.'),
220
      'group' => t('CAPTCHA'),
221
    );
222
  }
223

    
224
  /**
225
   * Testing the protection of the user log in form.
226
   */
227
  function testCaptchaOnLoginForm() {
228
    // Create user and test log in without CAPTCHA.
229
    $user = $this->drupalCreateUser();
230
    $this->drupalLogin($user);
231
    // Log out again.
232
    $this->drupalLogout();
233

    
234
    // Set a CAPTCHA on login form
235
    captcha_set_form_id_setting('user_login', 'captcha/Math');
236

    
237
    // Check if there is a CAPTCHA on the login form (look for the title).
238
    $this->drupalGet('user');
239
    $this->assertCaptchaPresence(TRUE);
240

    
241
    // Try to log in, which should fail.
242
    $edit = array(
243
      'name' => $user->name,
244
      'pass' => $user->pass_raw,
245
      'captcha_response' => '?',
246
    );
247
    $this->drupalPost('user', $edit, t('Log in'));
248
    // Check for error message.
249
    $this->assertText(t(CAPTCHA_WRONG_RESPONSE_ERROR_MESSAGE),
250
      'CAPTCHA should block user login form', 'CAPTCHA');
251

    
252
    // And make sure that user is not logged in: check for name and password fields on ?q=user
253
    $this->drupalGet('user');
254
    $this->assertField('name', t('Username field found.'), 'CAPTCHA');
255
    $this->assertField('pass', t('Password field found.'), 'CAPTCHA');
256

    
257
  }
258

    
259

    
260
  /**
261
   * Assert function for testing if comment posting works as it should.
262
   *
263
   * Creates node with comment writing enabled, tries to post comment
264
   * with given CAPTCHA response (caller should enable the desired
265
   * challenge on page node comment forms) and checks if the result is as expected.
266
   *
267
   * @param $captcha_response the response on the CAPTCHA
268
   * @param $should_pass boolean describing if the posting should pass or should be blocked
269
   * @param $message message to prefix to nested asserts
270
   */
271
  protected function assertCommentPosting($captcha_response, $should_pass, $message) {
272
    // Make sure comments on pages can be saved directely without preview.
273
    variable_set('comment_preview_page', DRUPAL_OPTIONAL);
274

    
275
    // Create a node with comments enabled.
276
    $node = $this->createNodeWithCommentsEnabled();
277

    
278
    // Post comment on node.
279
    $edit = $this->getCommentFormValues();
280
    $comment_subject = $edit['subject'];
281
    $comment_body = $edit['comment_body[' . LANGUAGE_NONE . '][0][value]'];
282
    $edit['captcha_response'] = $captcha_response;
283
    $this->drupalPost('comment/reply/' . $node->nid, $edit, t('Save'));
284

    
285
    if ($should_pass) {
286
      // There should be no error message.
287
      $this->assertCaptchaResponseAccepted();
288
      // Get node page and check that comment shows up.
289
      $this->drupalGet('node/' . $node->nid);
290
      $this->assertText($comment_subject, $message .' Comment should show up on node page.', 'CAPTCHA');
291
      $this->assertText($comment_body, $message . ' Comment should show up on node page.', 'CAPTCHA');
292
    }
293
    else {
294
      // Check for error message.
295
      $this->assertText(t(CAPTCHA_WRONG_RESPONSE_ERROR_MESSAGE), $message .' Comment submission should be blocked.', 'CAPTCHA');
296
      // Get node page and check that comment is not present.
297
      $this->drupalGet('node/' . $node->nid);
298
      $this->assertNoText($comment_subject, $message .' Comment should not show up on node page.', 'CAPTCHA');
299
      $this->assertNoText($comment_body, $message . ' Comment should not show up on node page.', 'CAPTCHA');
300
    }
301
  }
302

    
303
  /*
304
   * Testing the case sensistive/insensitive validation.
305
   */
306
  function testCaseInsensitiveValidation() {
307
    // Set Test CAPTCHA on comment form
308
    captcha_set_form_id_setting(self::COMMENT_FORM_ID, 'captcha/Test');
309

    
310
    // Log in as normal user.
311
    $this->drupalLogin($this->normal_user);
312

    
313
    // Test case sensitive posting.
314
    variable_set('captcha_default_validation', CAPTCHA_DEFAULT_VALIDATION_CASE_SENSITIVE);
315
    $this->assertCommentPosting('Test 123', TRUE, 'Case sensitive validation of right casing.');
316
    $this->assertCommentPosting('test 123', FALSE, 'Case sensitive validation of wrong casing.');
317
    $this->assertCommentPosting('TEST 123', FALSE, 'Case sensitive validation of wrong casing.');
318

    
319
    // Test case insensitive posting (the default)
320
    variable_set('captcha_default_validation', CAPTCHA_DEFAULT_VALIDATION_CASE_INSENSITIVE);
321
    $this->assertCommentPosting('Test 123', TRUE, 'Case insensitive validation of right casing.');
322
    $this->assertCommentPosting('test 123', TRUE, 'Case insensitive validation of wrong casing.');
323
    $this->assertCommentPosting('TEST 123', TRUE, 'Case insensitive validation of wrong casing.');
324

    
325
  }
326

    
327
  /**
328
   * Test if the CAPTCHA description is only shown if there are challenge widgets to show.
329
   * For example, when a comment is previewed with correct CAPTCHA answer,
330
   * a challenge is generated and added to the form but removed in the pre_render phase.
331
   * The CAPTCHA description should not show up either.
332
   *
333
   * \see testCaptchaSessionReuseOnNodeForms()
334
   */
335
  function testCaptchaDescriptionAfterCommentPreview() {
336
    // Set Test CAPTCHA on comment form.
337
    captcha_set_form_id_setting(self::COMMENT_FORM_ID, 'captcha/Test');
338

    
339
    // Log in as normal user.
340
    $this->drupalLogin($this->normal_user);
341

    
342
    // Create a node with comments enabled.
343
    $node = $this->createNodeWithCommentsEnabled();
344

    
345
    // Preview comment with correct CAPTCHA answer.
346
    $edit = $this->getCommentFormValues();
347
    $edit['captcha_response'] = 'Test 123';
348
    $this->drupalPost('comment/reply/' . $node->nid, $edit, t('Preview'));
349

    
350
    // Check that there is no CAPTCHA after preview.
351
    $this->assertCaptchaPresence(FALSE);
352
  }
353

    
354
  /**
355
   * Test if the CAPTCHA session ID is reused when previewing nodes:
356
   * node preview after correct response should not show CAPTCHA anymore.
357
   * The preview functionality of comments and nodes works slightly different under the hood.
358
   * CAPTCHA module should be able to handle both.
359
   *
360
   * \see testCaptchaDescriptionAfterCommentPreview()
361
   */
362
  function testCaptchaSessionReuseOnNodeForms() {
363
    // Set Test CAPTCHA on page form.
364
    captcha_set_form_id_setting('page_node_form', 'captcha/Test');
365

    
366
    // Log in as normal user.
367
    $this->drupalLogin($this->normal_user);
368

    
369
    // Page settings to post, with correct CAPTCHA answer.
370
    $edit = $this->getNodeFormValues();
371
    $edit['captcha_response'] = 'Test 123';
372
    // Preview the node
373
    $this->drupalPost('node/add/page', $edit, t('Preview'));
374

    
375
    // Check that there is no CAPTCHA after preview.
376
    $this->assertCaptchaPresence(FALSE);
377
  }
378

    
379

    
380
  /**
381
   * CAPTCHA should also be put on admin pages even if visitor
382
   * has no access
383
   */
384
  function testCaptchaOnLoginBlockOnAdminPagesIssue893810() {
385
    // Set a CAPTCHA on login block form
386
    captcha_set_form_id_setting('user_login_block', 'captcha/Math');
387

    
388
    // Check if there is a CAPTCHA on home page.
389
    $this->drupalGet('node');
390
    $this->assertCaptchaPresence(TRUE);
391

    
392
    // Check there is a CAPTCHA on "forbidden" admin pages
393
    $this->drupalGet('admin');
394
    $this->assertCaptchaPresence(TRUE);
395
  }
396

    
397
}
398

    
399

    
400
class CaptchaAdminTestCase extends CaptchaBaseWebTestCase {
401

    
402
  public static function getInfo() {
403
    return array(
404
      'name' => t('CAPTCHA administration functionality'),
405
      'description' => t('Testing of the CAPTCHA administration interface and functionality.'),
406
      'group' => t('CAPTCHA'),
407
    );
408
  }
409

    
410
  /**
411
   * Test access to the admin pages.
412
   */
413
  function testAdminAccess() {
414
    $this->drupalLogin($this->normal_user);
415
    $this->drupalGet(self::CAPTCHA_ADMIN_PATH);
416
    file_put_contents('tmp.simpletest.html', $this->drupalGetContent());
417
    $this->assertText(t('Access denied'), 'Normal users should not be able to access the CAPTCHA admin pages', 'CAPTCHA');
418

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

    
424
  /**
425
   * Test the CAPTCHA point setting getter/setter.
426
   */
427
  function testCaptchaPointSettingGetterAndSetter() {
428
    $comment_form_id = self::COMMENT_FORM_ID;
429
    // Set to 'none'.
430
    captcha_set_form_id_setting($comment_form_id, 'none');
431
    $result = captcha_get_form_id_setting($comment_form_id);
432
    $this->assertNotNull($result, 'Setting and getting CAPTCHA point: none', 'CAPTCHA');
433
    $this->assertNull($result->module, 'Setting and getting CAPTCHA point: none', 'CAPTCHA');
434
    $this->assertNull($result->captcha_type, 'Setting and getting CAPTCHA point: none', 'CAPTCHA');
435
    $result = captcha_get_form_id_setting($comment_form_id, TRUE);
436
    $this->assertEqual($result, 'none', 'Setting and symbolic getting CAPTCHA point: "none"', 'CAPTCHA');
437
    // Set to 'default'
438
    captcha_set_form_id_setting($comment_form_id, 'default');
439
    variable_set('captcha_default_challenge', 'foo/bar');
440
    $result = captcha_get_form_id_setting($comment_form_id);
441
    $this->assertNotNull($result, 'Setting and getting CAPTCHA point: default', 'CAPTCHA');
442
    $this->assertEqual($result->module, 'foo', 'Setting and getting CAPTCHA point: default', 'CAPTCHA');
443
    $this->assertEqual($result->captcha_type, 'bar', 'Setting and getting CAPTCHA point: default', 'CAPTCHA');
444
    $result = captcha_get_form_id_setting($comment_form_id, TRUE);
445
    $this->assertEqual($result, 'default', 'Setting and symbolic getting CAPTCHA point: "default"', 'CAPTCHA');
446
    // Set to 'baz/boo'.
447
    captcha_set_form_id_setting($comment_form_id, 'baz/boo');
448
    $result = captcha_get_form_id_setting($comment_form_id);
449
    $this->assertNotNull($result, 'Setting and getting CAPTCHA point: baz/boo', 'CAPTCHA');
450
    $this->assertEqual($result->module, 'baz', 'Setting and getting CAPTCHA point: baz/boo', 'CAPTCHA');
451
    $this->assertEqual($result->captcha_type, 'boo', 'Setting and getting CAPTCHA point: baz/boo', 'CAPTCHA');
452
    $result = captcha_get_form_id_setting($comment_form_id, TRUE);
453
    $this->assertEqual($result, 'baz/boo', 'Setting and symbolic getting CAPTCHA point: "baz/boo"', 'CAPTCHA');
454
    // Set to NULL (which should delete the CAPTCHA point setting entry).
455
    captcha_set_form_id_setting($comment_form_id, NULL);
456
    $result = captcha_get_form_id_setting($comment_form_id);
457
    $this->assertNull($result, 'Setting and getting CAPTCHA point: NULL', 'CAPTCHA');
458
    $result = captcha_get_form_id_setting($comment_form_id, TRUE);
459
    $this->assertNull($result, 'Setting and symbolic getting CAPTCHA point: NULL', 'CAPTCHA');
460
    // Set with object.
461
    $captcha_type = new stdClass;
462
    $captcha_type->module = 'baba';
463
    $captcha_type->captcha_type = 'fofo';
464
    captcha_set_form_id_setting($comment_form_id, $captcha_type);
465
    $result = captcha_get_form_id_setting($comment_form_id);
466
    $this->assertNotNull($result, 'Setting and getting CAPTCHA point: baba/fofo', 'CAPTCHA');
467
    $this->assertEqual($result->module, 'baba', 'Setting and getting CAPTCHA point: baba/fofo', 'CAPTCHA');
468
    $this->assertEqual($result->captcha_type, 'fofo', 'Setting and getting CAPTCHA point: baba/fofo', 'CAPTCHA');
469
    $result = captcha_get_form_id_setting($comment_form_id, TRUE);
470
    $this->assertEqual($result, 'baba/fofo', 'Setting and symbolic getting CAPTCHA point: "baba/fofo"', 'CAPTCHA');
471

    
472
  }
473

    
474

    
475
  /**
476
   * Helper function for checking CAPTCHA setting of a form.
477
   *
478
   * @param $form_id the form_id of the form to investigate.
479
   * @param $challenge_type what the challenge type should be:
480
   *   NULL, 'none', 'default' or something like 'captcha/Math'
481
   */
482
  protected function assertCaptchaSetting($form_id, $challenge_type) {
483
    $result = captcha_get_form_id_setting(self::COMMENT_FORM_ID, TRUE);
484
    $this->assertEqual($result, $challenge_type,
485
      t('Check CAPTCHA setting for form: expected: @expected, received: @received.',
486
      array('@expected' => var_export($challenge_type, TRUE), '@received' => var_export($result, TRUE))),
487
      'CAPTCHA');
488
  }
489

    
490
  /**
491
   * Testing of the CAPTCHA administration links.
492
   */
493
  function testCaptchAdminLinks() {
494
    // Log in as admin
495
    $this->drupalLogin($this->admin_user);
496

    
497
    // Enable CAPTCHA administration links.
498
    $edit = array(
499
      'captcha_administration_mode' => TRUE,
500
    );
501
    $this->drupalPost(self::CAPTCHA_ADMIN_PATH, $edit, 'Save configuration');
502

    
503
    // Create a node with comments enabled.
504
    $node = $this->createNodeWithCommentsEnabled();
505

    
506
    // Go to node page
507
    $this->drupalGet('node/' . $node->nid);
508

    
509
    // Click the add new comment link
510
    $this->clickLink(t('Add new comment'));
511
    $add_comment_url = $this->getUrl();
512
    // Remove fragment part from comment URL to avoid problems with later asserts
513
    $add_comment_url = strtok($add_comment_url, "#");
514

    
515
    ////////////////////////////////////////////////////////////
516
    // Click the CAPTCHA admin link to enable a challenge.
517
    $this->clickLink(t('Place a CAPTCHA here for untrusted users.'));
518
    // Enable Math CAPTCHA.
519
    $edit = array('captcha_type' => 'captcha/Math');
520
    $this->drupalPost($this->getUrl(), $edit, t('Save'));
521

    
522
    // Check if returned to original comment form.
523
    $this->assertUrl($add_comment_url, array(),
524
      'After setting CAPTCHA with CAPTCHA admin links: should return to original form.', 'CAPTCHA');
525
    // Check if CAPTCHA was successfully enabled (on CAPTCHA admin links fieldset).
526
    $this->assertText(t('CAPTCHA: challenge "@type" enabled', array('@type' => 'Math')),
527
      'Enable a challenge through the CAPTCHA admin links', 'CAPTCHA');
528
    // Check if CAPTCHA was successfully enabled (through API).
529
    $this->assertCaptchaSetting(self::COMMENT_FORM_ID, 'captcha/Math');
530

    
531
    //////////////////////////////////////////////////////
532
    // Edit challenge type through CAPTCHA admin links.
533
    $this->clickLink(t('change'));
534
    // Enable Math CAPTCHA.
535
    $edit = array('captcha_type' => 'default');
536
    $this->drupalPost($this->getUrl(), $edit, t('Save'));
537

    
538
    // Check if returned to original comment form.
539
    $this->assertEqual($add_comment_url, $this->getUrl(),
540
      'After editing challenge type CAPTCHA admin links: should return to original form.', 'CAPTCHA');
541
    // Check if CAPTCHA was successfully changed (on CAPTCHA admin links fieldset).
542
    // This is actually the same as the previous setting because the captcha/Math is the
543
    // default for the default challenge. TODO Make sure the edit is a real change.
544
    $this->assertText(t('CAPTCHA: challenge "@type" enabled', array('@type' => 'Math')),
545
      'Enable a challenge through the CAPTCHA admin links', 'CAPTCHA');
546
    // Check if CAPTCHA was successfully edited (through API).
547
    $this->assertCaptchaSetting(self::COMMENT_FORM_ID, 'default');
548

    
549

    
550

    
551
    //////////////////////////////////////////////////////
552
    // Disable challenge through CAPTCHA admin links.
553
    $this->clickLink(t('disable'));
554
    // And confirm.
555
    $this->drupalPost($this->getUrl(), array(), 'Disable');
556

    
557
    // Check if returned to original comment form.
558
    $this->assertEqual($add_comment_url, $this->getUrl(),
559
      'After disablin challenge with CAPTCHA admin links: should return to original form.', 'CAPTCHA');
560
    // Check if CAPTCHA was successfully disabled (on CAPTCHA admin links fieldset).
561
    $this->assertText(t('CAPTCHA: no challenge enabled'),
562
      'Disable challenge through the CAPTCHA admin links', 'CAPTCHA');
563
    // Check if CAPTCHA was successfully disabled (through API).
564
    $this->assertCaptchaSetting(self::COMMENT_FORM_ID, 'none');
565

    
566
  }
567

    
568

    
569
  function testUntrustedUserPosting() {
570
    // Set CAPTCHA on comment form.
571
    captcha_set_form_id_setting(self::COMMENT_FORM_ID, 'captcha/Math');
572

    
573
    // Create a node with comments enabled.
574
    $node = $this->createNodeWithCommentsEnabled();
575

    
576
    // Log in as normal (untrusted) user.
577
    $this->drupalLogin($this->normal_user);
578

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

    
584
    // Check if CAPTCHA is visible on form.
585
    $this->assertCaptchaPresence(TRUE);
586
    // Try to post a comment with wrong answer.
587
    $edit = $this->getCommentFormValues();
588
    $edit['captcha_response'] = 'xx';
589
    $this->drupalPost($add_comment_url, $edit, t('Preview'));
590
    $this->assertText(t(CAPTCHA_WRONG_RESPONSE_ERROR_MESSAGE),
591
      'wrong CAPTCHA should block form submission.', 'CAPTCHA');
592

    
593
    //TODO: more testing for untrusted posts.
594
  }
595

    
596

    
597

    
598
  /**
599
   * Test XSS vulnerability on CAPTCHA description.
600
   */
601
  function testXssOnCaptchaDescription() {
602
    // Set CAPTCHA on user register form.
603
    captcha_set_form_id_setting('user_register', 'captcha/Math');
604

    
605
    // Put Javascript snippet in CAPTCHA description.
606
    $this->drupalLogin($this->admin_user);
607
    $xss = '<script type="text/javascript">alert("xss")</script>';
608
    $edit = array('captcha_description' => $xss);
609
    $this->drupalPost(self::CAPTCHA_ADMIN_PATH, $edit, 'Save configuration');
610

    
611
    // Visit user register form and check if Javascript snippet is there.
612
    $this->drupalLogout();
613
    $this->drupalGet('user/register');
614
    $this->assertNoRaw($xss, 'Javascript should not be allowed in CAPTCHA description.', 'CAPTCHA');
615

    
616
  }
617

    
618
  /**
619
   * Test the CAPTCHA placement clearing.
620
   */
621
  function testCaptchaPlacementCacheClearing() {
622
    // Set CAPTCHA on user register form.
623
    captcha_set_form_id_setting('user_register_form', 'captcha/Math');
624
    // Visit user register form to fill the CAPTCHA placement cache.
625
    $this->drupalGet('user/register');
626
    // Check if there is CAPTCHA placement cache.
627
    $placement_map = variable_get('captcha_placement_map_cache', NULL);
628
    $this->assertNotNull($placement_map, 'CAPTCHA placement cache should be set.');
629
    // Clear the cache
630
    $this->drupalLogin($this->admin_user);
631
    $this->drupalPost(self::CAPTCHA_ADMIN_PATH, array(), t('Clear the CAPTCHA placement cache'));
632
    // Check that the placement cache is unset
633
    $placement_map = variable_get('captcha_placement_map_cache', NULL);
634
    $this->assertNull($placement_map, 'CAPTCHA placement cache should be unset after cache clear.');
635
  }
636

    
637
  /**
638
   * Helper function to get the CAPTCHA point setting straight from the database.
639
   * @param string $form_id
640
   * @return stdClass object
641
   */
642
  private function getCaptchaPointSettingFromDatabase($form_id) {
643
    $result = db_query(
644
      "SELECT * FROM {captcha_points} WHERE form_id = :form_id",
645
      array(':form_id' => $form_id)
646
    )->fetchObject();
647
    return $result;
648
  }
649

    
650
  /**
651
   * Method for testing the CAPTCHA point administration
652
   */
653
  function testCaptchaPointAdministration() {
654
    // Generate CAPTCHA point data:
655
    // Drupal form ID should consist of lowercase alphanumerics and underscore)
656
    $captcha_point_form_id = 'form_' . strtolower($this->randomName(32));
657
    // the Math CAPTCHA by the CAPTCHA module is always available, so let's use it
658
    $captcha_point_module = 'captcha';
659
    $captcha_point_type = 'Math';
660

    
661
    // Log in as admin
662
    $this->drupalLogin($this->admin_user);
663

    
664
    // Set CAPTCHA point through admin/user/captcha/captcha/captcha_point
665
    $form_values = array(
666
      'captcha_point_form_id' => $captcha_point_form_id,
667
      'captcha_type' => $captcha_point_module .'/'. $captcha_point_type,
668
    );
669
    $this->drupalPost(self::CAPTCHA_ADMIN_PATH . '/captcha/captcha_point', $form_values, t('Save'));
670
    $this->assertText(t('Saved CAPTCHA point settings.'),
671
      'Saving of CAPTCHA point settings');
672

    
673
    // Check in database
674
    $result = $this->getCaptchaPointSettingFromDatabase($captcha_point_form_id);
675
    $this->assertEqual($result->module, $captcha_point_module,
676
      'Enabled CAPTCHA point should have module set');
677
    $this->assertEqual($result->captcha_type, $captcha_point_type,
678
      'Enabled CAPTCHA point should have type set');
679

    
680
    // Disable CAPTCHA point again
681
    $this->drupalPost(self::CAPTCHA_ADMIN_PATH . '/captcha/captcha_point/'. $captcha_point_form_id .'/disable', array(), t('Disable'));
682
    $this->assertRaw(t('Disabled CAPTCHA for form %form_id.', array('%form_id' => $captcha_point_form_id)), 'Disabling of CAPTCHA point');
683

    
684
    // Check in database
685
    $result = $this->getCaptchaPointSettingFromDatabase($captcha_point_form_id);
686
    $this->assertNull($result->module,
687
      'Disabled CAPTCHA point should have NULL as module');
688
    $this->assertNull($result->captcha_type,
689
      'Disabled CAPTCHA point should have NULL as type');
690

    
691
    // Set CAPTCHA point through admin/user/captcha/captcha/captcha_point/$form_id
692
    $form_values = array(
693
      'captcha_type' => $captcha_point_module .'/'. $captcha_point_type,
694
    );
695
    $this->drupalPost(self::CAPTCHA_ADMIN_PATH . '/captcha/captcha_point/'. $captcha_point_form_id, $form_values, t('Save'));
696
    $this->assertText(t('Saved CAPTCHA point settings.'),
697
      'Saving of CAPTCHA point settings');
698

    
699
    // Check in database
700
    $result = $this->getCaptchaPointSettingFromDatabase($captcha_point_form_id);
701
    $this->assertEqual($result->module, $captcha_point_module,
702
      'Enabled CAPTCHA point should have module set');
703
    $this->assertEqual($result->captcha_type, $captcha_point_type,
704
      'Enabled CAPTCHA point should have type set');
705

    
706
    // Delete CAPTCHA point
707
    $this->drupalPost(self::CAPTCHA_ADMIN_PATH . '/captcha/captcha_point/'. $captcha_point_form_id .'/delete', array(), t('Delete'));
708
    $this->assertRaw(t('Deleted CAPTCHA for form %form_id.', array('%form_id' => $captcha_point_form_id)),
709
      'Deleting of CAPTCHA point');
710

    
711
    // Check in database
712
    $result = $this->getCaptchaPointSettingFromDatabase($captcha_point_form_id);
713
    $this->assertFalse($result, 'Deleted CAPTCHA point should be in database');
714
  }
715

    
716
  /**
717
   * Method for testing the CAPTCHA point administration
718
   */
719
  function testCaptchaPointAdministrationByNonAdmin() {
720
    // First add a CAPTCHA point (as admin)
721
    $this->drupalLogin($this->admin_user);
722
    $captcha_point_form_id = 'form_' . strtolower($this->randomName(32));
723
    $captcha_point_module = 'captcha';
724
    $captcha_point_type = 'Math';
725
    $form_values = array(
726
      'captcha_point_form_id' => $captcha_point_form_id,
727
      'captcha_type' => $captcha_point_module .'/'. $captcha_point_type,
728
    );
729
    $this->drupalPost(self::CAPTCHA_ADMIN_PATH . '/captcha/captcha_point/', $form_values, t('Save'));
730
    $this->assertText(t('Saved CAPTCHA point settings.'),
731
      'Saving of CAPTCHA point settings');
732

    
733
    // Switch from admin to nonadmin
734
    $this->drupalGet(url('logout', array('absolute' => TRUE)));
735
    $this->drupalLogin($this->normal_user);
736

    
737

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

    
743
    // Try to set CAPTCHA point through admin/user/captcha/captcha/captcha_point/$form_id
744
    $this->drupalGet(self::CAPTCHA_ADMIN_PATH . '/captcha/captcha_point/' . 'form_' . strtolower($this->randomName(32)));
745
    $this->assertText(t('You are not authorized to access this page.'),
746
      'Non admin should not be able to set a CAPTCHA point');
747

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

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

    
758
    // Switch from nonadmin to admin again
759
    $this->drupalGet(url('logout', array('absolute' => TRUE)));
760
    $this->drupalLogin($this->admin_user);
761

    
762
    // Check if original CAPTCHA point still exists in database
763
    $result = $this->getCaptchaPointSettingFromDatabase($captcha_point_form_id);
764
    $this->assertEqual($result->module, $captcha_point_module,
765
      'Enabled CAPTCHA point should still have module set');
766
    $this->assertEqual($result->captcha_type, $captcha_point_type,
767
      'Enabled CAPTCHA point should still have type set');
768

    
769
    // Delete CAPTCHA point
770
    $this->drupalPost(self::CAPTCHA_ADMIN_PATH . '/captcha/captcha_point/'. $captcha_point_form_id .'/delete', array(), t('Delete'));
771
    $this->assertRaw(t('Deleted CAPTCHA for form %form_id.', array('%form_id' => $captcha_point_form_id)),
772
      'Deleting of CAPTCHA point');
773
  }
774

    
775

    
776

    
777
}
778

    
779

    
780

    
781
class CaptchaPersistenceTestCase extends CaptchaBaseWebTestCase {
782

    
783
  public static function getInfo() {
784
    return array(
785
      'name' => t('CAPTCHA persistence functionality'),
786
      'description' => t('Testing of the CAPTCHA persistence functionality.'),
787
      'group' => t('CAPTCHA'),
788
    );
789
  }
790

    
791
  /**
792
   * Set up the persistence and CAPTCHA settings.
793
   * @param int $persistence the persistence value.
794
   */
795
  private function setUpPersistence($persistence) {
796
    // Log in as admin
797
    $this->drupalLogin($this->admin_user);
798
    // Set persistence.
799
    $edit = array('captcha_persistence' => $persistence);
800
    $this->drupalPost(self::CAPTCHA_ADMIN_PATH, $edit, 'Save configuration');
801
    // Log admin out.
802
    $this->drupalLogout();
803

    
804
    // Set the Test123 CAPTCHA on user register and comment form.
805
    // We have to do this with the function captcha_set_form_id_setting()
806
    // (because the CATCHA admin form does not show the Test123 option).
807
    // We also have to do this after all usage of the CAPTCHA admin form
808
    // (because posting the CAPTCHA admin form would set the CAPTCHA to 'none').
809
    captcha_set_form_id_setting('user_login', 'captcha/Test');
810
    $this->drupalGet('user');
811
    $this->assertCaptchaPresence(TRUE);
812
    captcha_set_form_id_setting('user_register_form', 'captcha/Test');
813
    $this->drupalGet('user/register');
814
    $this->assertCaptchaPresence(TRUE);
815
  }
816

    
817
  protected function assertPreservedCsid($captcha_sid_initial) {
818
    $captcha_sid = $this->getCaptchaSidFromForm();
819
    $this->assertEqual($captcha_sid_initial, $captcha_sid,
820
      "CAPTCHA session ID should be preserved (expected: $captcha_sid_initial, found: $captcha_sid).");
821
  }
822

    
823
  protected function assertDifferentCsid($captcha_sid_initial) {
824
    $captcha_sid = $this->getCaptchaSidFromForm();
825
    $this->assertNotEqual($captcha_sid_initial, $captcha_sid,
826
      "CAPTCHA session ID should be different.");
827
  }
828

    
829
  function testPersistenceAlways(){
830
    // Set up of persistence and CAPTCHAs.
831
    $this->setUpPersistence(CAPTCHA_PERSISTENCE_SHOW_ALWAYS);
832

    
833
    // Go to login form and check if there is a CAPTCHA on the login form (look for the title).
834
    $this->drupalGet('user');
835
    $this->assertCaptchaPresence(TRUE);
836
    $captcha_sid_initial = $this->getCaptchaSidFromForm();
837

    
838
    // Try to with wrong user name and password, but correct CAPTCHA.
839
    $edit = array(
840
      'name' => 'foobar',
841
      'pass' => 'bazlaz',
842
      'captcha_response' => 'Test 123',
843
    );
844
    $this->drupalPost(NULL, $edit, t('Log in'));
845
    // Check that there was no error message for the CAPTCHA.
846
    $this->assertCaptchaResponseAccepted();
847

    
848
    // Name and password were wrong, we should get an updated form with a fresh CAPTCHA.
849
    $this->assertCaptchaPresence(TRUE);
850
    $this->assertPreservedCsid($captcha_sid_initial);
851

    
852
    // Post from again.
853
    $this->drupalPost(NULL, $edit, t('Log in'));
854
    // Check that there was no error message for the CAPTCHA.
855
    $this->assertCaptchaResponseAccepted();
856
    $this->assertPreservedCsid($captcha_sid_initial);
857

    
858
  }
859

    
860
  function testPersistencePerFormInstance(){
861
    // Set up of persistence and CAPTCHAs.
862
    $this->setUpPersistence(CAPTCHA_PERSISTENCE_SKIP_ONCE_SUCCESSFUL_PER_FORM_INSTANCE);
863

    
864
    // Go to login form and check if there is a CAPTCHA on the login form.
865
    $this->drupalGet('user');
866
    $this->assertCaptchaPresence(TRUE);
867
    $captcha_sid_initial = $this->getCaptchaSidFromForm();
868

    
869
    // Try to with wrong user name and password, but correct CAPTCHA.
870
    $edit = array(
871
      'name' => 'foobar',
872
      'pass' => 'bazlaz',
873
      'captcha_response' => 'Test 123',
874
    );
875
    $this->drupalPost(NULL, $edit, t('Log in'));
876
    // Check that there was no error message for the CAPTCHA.
877
    $this->assertCaptchaResponseAccepted();
878
    // There shouldn't be a CAPTCHA on the new form.
879
    $this->assertCaptchaPresence(FALSE);
880
    $this->assertPreservedCsid($captcha_sid_initial);
881

    
882
    // Start a new form instance/session
883
    $this->drupalGet('node');
884
    $this->drupalGet('user');
885
    $this->assertCaptchaPresence(TRUE);
886
    $this->assertDifferentCsid($captcha_sid_initial);
887

    
888
    // Check another form
889
    $this->drupalGet('user/register');
890
    $this->assertCaptchaPresence(TRUE);
891
    $this->assertDifferentCsid($captcha_sid_initial);
892

    
893
  }
894

    
895
  function testPersistencePerFormType(){
896
    // Set up of persistence and CAPTCHAs.
897
    $this->setUpPersistence(CAPTCHA_PERSISTENCE_SKIP_ONCE_SUCCESSFUL_PER_FORM_TYPE);
898

    
899
    // Go to login form and check if there is a CAPTCHA on the login form.
900
    $this->drupalGet('user');
901
    $this->assertCaptchaPresence(TRUE);
902
    $captcha_sid_initial = $this->getCaptchaSidFromForm();
903

    
904
    // Try to with wrong user name and password, but correct CAPTCHA.
905
    $edit = array(
906
      'name' => 'foobar',
907
      'pass' => 'bazlaz',
908
      'captcha_response' => 'Test 123',
909
    );
910
    $this->drupalPost(NULL, $edit, t('Log in'));
911
    // Check that there was no error message for the CAPTCHA.
912
    $this->assertCaptchaResponseAccepted();
913
    // There shouldn't be a CAPTCHA on the new form.
914
    $this->assertCaptchaPresence(FALSE);
915
    $this->assertPreservedCsid($captcha_sid_initial);
916

    
917
    // Start a new form instance/session
918
    $this->drupalGet('node');
919
    $this->drupalGet('user');
920
    $this->assertCaptchaPresence(FALSE);
921
    $this->assertDifferentCsid($captcha_sid_initial);
922

    
923
    // Check another form
924
    $this->drupalGet('user/register');
925
    $this->assertCaptchaPresence(TRUE);
926
    $this->assertDifferentCsid($captcha_sid_initial);
927
  }
928

    
929
  function testPersistenceOnlyOnce(){
930
    // Set up of persistence and CAPTCHAs.
931
    $this->setUpPersistence(CAPTCHA_PERSISTENCE_SKIP_ONCE_SUCCESSFUL);
932

    
933
    // Go to login form and check if there is a CAPTCHA on the login form.
934
    $this->drupalGet('user');
935
    $this->assertCaptchaPresence(TRUE);
936
    $captcha_sid_initial = $this->getCaptchaSidFromForm();
937

    
938
    // Try to with wrong user name and password, but correct CAPTCHA.
939
    $edit = array(
940
      'name' => 'foobar',
941
      'pass' => 'bazlaz',
942
      'captcha_response' => 'Test 123',
943
    );
944
    $this->drupalPost(NULL, $edit, t('Log in'));
945
    // Check that there was no error message for the CAPTCHA.
946
    $this->assertCaptchaResponseAccepted();
947
    // There shouldn't be a CAPTCHA on the new form.
948
    $this->assertCaptchaPresence(FALSE);
949
    $this->assertPreservedCsid($captcha_sid_initial);
950

    
951
    // Start a new form instance/session
952
    $this->drupalGet('node');
953
    $this->drupalGet('user');
954
    $this->assertCaptchaPresence(FALSE);
955
    $this->assertDifferentCsid($captcha_sid_initial);
956

    
957
    // Check another form
958
    $this->drupalGet('user/register');
959
    $this->assertCaptchaPresence(FALSE);
960
    $this->assertDifferentCsid($captcha_sid_initial);
961
  }
962

    
963
}
964

    
965

    
966
class CaptchaSessionReuseAttackTestCase extends CaptchaBaseWebTestCase {
967

    
968
  public static function getInfo() {
969
    return array(
970
      'name' => t('CAPTCHA session reuse attack tests'),
971
      'description' => t('Testing of the protection against CAPTCHA session reuse attacks.'),
972
      'group' => t('CAPTCHA'),
973
    );
974
  }
975

    
976
  /**
977
   * Assert that the CAPTCHA session ID reuse attack was detected.
978
   */
979
  protected function assertCaptchaSessionIdReuseAttackDetection() {
980
    $this->assertText(t(CAPTCHA_SESSION_REUSE_ATTACK_ERROR_MESSAGE),
981
      'CAPTCHA session ID reuse attack should be detected.',
982
      'CAPTCHA');
983
    // There should be an error message about wrong response.
984
    $this->assertText(t(CAPTCHA_WRONG_RESPONSE_ERROR_MESSAGE),
985
      'CAPTCHA response should flagged as wrong.',
986
      'CAPTCHA');
987
  }
988

    
989
  function testCaptchaSessionReuseAttackDetectionOnCommentPreview() {
990
    // Create commentable node
991
    $node = $this->createNodeWithCommentsEnabled();
992
    // Set Test CAPTCHA on comment form.
993
    captcha_set_form_id_setting(self::COMMENT_FORM_ID, 'captcha/Math');
994
    variable_set('captcha_persistence', CAPTCHA_PERSISTENCE_SKIP_ONCE_SUCCESSFUL_PER_FORM_INSTANCE);
995

    
996
    // Log in as normal user.
997
    $this->drupalLogin($this->normal_user);
998

    
999
    // Go to comment form of commentable node.
1000
    $this->drupalGet('comment/reply/' . $node->nid);
1001
    $this->assertCaptchaPresence(TRUE);
1002

    
1003
    // Get CAPTCHA session ID and solution of the challenge.
1004
    $captcha_sid = $this->getCaptchaSidFromForm();
1005
    $captcha_token = $this->getCaptchaTokenFromForm();
1006
    $solution = $this->getMathCaptchaSolutionFromForm();
1007

    
1008
    // Post the form with the solution.
1009
    $edit = $this->getCommentFormValues();
1010
    $edit['captcha_response'] = $solution;
1011
    $this->drupalPost(NULL, $edit, t('Preview'));
1012
    // Answer should be accepted and further CAPTCHA ommitted.
1013
    $this->assertCaptchaResponseAccepted();
1014
    $this->assertCaptchaPresence(FALSE);
1015

    
1016
    // Post a new comment, reusing the previous CAPTCHA session.
1017
    $edit = $this->getCommentFormValues();
1018
    $edit['captcha_sid'] = $captcha_sid;
1019
    $edit['captcha_token'] = $captcha_token;
1020
    $edit['captcha_response'] = $solution;
1021
    $this->drupalPost('comment/reply/' . $node->nid, $edit, t('Preview'));
1022
    // CAPTCHA session reuse attack should be detected.
1023
    $this->assertCaptchaSessionIdReuseAttackDetection();
1024
    // There should be a CAPTCHA.
1025
    $this->assertCaptchaPresence(TRUE);
1026

    
1027
  }
1028

    
1029
  function testCaptchaSessionReuseAttackDetectionOnNodeForm() {
1030
    // Set CAPTCHA on page form.
1031
    captcha_set_form_id_setting('page_node_form', 'captcha/Math');
1032
    variable_set('captcha_persistence', CAPTCHA_PERSISTENCE_SKIP_ONCE_SUCCESSFUL_PER_FORM_INSTANCE);
1033

    
1034
    // Log in as normal user.
1035
    $this->drupalLogin($this->normal_user);
1036

    
1037
    // Go to node add form.
1038
    $this->drupalGet('node/add/page');
1039
    $this->assertCaptchaPresence(TRUE);
1040

    
1041
    // Get CAPTCHA session ID and solution of the challenge.
1042
    $captcha_sid = $this->getCaptchaSidFromForm();
1043
    $captcha_token = $this->getCaptchaTokenFromForm();
1044
    $solution = $this->getMathCaptchaSolutionFromForm();
1045

    
1046
    // Page settings to post, with correct CAPTCHA answer.
1047
    $edit = $this->getNodeFormValues();
1048
    $edit['captcha_response'] = $solution;
1049
    // Preview the node
1050
    $this->drupalPost(NULL, $edit, t('Preview'));
1051
    // Answer should be accepted.
1052
    $this->assertCaptchaResponseAccepted();
1053
    // Check that there is no CAPTCHA after preview.
1054
    $this->assertCaptchaPresence(FALSE);
1055

    
1056
    // Post a new comment, reusing the previous CAPTCHA session.
1057
    $edit = $this->getNodeFormValues();
1058
    $edit['captcha_sid'] = $captcha_sid;
1059
    $edit['captcha_token'] = $captcha_token;
1060
    $edit['captcha_response'] = $solution;
1061
    $this->drupalPost('node/add/page', $edit, t('Preview'));
1062
    // CAPTCHA session reuse attack should be detected.
1063
    $this->assertCaptchaSessionIdReuseAttackDetection();
1064
    // There should be a CAPTCHA.
1065
    $this->assertCaptchaPresence(TRUE);
1066

    
1067
  }
1068

    
1069
  function testCaptchaSessionReuseAttackDetectionOnLoginForm() {
1070
    // Set CAPTCHA on login form.
1071
    captcha_set_form_id_setting('user_login', 'captcha/Math');
1072
    variable_set('captcha_persistence', CAPTCHA_PERSISTENCE_SKIP_ONCE_SUCCESSFUL_PER_FORM_INSTANCE);
1073

    
1074
    // Go to log in form.
1075
    $this->drupalGet('user');
1076
    $this->assertCaptchaPresence(TRUE);
1077

    
1078
    // Get CAPTCHA session ID and solution of the challenge.
1079
    $captcha_sid = $this->getCaptchaSidFromForm();
1080
    $captcha_token = $this->getCaptchaTokenFromForm();
1081
    $solution = $this->getMathCaptchaSolutionFromForm();
1082

    
1083
    // Log in through form.
1084
    $edit = array(
1085
      'name' => $this->normal_user->name,
1086
      'pass' => $this->normal_user->pass_raw,
1087
      'captcha_response' => $solution,
1088
    );
1089
    $this->drupalPost(NULL, $edit, t('Log in'));
1090
    $this->assertCaptchaResponseAccepted();
1091
    $this->assertCaptchaPresence(FALSE);
1092
    // If a "log out" link appears on the page, it is almost certainly because
1093
    // the login was successful.
1094
    $pass = $this->assertLink(t('Log out'), 0, t('User %name successfully logged in.', array('%name' => $this->normal_user->name)), t('User login'));
1095

    
1096
    // Log out again.
1097
    $this->drupalLogout();
1098

    
1099
    // Try to log in again, reusing the previous CAPTCHA session.
1100
    $edit += array(
1101
      'captcha_sid' => $captcha_sid,
1102
      'captcha_token' => $captcha_token,
1103
    );
1104
    $this->drupalPost('user', $edit, t('Log in'));
1105
    // CAPTCHA session reuse attack should be detected.
1106
    $this->assertCaptchaSessionIdReuseAttackDetection();
1107
    // There should be a CAPTCHA.
1108
    $this->assertCaptchaPresence(TRUE);
1109
  }
1110

    
1111

    
1112
  public function testMultipleCaptchaProtectedFormsOnOnePage()
1113
  {
1114
    // Set Test CAPTCHA on comment form and login block
1115
    captcha_set_form_id_setting(self::COMMENT_FORM_ID, 'captcha/Test');
1116
    captcha_set_form_id_setting('user_login_block', 'captcha/Math');
1117
    $this->allowCommentPostingForAnonymousVisitors();
1118

    
1119
    // Create a node with comments enabled.
1120
    $node = $this->createNodeWithCommentsEnabled();
1121

    
1122
    // Preview comment with correct CAPTCHA answer.
1123
    $edit = $this->getCommentFormValues();
1124
    $comment_subject = $edit['subject'];
1125
    $edit['captcha_response'] = 'Test 123';
1126
    $this->drupalPost('comment/reply/' . $node->nid, $edit, t('Preview'));
1127
    // Post should be accepted: no warnings,
1128
    // no CAPTCHA reuse detection (which could be used by user log in block).
1129
    $this->assertCaptchaResponseAccepted();
1130
    $this->assertText($comment_subject);
1131

    
1132
  }
1133

    
1134
}
1135

    
1136

    
1137
// Some tricks to debug:
1138
// drupal_debug($data) // from devel module
1139
// file_put_contents('tmp.simpletest.html', $this->drupalGetContent());