Projet

Général

Profil

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

root / drupal7 / sites / all / modules / captcha / captcha.test @ a8cee257

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
    $modules = func_get_args();
64
    if (isset($modules[0]) && is_array($modules[0])) {
65
      $modules = $modules[0];
66
    }
67
    parent::setUp(array_merge(array('captcha', 'comment'), $modules));
68
    module_load_include('inc', 'captcha');
69

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

    
77
    // Create an admin user.
78
    $permissions[] = 'administer CAPTCHA settings';
79
    $permissions[] = 'skip CAPTCHA';
80
    $permissions[] = 'administer permissions';
81
    $permissions[] = 'administer content types';
82
    $this->admin_user = $this->drupalCreateUser($permissions);
83

    
84
    // Put comments on page nodes on a separate page (default in D7: below post).
85
    variable_set('comment_form_location_page', COMMENT_FORM_SEPARATE_PAGE);
86

    
87
  }
88

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

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

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

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

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

    
161

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

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

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

    
214
}
215

    
216

    
217

    
218
class CaptchaTestCase extends CaptchaBaseWebTestCase {
219

    
220
  public static function getInfo() {
221
    return array(
222
      'name' => t('General CAPTCHA functionality'),
223
      'description' => t('Testing of the basic CAPTCHA functionality.'),
224
      'group' => t('CAPTCHA'),
225
    );
226
  }
227

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

    
238
    // Set a CAPTCHA on login form
239
    captcha_set_form_id_setting('user_login', 'captcha/Math');
240

    
241
    // Check if there is a CAPTCHA on the login form (look for the title).
242
    $this->drupalGet('user');
243
    $this->assertCaptchaPresence(TRUE);
244

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

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

    
261
  }
262

    
263

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

    
279
    // Create a node with comments enabled.
280
    $node = $this->createNodeWithCommentsEnabled();
281

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

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

    
307
  /*
308
   * Testing the case sensistive/insensitive validation.
309
   */
310
  function testCaseInsensitiveValidation() {
311
    // Set Test CAPTCHA on comment form
312
    captcha_set_form_id_setting(self::COMMENT_FORM_ID, 'captcha/Test');
313

    
314
    // Log in as normal user.
315
    $this->drupalLogin($this->normal_user);
316

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

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

    
329
  }
330

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

    
343
    // Log in as normal user.
344
    $this->drupalLogin($this->normal_user);
345

    
346
    // Create a node with comments enabled.
347
    $node = $this->createNodeWithCommentsEnabled();
348

    
349
    // Preview comment with correct CAPTCHA answer.
350
    $edit = $this->getCommentFormValues();
351
    $edit['captcha_response'] = 'Test 123';
352
    $this->drupalPost('comment/reply/' . $node->nid, $edit, t('Preview'));
353

    
354
    // Check that there is no CAPTCHA after preview.
355
    $this->assertCaptchaPresence(FALSE);
356
  }
357

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

    
370
    // Log in as normal user.
371
    $this->drupalLogin($this->normal_user);
372

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

    
379
    // Check that there is no CAPTCHA after preview.
380
    $this->assertCaptchaPresence(FALSE);
381
  }
382

    
383

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

    
392
    // Check if there is a CAPTCHA on home page.
393
    $this->drupalGet('node');
394
    $this->assertCaptchaPresence(TRUE);
395

    
396
    // Check there is a CAPTCHA on "forbidden" admin pages
397
    $this->drupalGet('admin');
398
    $this->assertCaptchaPresence(TRUE);
399
  }
400

    
401
}
402

    
403

    
404
class CaptchaAdminTestCase extends CaptchaBaseWebTestCase {
405

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

    
414
  /**
415
   * Test access to the admin pages.
416
   */
417
  function testAdminAccess() {
418
    $this->drupalLogin($this->normal_user);
419
    $this->drupalGet(self::CAPTCHA_ADMIN_PATH);
420
    $this->assertText(t('Access denied'), 'Normal users should not be able to access the CAPTCHA admin pages', 'CAPTCHA');
421

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

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

    
475
  }
476

    
477

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

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

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

    
506
    // Create a node with comments enabled.
507
    $node = $this->createNodeWithCommentsEnabled();
508

    
509
    // Go to node page
510
    $this->drupalGet('node/' . $node->nid);
511

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

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

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

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

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

    
552

    
553

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

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

    
569
  }
