Projet

Général

Profil

Paste
Télécharger (44 ko) Statistiques
| Branche: | Révision:

root / drupal7 / sites / all / modules / captcha / captcha.test @ 7547bb19

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
    file_put_contents('tmp.simpletest.html', $this->drupalGetContent());
421
    $this->assertText(t('Access denied'), 'Normal users should not be able to access the CAPTCHA admin pages', 'CAPTCHA');
422

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

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

    
476
  }
477

    
478

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

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

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

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

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

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

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

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

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

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

    
553

    
554

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

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

    
570
  }
571

    
572

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

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

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

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

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

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

    
600

    
601

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

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

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

    
620
  }
621

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
741

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

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

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

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

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

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

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

    
779

    
780

    
781
}
782

    
783

    
784

    
785
class CaptchaPersistenceTestCase extends CaptchaBaseWebTestCase {
786

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

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

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

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

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

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

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

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

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

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

    
862
  }
863

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

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

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

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

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

    
897
  }
898

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

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

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

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

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

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

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

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

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

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

    
967
}
968

    
969

    
970
class CaptchaSessionReuseAttackTestCase extends CaptchaBaseWebTestCase {
971

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

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

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

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

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

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

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

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

    
1031
  }
1032

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

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

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

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

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

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

    
1071
  }
1072

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

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

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

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

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

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

    
1115

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

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

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

    
1136
  }
1137

    
1138
}
1139

    
1140

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