570

    
571

    
572
  function testUntrustedUserPosting() {
573
    // Set CAPTCHA on comment form.
574
    captcha_set_form_id_setting(self::COMMENT_FORM_ID, 'captcha/Math');
575

    
576
    // Create a node with comments enabled.
577
    $node = $this->createNodeWithCommentsEnabled();
578

    
579
    // Log in as normal (untrusted) user.
580
    $this->drupalLogin($this->normal_user);
581

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

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

    
596
    //TODO: more testing for untrusted posts.
597
  }
598

    
599

    
600

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

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

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

    
619
  }
620

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

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

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

    
664
    // Log in as admin
665
    $this->drupalLogin($this->admin_user);
666

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

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

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

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

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

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

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

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

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

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

    
740

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

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

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

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

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

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

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

    
778

    
779

    
780
}
781

    
782

    
783

    
784
class CaptchaPersistenceTestCase extends CaptchaBaseWebTestCase {
785

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

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

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

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

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

    
832
  function testPersistenceAlways(){
833
    // Set up of persistence and CAPTCHAs.
834
    $this->setUpPersistence(CAPTCHA_PERSISTENCE_SHOW_ALWAYS);
835

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

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

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

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

    
861
  }
862

    
863
  function testPersistencePerFormInstance(){
864
    // Set up of persistence and CAPTCHAs.
865
    $this->setUpPersistence(CAPTCHA_PERSISTENCE_SKIP_ONCE_SUCCESSFUL_PER_FORM_INSTANCE);
866

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

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

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

    
891
    // Check another form
892
    $this->drupalGet('user/register');
893
    $this->assertCaptchaPresence(TRUE);
894
    $this->assertDifferentCsid($captcha_sid_initial);
895

    
896
  }
897

    
898
  function testPersistencePerFormType(){
899
    // Set up of persistence and CAPTCHAs.
900
    $this->setUpPersistence(CAPTCHA_PERSISTENCE_SKIP_ONCE_SUCCESSFUL_PER_FORM_TYPE);
901

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

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

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

    
926
    // Check another form
927
    $this->drupalGet('user/register');
928
    $this->assertCaptchaPresence(TRUE);
929
    $this->assertDifferentCsid($captcha_sid_initial);
930
  }
931

    
932
  function testPersistenceOnlyOnce(){
933
    // Set up of persistence and CAPTCHAs.
934
    $this->setUpPersistence(CAPTCHA_PERSISTENCE_SKIP_ONCE_SUCCESSFUL);
935

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

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

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

    
960
    // Check another form
961
    $this->drupalGet('user/register');
962
    $this->assertCaptchaPresence(FALSE);
963
    $this->assertDifferentCsid($captcha_sid_initial);
964
  }
965

    
966
}
967

    
968

    
969
class CaptchaSessionReuseAttackTestCase extends CaptchaBaseWebTestCase {
970

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

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

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

    
999
    // Log in as normal user.
1000
    $this->drupalLogin($this->normal_user);
1001

    
1002
    // Go to comment form of commentable node.
1003
    $this->drupalGet('comment/reply/' . $node->nid);
1004
    $this->assertCaptchaPresence(TRUE);
1005

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

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

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

    
1030
  }
1031

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

    
1037
    // Log in as normal user.
1038
    $this->drupalLogin($this->normal_user);
1039

    
1040
    // Go to node add form.
1041
    $this->drupalGet('node/add/page');
1042
    $this->assertCaptchaPresence(TRUE);
1043

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

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

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

    
1070
  }
1071

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

    
1077
    // Go to log in form.
1078
    $this->drupalGet('user');
1079
    $this->assertCaptchaPresence(TRUE);
1080

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

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

    
1099
    // Log out again.
1100
    $this->drupalLogout();
1101

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

    
1114

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

    
1122
    // Create a node with comments enabled.
1123
    $node = $this->createNodeWithCommentsEnabled();
1124

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

    
1135
  }
1136

    
1137
}
1138

    
1139

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