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 | |
---|
17 | // Some constants for better reuse. |
---|
18 | define('CAPTCHA_WRONG_RESPONSE_ERROR_MESSAGE', |
---|
19 | 'The answer you entered for the CAPTCHA was not correct.'); |
---|
20 | |
---|
21 | define('CAPTCHA_SESSION_REUSE_ATTACK_ERROR_MESSAGE', |
---|
22 | 'CAPTCHA session reuse attack detected.'); |
---|
23 | |
---|
24 | define('CAPTCHA_UNKNOWN_CSID_ERROR_MESSAGE', |
---|
25 | 'CAPTCHA validation error: unknown CAPTCHA session ID. Contact the site administrator if this problem persists.'); |
---|
26 | |
---|
27 | |
---|
28 | |
---|
29 | /** |
---|
30 | * Base class for CAPTCHA tests. |
---|
31 | * |
---|
32 | * Provides common setup stuff and various helper functions |
---|
33 | */ |
---|
34 | abstract class CaptchaBaseWebTestCase extends DrupalWebTestCase { |
---|
35 | |
---|
36 | /** |
---|
37 | * User with various administrative permissions. |
---|
38 | * @var Drupal user |
---|
39 | */ |
---|
40 | protected $admin_user; |
---|
41 | |
---|
42 | /** |
---|
43 | * Normal visitor with limited permissions |
---|
44 | * @var Drupal user; |
---|
45 | */ |
---|
46 | protected $normal_user; |
---|
47 | |
---|
48 | /** |
---|
49 | * Form ID of comment form on standard (page) node |
---|
50 | * @var string |
---|
51 | */ |
---|
52 | const COMMENT_FORM_ID = 'comment_form'; |
---|
53 | |
---|
54 | /** |
---|
55 | * Drupal path of the (general) CAPTCHA admin page |
---|
56 | */ |
---|
57 | const CAPTCHA_ADMIN_PATH = 'admin/user/captcha'; |
---|
58 | |
---|
59 | |
---|
60 | function setUp() { |
---|
61 | // Load two modules: the captcha module itself and the comment module for testing anonymous comments. |
---|
62 | parent::setUp('captcha', 'comment'); |
---|
63 | module_load_include('inc', 'captcha'); |
---|
64 | |
---|
65 | // Create a normal user. |
---|
66 | $permissions = array( |
---|
67 | 'access comments', 'post comments', 'post comments without approval', |
---|
68 | 'access content', 'create page content', 'edit own page content', |
---|
69 | ); |
---|
70 | $this->normal_user = $this->drupalCreateUser($permissions); |
---|
71 | |
---|
72 | // Create an admin user. |
---|
73 | $permissions[] = 'administer CAPTCHA settings'; |
---|
74 | $permissions[] = 'skip CAPTCHA'; |
---|
75 | $permissions[] = 'administer permissions'; |
---|
76 | $permissions[] = 'administer content types'; |
---|
77 | $this->admin_user = $this->drupalCreateUser($permissions); |
---|
78 | |
---|
79 | } |
---|
80 | |
---|
81 | /** |
---|
82 | * Assert that the response is accepted: |
---|
83 | * no "unknown CSID" message, no "CSID reuse attack detection" message, |
---|
84 | * no "wrong answer" message. |
---|
85 | */ |
---|
86 | protected function assertCaptchaResponseAccepted() { |
---|
87 | // There should be no error message about unknown CAPTCHA session ID. |
---|
88 | $this->assertNoText(t(CAPTCHA_UNKNOWN_CSID_ERROR_MESSAGE), |
---|
89 | 'CAPTCHA response should be accepted (known CSID).', |
---|
90 | 'CAPTCHA'); |
---|
91 | // There should be no error message about CSID reuse attack. |
---|
92 | $this->assertNoText(t(CAPTCHA_SESSION_REUSE_ATTACK_ERROR_MESSAGE), |
---|
93 | 'CAPTCHA response should be accepted (no CAPTCHA session reuse attack detection).', |
---|
94 | 'CAPTCHA'); |
---|
95 | // There should be no error message about wrong response. |
---|
96 | $this->assertNoText(t(CAPTCHA_WRONG_RESPONSE_ERROR_MESSAGE), |
---|
97 | 'CAPTCHA response should be accepted (correct response).', |
---|
98 | 'CAPTCHA'); |
---|
99 | } |
---|
100 | |
---|
101 | /** |
---|
102 | * Assert that there is a CAPTCHA on the form or not. |
---|
103 | * @param bool $presence whether there should be a CAPTCHA or not. |
---|
104 | */ |
---|
105 | protected function assertCaptchaPresence($presence) { |
---|
106 | if ($presence) { |
---|
107 | $this->assertText(_captcha_get_description(), |
---|
108 | 'There should be a CAPTCHA on the form.', 'CAPTCHA'); |
---|
109 | } |
---|
110 | else { |
---|
111 | $this->assertNoText(_captcha_get_description(), |
---|
112 | 'There should be no CAPTCHA on the form.', 'CAPTCHA'); |
---|
113 | } |
---|
114 | } |
---|
115 | |
---|
116 | /** |
---|
117 | * Helper function to create a node with comments enabled. |
---|
118 | * |
---|
119 | * @return |
---|
120 | * Created node object. |
---|
121 | */ |
---|
122 | protected function createNodeWithCommentsEnabled($type='page') { |
---|
123 | $node_settings = array( |
---|
124 | 'type' => $type, |
---|
125 | 'comment' => COMMENT_NODE_READ_WRITE, |
---|
126 | ); |
---|
127 | $node = $this->drupalCreateNode($node_settings); |
---|
128 | return $node; |
---|
129 | } |
---|
130 | |
---|
131 | /** |
---|
132 | * Helper function to generate a form values array for comment forms |
---|
133 | */ |
---|
134 | protected function getCommentFormValues() { |
---|
135 | $edit = array( |
---|
136 | 'subject' => 'comment_subject ' . $this->randomName(32), |
---|
137 | 'comment' => 'comment_body ' . $this->randomName(256), |
---|
138 | ); |
---|
139 | return $edit; |
---|
140 | } |
---|
141 | |
---|
142 | /** |
---|
143 | * Helper function to generate a form values array for node forms |
---|
144 | */ |
---|
145 | protected function getNodeFormValues() { |
---|
146 | $edit = array( |
---|
147 | 'title' => 'node_title ' . $this->randomName(32), |
---|
148 | 'body' => 'node_body ' . $this->randomName(256), |
---|
149 | ); |
---|
150 | return $edit; |
---|
151 | } |
---|
152 | |
---|
153 | |
---|
154 | /** |
---|
155 | * Get the CAPTCHA session id from the current form in the browser. |
---|
156 | */ |
---|
157 | protected function getCaptchaSidFromForm() { |
---|
158 | $elements = $this->xpath('//input[@name="captcha_sid"]'); |
---|
159 | $captcha_sid = (int) $elements[0]['value']; |
---|
160 | return $captcha_sid; |
---|
161 | } |
---|
162 | /** |
---|
163 | * Get the CAPTCHA token from the current form in the browser. |
---|
164 | */ |
---|
165 | protected function getCaptchaTokenFromForm() { |
---|
166 | $elements = $this->xpath('//input[@name="captcha_token"]'); |
---|
167 | $captcha_token = (int) $elements[0]['value']; |
---|
168 | return $captcha_token; |
---|
169 | } |
---|
170 | |
---|
171 | /** |
---|
172 | * Get the solution of the math CAPTCHA from the current form in the browser. |
---|
173 | */ |
---|
174 | protected function getMathCaptchaSolutionFromForm() { |
---|
175 | // Get the math challenge. |
---|
176 | $elements = $this->xpath('//div[@id="edit-captcha-response-wrapper"]/span[@class="field-prefix"]'); |
---|
177 | $challenge = (string) $elements[0]; |
---|
178 | // Extract terms and operator from challenge. |
---|
179 | $matches = array(); |
---|
180 | $ret = preg_match('/\\s*(\\d+)\\s*(-|\\+)\\s*(\\d+)\\s*=\\s*/', $challenge, $matches); |
---|
181 | // Solve the challenge |
---|
182 | $a = (int) $matches[1]; |
---|
183 | $b = (int) $matches[3]; |
---|
184 | $solution = $matches[2] == '-' ? $a - $b : $a + $b; |
---|
185 | return $solution; |
---|
186 | } |
---|
187 | |
---|
188 | /** |
---|
189 | * Helper function to allow comment posting for anonymous users. |
---|
190 | */ |
---|
191 | protected function allowCommentPostingForAnonymousVisitors() { |
---|
192 | // Log in as admin. |
---|
193 | $this->drupalLogin($this->admin_user); |
---|
194 | // Post user permissions form |
---|
195 | $edit = array( |
---|
196 | '1[access comments]' => true, |
---|
197 | '1[post comments]' => true, |
---|
198 | '1[post comments without approval]' => true, |
---|
199 | ); |
---|
200 | $this->drupalPost('admin/user/permissions', $edit, 'Save permissions'); |
---|
201 | $this->assertText('The changes have been saved.'); |
---|
202 | // Log admin out |
---|
203 | $this->drupalLogout(); |
---|
204 | } |
---|
205 | |
---|
206 | } |
---|
207 | |
---|
208 | |
---|
209 | |
---|
210 | class CaptchaTestCase extends CaptchaBaseWebTestCase { |
---|
211 | |
---|
212 | public static function getInfo() { |
---|
213 | return array( |
---|
214 | 'name' => t('General CAPTCHA functionality'), |
---|
215 | 'description' => t('Testing of the basic CAPTCHA functionality.'), |
---|
216 | 'group' => t('CAPTCHA'), |
---|
217 | ); |
---|
218 | } |
---|
219 | |
---|
220 | /** |
---|
221 | * Testing the protection of the user log in form. |
---|
222 | */ |
---|
223 | function testCaptchaOnLoginForm() { |
---|
224 | // Create user and test log in without CAPTCHA. |
---|
225 | $user = $this->drupalCreateUser(); |
---|
226 | $this->drupalLogin($user); |
---|
227 | // Log out again. |
---|
228 | $this->drupalLogout(); |
---|
229 | |
---|
230 | // Set a CAPTCHA on login form |
---|
231 | captcha_set_form_id_setting('user_login', 'captcha/Math'); |
---|
232 | |
---|
233 | // Check if there is a CAPTCHA on the login form (look for the title). |
---|
234 | $this->drupalGet('user'); |
---|
235 | $this->assertCaptchaPresence(TRUE); |
---|
236 | |
---|
237 | // Try to log in, which should fail. |
---|
238 | $edit = array( |
---|
239 | 'name' => $user->name, |
---|
240 | 'pass' => $user->pass_raw, |
---|
241 | 'captcha_response' => '?', |
---|
242 | ); |
---|
243 | $this->drupalPost('user', $edit, t('Log in')); |
---|
244 | // Check for error message. |
---|
245 | $this->assertText(t(CAPTCHA_WRONG_RESPONSE_ERROR_MESSAGE), |
---|
246 | 'CAPTCHA should block user login form', 'CAPTCHA'); |
---|
247 | |
---|
248 | // And make sure that user is not logged in: check for name and password fields on ?q=user |
---|
249 | $this->drupalGet('user'); |
---|
250 | $this->assertField('name', t('Username field found.'), 'CAPTCHA'); |
---|
251 | $this->assertField('pass', t('Password field found.'), 'CAPTCHA'); |
---|
252 | |
---|
253 | } |
---|
254 | |
---|
255 | |
---|
256 | /** |
---|
257 | * Assert function for testing if comment posting works as it should. |
---|
258 | * |
---|
259 | * Creates node with comment writing enabled, tries to post comment |
---|
260 | * with given CAPTCHA response (caller should enable the desired |
---|
261 | * challenge on page node comment forms) and checks if the result is as expected. |
---|
262 | * |
---|
263 | * @param $captcha_response the response on the CAPTCHA |
---|
264 | * @param $should_pass boolean describing if the posting should pass or should be blocked |
---|
265 | * @param $message message to prefix to nested asserts |
---|
266 | */ |
---|
267 | protected function assertCommentPosting($captcha_response, $should_pass, $message) { |
---|
268 | // Make sure comments on pages can be saved directely without preview. |
---|
269 | variable_set('comment_preview_page', COMMENT_PREVIEW_OPTIONAL); |
---|
270 | |
---|
271 | // Create a node with comments enabled. |
---|
272 | $node = $this->createNodeWithCommentsEnabled(); |
---|
273 | |
---|
274 | // Post comment on node. |
---|
275 | $edit = $this->getCommentFormValues(); |
---|
276 | $comment_subject = $edit['subject']; |
---|
277 | $comment_body = $edit['comment']; |
---|
278 | $edit['captcha_response'] = $captcha_response; |
---|
279 | $this->drupalPost('comment/reply/' . $node->nid, $edit, t('Save')); |
---|
280 | |
---|
281 | if ($should_pass) { |
---|
282 | // There should be no error message. |
---|
283 | $this->assertCaptchaResponseAccepted(); |
---|
284 | // Get node page and check that comment shows up. |
---|
285 | $this->drupalGet('node/' . $node->nid); |
---|
286 | $this->assertText($comment_subject, $message .' Comment should show up on node page.', 'CAPTCHA'); |
---|
287 | $this->assertText($comment_body, $message .' Comment should show up on node page.', 'CAPTCHA'); |
---|
288 | } |
---|
289 | else { |
---|
290 | // Check for error message. |
---|
291 | $this->assertText(t(CAPTCHA_WRONG_RESPONSE_ERROR_MESSAGE), $message .' Comment submission should be blocked.', 'CAPTCHA'); |
---|
292 | // Get node page and check that comment is not present. |
---|
293 | $this->drupalGet('node/' . $node->nid); |
---|
294 | $this->assertNoText($comment_subject, $message .' Comment should not show up on node page.', 'CAPTCHA'); |
---|
295 | $this->assertNoText($comment_body, $message .' Comment should not show up on node page.', 'CAPTCHA'); |
---|
296 | } |
---|
297 | } |
---|
298 | |
---|
299 | /* |
---|
300 | * Testing the case sensistive/insensitive validation. |
---|
301 | */ |
---|
302 | function testCaseInsensitiveValidation() { |
---|
303 | // Set Test CAPTCHA on comment form |
---|
304 | captcha_set_form_id_setting(self::COMMENT_FORM_ID, 'captcha/Test'); |
---|
305 | |
---|
306 | // Log in as normal user. |
---|
307 | $this->drupalLogin($this->normal_user); |
---|
308 | |
---|
309 | // Test case sensitive posting. |
---|
310 | variable_set('captcha_default_validation', CAPTCHA_DEFAULT_VALIDATION_CASE_SENSITIVE); |
---|
311 | $this->assertCommentPosting('Test 123', TRUE, 'Case sensitive validation of right casing.'); |
---|
312 | $this->assertCommentPosting('test 123', FALSE, 'Case sensitive validation of wrong casing.'); |
---|
313 | $this->assertCommentPosting('TEST 123', FALSE, 'Case sensitive validation of wrong casing.'); |
---|
314 | |
---|
315 | // Test case insensitive posting (the default) |
---|
316 | variable_set('captcha_default_validation', CAPTCHA_DEFAULT_VALIDATION_CASE_INSENSITIVE); |
---|
317 | $this->assertCommentPosting('Test 123', TRUE, 'Case insensitive validation of right casing.'); |
---|
318 | $this->assertCommentPosting('test 123', TRUE, 'Case insensitive validation of wrong casing.'); |
---|
319 | $this->assertCommentPosting('TEST 123', TRUE, 'Case insensitive validation of wrong casing.'); |
---|
320 | |
---|
321 | } |
---|
322 | |
---|
323 | /** |
---|
324 | * Test if the CAPTCHA description is only shown if there are challenge widgets to show. |
---|
325 | * For example, when a comment is previewed with correct CAPTCHA answer, |
---|
326 | * a challenge is generated and added to the form but removed in the pre_render phase. |
---|
327 | * The CAPTCHA description should not show up either. |
---|
328 | * |
---|
329 | * \see testCaptchaSessionReuseOnNodeForms() |
---|
330 | */ |
---|
331 | function testCaptchaDescriptionAfterCommentPreview() { |
---|
332 | // Set Test CAPTCHA on comment form. |
---|
333 | captcha_set_form_id_setting(self::COMMENT_FORM_ID, 'captcha/Test'); |
---|
334 | |
---|
335 | // Log in as normal user. |
---|
336 | $this->drupalLogin($this->normal_user); |
---|
337 | |
---|
338 | // Create a node with comments enabled. |
---|
339 | $node = $this->createNodeWithCommentsEnabled(); |
---|
340 | |
---|
341 | // Preview comment with correct CAPTCHA answer. |
---|
342 | $edit = $this->getCommentFormValues(); |
---|
343 | $edit['captcha_response'] = 'Test 123'; |
---|
344 | $this->drupalPost('comment/reply/' . $node->nid, $edit, t('Preview')); |
---|
345 | |
---|
346 | // Check that there is no CAPTCHA after preview. |
---|
347 | $this->assertCaptchaPresence(FALSE); |
---|
348 | } |
---|
349 | |
---|
350 | /** |
---|
351 | * Test if the CAPTCHA session ID is reused when previewing nodes: |
---|
352 | * node preview after correct response should not show CAPTCHA anymore. |
---|
353 | * The preview functionality of comments and nodes works slightly different under the hood. |
---|
354 | * CAPTCHA module should be able to handle both. |
---|
355 | * |
---|
356 | * \see testCaptchaDescriptionAfterCommentPreview() |
---|
357 | */ |
---|
358 | function testCaptchaSessionReuseOnNodeForms() { |
---|
359 | // Set Test CAPTCHA on page form. |
---|
360 | captcha_set_form_id_setting('page_node_form', 'captcha/Test'); |
---|
361 | |
---|
362 | // Log in as normal user. |
---|
363 | $this->drupalLogin($this->normal_user); |
---|
364 | |
---|
365 | // Page settings to post, with correct CAPTCHA answer. |
---|
366 | $edit = $this->getNodeFormValues(); |
---|
367 | $edit['captcha_response'] = 'Test 123'; |
---|
368 | // Preview the node |
---|
369 | $this->drupalPost('node/add/page', $edit, t('Preview')); |
---|
370 | |
---|
371 | // Check that there is no CAPTCHA after preview. |
---|
372 | $this->assertCaptchaPresence(FALSE); |
---|
373 | } |
---|
374 | |
---|
375 | |
---|
376 | /** |
---|
377 | * CAPTCHA should also be put on admin pages even if visitor |
---|
378 | * has no access |
---|
379 | */ |
---|
380 | function testCaptchaOnLoginBlockOnAdminPagesIssue893810() { |
---|
381 | // Set a CAPTCHA on login block form |
---|
382 | captcha_set_form_id_setting('user_login_block', 'captcha/Math'); |
---|
383 | |
---|
384 | // Check if there is a CAPTCHA on home page. |
---|
385 | $this->drupalGet('node'); |
---|
386 | $this->assertCaptchaPresence(TRUE); |
---|
387 | |
---|
388 | // Check there is a CAPTCHA on "forbidden" admin pages |
---|
389 | $this->drupalGet('admin'); |
---|
390 | $this->assertCaptchaPresence(TRUE); |
---|
391 | } |
---|
392 | |
---|
393 | } |
---|
394 | |
---|
395 | |
---|
396 | class CaptchaAdminTestCase extends CaptchaBaseWebTestCase { |
---|
397 | |
---|
398 | public static function getInfo() { |
---|
399 | return array( |
---|
400 | 'name' => t('CAPTCHA administration functionality'), |
---|
401 | 'description' => t('Testing of the CAPTCHA administration interface and functionality.'), |
---|
402 | 'group' => t('CAPTCHA'), |
---|
403 | ); |
---|
404 | } |
---|
405 | |
---|
406 | /** |
---|
407 | * Test access to the admin pages. |
---|
408 | */ |
---|
409 | function testAdminAccess() { |
---|
410 | $this->drupalLogin($this->normal_user); |
---|
411 | $this->drupalGet(self::CAPTCHA_ADMIN_PATH); |
---|
412 | file_put_contents('tmp.simpletest.html', $this->drupalGetContent()); |
---|
413 | $this->assertText(t('Access denied'), 'Normal users should not be able to access the CAPTCHA admin pages', 'CAPTCHA'); |
---|
414 | |
---|
415 | $this->drupalLogin($this->admin_user); |
---|
416 | $this->drupalGet(self::CAPTCHA_ADMIN_PATH); |
---|
417 | $this->assertNoText(t('Access denied'), 'Admin users should be able to access the CAPTCHA admin pages', 'CAPTCHA'); |
---|
418 | } |
---|
419 | |
---|
420 | /** |
---|
421 | * Test the CAPTCHA point setting getter/setter. |
---|
422 | */ |
---|
423 | function testCaptchaPointSettingGetterAndSetter() { |
---|
424 | $comment_form_id = self::COMMENT_FORM_ID; |
---|
425 | // Set to 'none'. |
---|
426 | captcha_set_form_id_setting($comment_form_id, 'none'); |
---|
427 | $result = captcha_get_form_id_setting($comment_form_id); |
---|
428 | $this->assertNotNull($result, 'Setting and getting CAPTCHA point: none', 'CAPTCHA'); |
---|
429 | $this->assertNull($result->module, 'Setting and getting CAPTCHA point: none', 'CAPTCHA'); |
---|
430 | $this->assertNull($result->captcha_type, 'Setting and getting CAPTCHA point: none', 'CAPTCHA'); |
---|
431 | $result = captcha_get_form_id_setting($comment_form_id, TRUE); |
---|
432 | $this->assertEqual($result, 'none', 'Setting and symbolic getting CAPTCHA point: "none"', 'CAPTCHA'); |
---|
433 | // Set to 'default' |
---|
434 | captcha_set_form_id_setting($comment_form_id, 'default'); |
---|
435 | variable_set('captcha_default_challenge', 'foo/bar'); |
---|
436 | $result = captcha_get_form_id_setting($comment_form_id); |
---|
437 | $this->assertNotNull($result, 'Setting and getting CAPTCHA point: default', 'CAPTCHA'); |
---|
438 | $this->assertEqual($result->module, 'foo', 'Setting and getting CAPTCHA point: default', 'CAPTCHA'); |
---|
439 | $this->assertEqual($result->captcha_type, 'bar', 'Setting and getting CAPTCHA point: default', 'CAPTCHA'); |
---|
440 | $result = captcha_get_form_id_setting($comment_form_id, TRUE); |
---|
441 | $this->assertEqual($result, 'default', 'Setting and symbolic getting CAPTCHA point: "default"', 'CAPTCHA'); |
---|
442 | // Set to 'baz/boo'. |
---|
443 | captcha_set_form_id_setting($comment_form_id, 'baz/boo'); |
---|
444 | $result = captcha_get_form_id_setting($comment_form_id); |
---|
445 | $this->assertNotNull($result, 'Setting and getting CAPTCHA point: baz/boo', 'CAPTCHA'); |
---|
446 | $this->assertEqual($result->module, 'baz', 'Setting and getting CAPTCHA point: baz/boo', 'CAPTCHA'); |
---|
447 | $this->assertEqual($result->captcha_type, 'boo', 'Setting and getting CAPTCHA point: baz/boo', 'CAPTCHA'); |
---|
448 | $result = captcha_get_form_id_setting($comment_form_id, TRUE); |
---|
449 | $this->assertEqual($result, 'baz/boo', 'Setting and symbolic getting CAPTCHA point: "baz/boo"', 'CAPTCHA'); |
---|
450 | // Set to NULL (which should delete the CAPTCHA point setting entry). |
---|
451 | captcha_set_form_id_setting($comment_form_id, NULL); |
---|
452 | $result = captcha_get_form_id_setting($comment_form_id); |
---|
453 | $this->assertNull($result, 'Setting and getting CAPTCHA point: NULL', 'CAPTCHA'); |
---|
454 | $result = captcha_get_form_id_setting($comment_form_id, TRUE); |
---|
455 | $this->assertNull($result, 'Setting and symbolic getting CAPTCHA point: NULL', 'CAPTCHA'); |
---|
456 | // Set with object. |
---|
457 | $captcha_type = new stdClass; |
---|
458 | $captcha_type->module = 'baba'; |
---|
459 | $captcha_type->captcha_type = 'fofo'; |
---|
460 | captcha_set_form_id_setting($comment_form_id, $captcha_type); |
---|
461 | $result = captcha_get_form_id_setting($comment_form_id); |
---|
462 | $this->assertNotNull($result, 'Setting and getting CAPTCHA point: baba/fofo', 'CAPTCHA'); |
---|
463 | $this->assertEqual($result->module, 'baba', 'Setting and getting CAPTCHA point: baba/fofo', 'CAPTCHA'); |
---|
464 | $this->assertEqual($result->captcha_type, 'fofo', 'Setting and getting CAPTCHA point: baba/fofo', 'CAPTCHA'); |
---|
465 | $result = captcha_get_form_id_setting($comment_form_id, TRUE); |
---|
466 | $this->assertEqual($result, 'baba/fofo', 'Setting and symbolic getting CAPTCHA point: "baba/fofo"', 'CAPTCHA'); |
---|
467 | |
---|
468 | } |
---|
469 | |
---|
470 | |
---|
471 | /** |
---|
472 | * Helper function for checking CAPTCHA setting of a form. |
---|
473 | * |
---|
474 | * @param $form_id the form_id of the form to investigate. |
---|
475 | * @param $challenge_type what the challenge type should be: |
---|
476 | * NULL, 'none', 'default' or something like 'captcha/Math' |
---|
477 | */ |
---|
478 | protected function assertCaptchaSetting($form_id, $challenge_type) { |
---|
479 | $result = captcha_get_form_id_setting(self::COMMENT_FORM_ID, TRUE); |
---|
480 | $this->assertEqual($result, $challenge_type, |
---|
481 | t('Check CAPTCHA setting for form: expected: @expected, received: @received.', |
---|
482 | array('@expected' => var_export($challenge_type, TRUE), '@received' => var_export($result, TRUE))), |
---|
483 | 'CAPTCHA'); |
---|
484 | } |
---|
485 | |
---|
486 | /** |
---|
487 | * Testing of the CAPTCHA administration links. |
---|
488 | */ |
---|
489 | function testCaptchAdminLinks() { |
---|
490 | // Log in as admin |
---|
491 | $this->drupalLogin($this->admin_user); |
---|
492 | |
---|
493 | // Enable CAPTCHA administration links. |
---|
494 | $edit = array( |
---|
495 | 'captcha_administration_mode' => TRUE, |
---|
496 | ); |
---|
497 | $this->drupalPost(self::CAPTCHA_ADMIN_PATH, $edit, 'Save configuration'); |
---|
498 | |
---|
499 | // Create a node with comments enabled. |
---|
500 | $node = $this->createNodeWithCommentsEnabled(); |
---|
501 | |
---|
502 | // Go to node page |
---|
503 | $this->drupalGet('node/' . $node->nid); |
---|
504 | |
---|
505 | // Click the add new comment link |
---|
506 | $this->clickLink(t('Add new comment')); |
---|
507 | $add_comment_url = $this->getUrl(); |
---|
508 | // Remove fragment part from comment URL to avoid problems with later asserts |
---|
509 | $add_comment_url = strtok($add_comment_url, "#"); |
---|
510 | |
---|
511 | //////////////////////////////////////////////////////////// |
---|
512 | // Click the CAPTCHA admin link to enable a challenge. |
---|
513 | $this->clickLink(t('Place a CAPTCHA here for untrusted users.')); |
---|
514 | // Enable Math CAPTCHA. |
---|
515 | $edit = array('captcha_type' => 'captcha/Math'); |
---|
516 | $this->drupalPost($this->getUrl(), $edit, t('Save')); |
---|
517 | |
---|
518 | // Check if returned to original comment form. |
---|
519 | $this->assertUrl($add_comment_url, array(), |
---|
520 | 'After setting CAPTCHA with CAPTCHA admin links: should return to original form.', 'CAPTCHA'); |
---|
521 | // Check if CAPTCHA was successfully enabled (on CAPTCHA admin links fieldset). |
---|
522 | $this->assertText(t('CAPTCHA: challenge "@type" enabled', array('@type' => 'Math')), |
---|
523 | 'Enable a challenge through the CAPTCHA admin links', 'CAPTCHA'); |
---|
524 | // Check if CAPTCHA was successfully enabled (through API). |
---|
525 | $this->assertCaptchaSetting(self::COMMENT_FORM_ID, 'captcha/Math'); |
---|
526 | |
---|
527 | ////////////////////////////////////////////////////// |
---|
528 | // Edit challenge type through CAPTCHA admin links. |
---|
529 | $this->clickLink(t('change')); |
---|
530 | // Enable Math CAPTCHA. |
---|
531 | $edit = array('captcha_type' => 'default'); |
---|
532 | $this->drupalPost($this->getUrl(), $edit, t('Save')); |
---|
533 | |
---|
534 | // Check if returned to original comment form. |
---|
535 | $this->assertEqual($add_comment_url, $this->getUrl(), |
---|
536 | 'After editing challenge type CAPTCHA admin links: should return to original form.', 'CAPTCHA'); |
---|
537 | // Check if CAPTCHA was successfully changed (on CAPTCHA admin links fieldset). |
---|
538 | // This is actually the same as the previous setting because the captcha/Math is the |
---|
539 | // default for the default challenge. TODO Make sure the edit is a real change. |
---|
540 | $this->assertText(t('CAPTCHA: challenge "@type" enabled', array('@type' => 'Math')), |
---|
541 | 'Enable a challenge through the CAPTCHA admin links', 'CAPTCHA'); |
---|
542 | // Check if CAPTCHA was successfully edited (through API). |
---|
543 | $this->assertCaptchaSetting(self::COMMENT_FORM_ID, 'default'); |
---|
544 | |
---|
545 | |
---|
546 | |
---|
547 | ////////////////////////////////////////////////////// |
---|
548 | // Disable challenge through CAPTCHA admin links. |
---|
549 | $this->clickLink(t('disable')); |
---|
550 | // And confirm. |
---|
551 | $this->drupalPost($this->getUrl(), array(), 'Disable'); |
---|
552 | |
---|
553 | // Check if returned to original comment form. |
---|
554 | $this->assertEqual($add_comment_url, $this->getUrl(), |
---|
555 | 'After disablin challenge with CAPTCHA admin links: should return to original form.', 'CAPTCHA'); |
---|
556 | // Check if CAPTCHA was successfully disabled (on CAPTCHA admin links fieldset). |
---|
557 | $this->assertText(t('CAPTCHA: no challenge enabled'), |
---|
558 | 'Disable challenge through the CAPTCHA admin links', 'CAPTCHA'); |
---|
559 | // Check if CAPTCHA was successfully disabled (through API). |
---|
560 | $this->assertCaptchaSetting(self::COMMENT_FORM_ID, 'none'); |
---|
561 | |
---|
562 | } |
---|
563 | |
---|
564 | |
---|
565 | function testUntrustedUserPosting() { |
---|
566 | // Set CAPTCHA on comment form. |
---|
567 | captcha_set_form_id_setting(self::COMMENT_FORM_ID, 'captcha/Math'); |
---|
568 | |
---|
569 | // Create a node with comments enabled. |
---|
570 | $node = $this->createNodeWithCommentsEnabled(); |
---|
571 | |
---|
572 | // Log in as normal (untrusted) user. |
---|
573 | $this->drupalLogin($this->normal_user); |
---|
574 | |
---|
575 | // Go to node page and click the "add comment" link. |
---|
576 | $this->drupalGet('node/' . $node->nid); |
---|
577 | $this->clickLink(t('Add new comment')); |
---|
578 | $add_comment_url = $this->getUrl(); |
---|
579 | |
---|
580 | // Check if CAPTCHA is visible on form. |
---|
581 | $this->assertCaptchaPresence(TRUE); |
---|
582 | // Try to post a comment with wrong answer. |
---|
583 | $edit = $this->getCommentFormValues(); |
---|
584 | $edit['captcha_response'] = 'xx'; |
---|
585 | $this->drupalPost($add_comment_url, $edit, t('Preview')); |
---|
586 | $this->assertText(t(CAPTCHA_WRONG_RESPONSE_ERROR_MESSAGE), |
---|
587 | 'wrong CAPTCHA should block form submission.', 'CAPTCHA'); |
---|
588 | |
---|
589 | //TODO: more testing for untrusted posts. |
---|
590 | } |
---|
591 | |
---|
592 | |
---|
593 | |
---|
594 | /** |
---|
595 | * Test XSS vulnerability on CAPTCHA description. |
---|
596 | */ |
---|
597 | function testXssOnCaptchaDescription() { |
---|
598 | // Set CAPTCHA on user register form. |
---|
599 | captcha_set_form_id_setting('user_register', 'captcha/Math'); |
---|
600 | |
---|
601 | // Put Javascript snippet in CAPTCHA description. |
---|
602 | $this->drupalLogin($this->admin_user); |
---|
603 | $xss = '<script type="text/javascript">alert("xss")</script>'; |
---|
604 | $edit = array('captcha_description' => $xss); |
---|
605 | $this->drupalPost(self::CAPTCHA_ADMIN_PATH, $edit, 'Save configuration'); |
---|
606 | |
---|
607 | // Visit user register form and check if Javascript snippet is there. |
---|
608 | $this->drupalLogout(); |
---|
609 | $this->drupalGet('user/register'); |
---|
610 | $this->assertNoRaw($xss, 'Javascript should not be allowed in CAPTCHA description.', 'CAPTCHA'); |
---|
611 | |
---|
612 | } |
---|
613 | |
---|
614 | /** |
---|
615 | * Test the CAPTCHA placement flushing. |
---|
616 | */ |
---|
617 | function testCaptchaPlacementCacheFlushing() { |
---|
618 | // Set CAPTCHA on user register form. |
---|
619 | captcha_set_form_id_setting('user_register', 'captcha/Math'); |
---|
620 | // Visit user register form to fill the CAPTCHA placement cache. |
---|
621 | $this->drupalGet('user/register'); |
---|
622 | // Check if there is CAPTCHA placement cache. |
---|
623 | $placement_map = variable_get('captcha_placement_map_cache', NULL); |
---|
624 | $this->assertNotNull($placement_map, 'CAPTCHA placement cache should be set.'); |
---|
625 | // Flush the cache |
---|
626 | $this->drupalLogin($this->admin_user); |
---|
627 | $this->drupalPost(self::CAPTCHA_ADMIN_PATH, array(), t('Flush the CAPTCHA placement cache')); |
---|
628 | // Check that the placement cache is unset |
---|
629 | $placement_map = variable_get('captcha_placement_map_cache', NULL); |
---|
630 | $this->assertNull($placement_map, 'CAPTCHA placement cache should be unset after flush.'); |
---|
631 | } |
---|
632 | |
---|
633 | /** |
---|
634 | * Helper function to get the CAPTCHA point setting straight from the database. |
---|
635 | * @param string $form_id |
---|
636 | * @return stdClass object |
---|
637 | */ |
---|
638 | private function getCaptchaPointSettingFromDatabase($form_id) { |
---|
639 | $result = db_fetch_object(db_query( |
---|
640 | "SELECT * FROM {captcha_points} WHERE form_id='%s'", |
---|
641 | array($form_id) |
---|
642 | )); |
---|
643 | return $result; |
---|
644 | } |
---|
645 | |
---|
646 | /** |
---|
647 | * Method for testing the CAPTCHA point administration |
---|
648 | */ |
---|
649 | function testCaptchaPointAdministration() { |
---|
650 | // Generate CAPTCHA point data: |
---|
651 | // Drupal form ID should consist of lowercase alphanumerics and underscore) |
---|
652 | $captcha_point_form_id = 'form_' . strtolower($this->randomName(32)); |
---|
653 | // the Math CAPTCHA by the CAPTCHA module is always available, so let's use it |
---|
654 | $captcha_point_module = 'captcha'; |
---|
655 | $captcha_point_type = 'Math'; |
---|
656 | |
---|
657 | // Log in as admin |
---|
658 | $this->drupalLogin($this->admin_user); |
---|
659 | |
---|
660 | // Set CAPTCHA point through admin/user/captcha/captcha/captcha_point |
---|
661 | $form_values = array( |
---|
662 | 'captcha_point_form_id' => $captcha_point_form_id, |
---|
663 | 'captcha_type' => $captcha_point_module .'/'. $captcha_point_type, |
---|
664 | ); |
---|
665 | $this->drupalPost(self::CAPTCHA_ADMIN_PATH . '/captcha/captcha_point', $form_values, t('Save')); |
---|
666 | $this->assertText(t('Saved CAPTCHA point settings.'), |
---|
667 | 'Saving of CAPTCHA point settings'); |
---|
668 | |
---|
669 | // Check in database |
---|
670 | $result = $this->getCaptchaPointSettingFromDatabase($captcha_point_form_id); |
---|
671 | $this->assertEqual($result->module, $captcha_point_module, |
---|
672 | 'Enabled CAPTCHA point should have module set'); |
---|
673 | $this->assertEqual($result->captcha_type, $captcha_point_type, |
---|
674 | 'Enabled CAPTCHA point should have type set'); |
---|
675 | |
---|
676 | // Disable CAPTCHA point again |
---|
677 | $this->drupalPost(self::CAPTCHA_ADMIN_PATH . '/captcha/captcha_point/'. $captcha_point_form_id .'/disable', array(), t('Disable')); |
---|
678 | $this->assertRaw(t('Disabled CAPTCHA for form %form_id.', array('%form_id' => $captcha_point_form_id)), 'Disabling of CAPTCHA point'); |
---|
679 | |
---|
680 | // Check in database |
---|
681 | $result = $this->getCaptchaPointSettingFromDatabase($captcha_point_form_id); |
---|
682 | $this->assertNull($result->module, |
---|
683 | 'Disabled CAPTCHA point should have NULL as module'); |
---|
684 | $this->assertNull($result->captcha_type, |
---|
685 | 'Disabled CAPTCHA point should have NULL as type'); |
---|
686 | |
---|
687 | // Set CAPTCHA point through admin/user/captcha/captcha/captcha_point/$form_id |
---|
688 | $form_values = array( |
---|
689 | 'captcha_type' => $captcha_point_module .'/'. $captcha_point_type, |
---|
690 | ); |
---|
691 | $this->drupalPost(self::CAPTCHA_ADMIN_PATH . '/captcha/captcha_point/'. $captcha_point_form_id, $form_values, t('Save')); |
---|
692 | $this->assertText(t('Saved CAPTCHA point settings.'), |
---|
693 | 'Saving of CAPTCHA point settings'); |
---|
694 | |
---|
695 | // Check in database |
---|
696 | $result = $this->getCaptchaPointSettingFromDatabase($captcha_point_form_id); |
---|
697 | $this->assertEqual($result->module, $captcha_point_module, |
---|
698 | 'Enabled CAPTCHA point should have module set'); |
---|
699 | $this->assertEqual($result->captcha_type, $captcha_point_type, |
---|
700 | 'Enabled CAPTCHA point should have type set'); |
---|
701 | |
---|
702 | // Delete CAPTCHA point |
---|
703 | $this->drupalPost(self::CAPTCHA_ADMIN_PATH . '/captcha/captcha_point/'. $captcha_point_form_id .'/delete', array(), t('Delete')); |
---|
704 | $this->assertRaw(t('Deleted CAPTCHA for form %form_id.', array('%form_id' => $captcha_point_form_id)), |
---|
705 | 'Deleting of CAPTCHA point'); |
---|
706 | |
---|
707 | // Check in database |
---|
708 | $result = $this->getCaptchaPointSettingFromDatabase($captcha_point_form_id); |
---|
709 | $this->assertFalse($result, 'Deleted CAPTCHA point should be in database'); |
---|
710 | } |
---|
711 | |
---|
712 | /** |
---|
713 | * Method for testing the CAPTCHA point administration |
---|
714 | */ |
---|
715 | function testCaptchaPointAdministrationByNonAdmin() { |
---|
716 | // First add a CAPTCHA point (as admin) |
---|
717 | $this->drupalLogin($this->admin_user); |
---|
718 | $captcha_point_form_id = 'form_' . strtolower($this->randomName(32)); |
---|
719 | $captcha_point_module = 'captcha'; |
---|
720 | $captcha_point_type = 'Math'; |
---|
721 | $form_values = array( |
---|
722 | 'captcha_point_form_id' => $captcha_point_form_id, |
---|
723 | 'captcha_type' => $captcha_point_module .'/'. $captcha_point_type, |
---|
724 | ); |
---|
725 | $this->drupalPost(self::CAPTCHA_ADMIN_PATH . '/captcha/captcha_point/', $form_values, t('Save')); |
---|
726 | $this->assertText(t('Saved CAPTCHA point settings.'), |
---|
727 | 'Saving of CAPTCHA point settings'); |
---|
728 | |
---|
729 | // Switch from admin to nonadmin |
---|
730 | $this->drupalGet(url('logout', array('absolute' => TRUE))); |
---|
731 | $this->drupalLogin($this->normal_user); |
---|
732 | |
---|
733 | |
---|
734 | // Try to set CAPTCHA point through admin/user/captcha/captcha/captcha_point |
---|
735 | $this->drupalGet(self::CAPTCHA_ADMIN_PATH . '/captcha/captcha_point'); |
---|
736 | $this->assertText(t('You are not authorized to access this page.'), |
---|
737 | 'Non admin should not be able to set a CAPTCHA point'); |
---|
738 | |
---|
739 | // Try to set CAPTCHA point through admin/user/captcha/captcha/captcha_point/$form_id |
---|
740 | $this->drupalGet(self::CAPTCHA_ADMIN_PATH . '/captcha/captcha_point/' . 'form_' . strtolower($this->randomName(32))); |
---|
741 | $this->assertText(t('You are not authorized to access this page.'), |
---|
742 | 'Non admin should not be able to set a CAPTCHA point'); |
---|
743 | |
---|
744 | // Try to disable the CAPTCHA point |
---|
745 | $this->drupalGet(self::CAPTCHA_ADMIN_PATH . '/captcha/captcha_point/'. $captcha_point_form_id .'/disable'); |
---|
746 | $this->assertText(t('You are not authorized to access this page.'), |
---|
747 | 'Non admin should not be able to disable a CAPTCHA point'); |
---|
748 | |
---|
749 | // Try to delete the CAPTCHA point |
---|
750 | $this->drupalGet(self::CAPTCHA_ADMIN_PATH . '/captcha/captcha_point/'. $captcha_point_form_id .'/delete'); |
---|
751 | $this->assertText(t('You are not authorized to access this page.'), |
---|
752 | 'Non admin should not be able to delete a CAPTCHA point'); |
---|
753 | |
---|
754 | // Switch from nonadmin to admin again |
---|
755 | $this->drupalGet(url('logout', array('absolute' => TRUE))); |
---|
756 | $this->drupalLogin($this->admin_user); |
---|
757 | |
---|
758 | // Check if original CAPTCHA point still exists in database |
---|
759 | $result = $this->getCaptchaPointSettingFromDatabase($captcha_point_form_id); |
---|
760 | $this->assertEqual($result->module, $captcha_point_module, |
---|
761 | 'Enabled CAPTCHA point should still have module set'); |
---|
762 | $this->assertEqual($result->captcha_type, $captcha_point_type, |
---|
763 | 'Enabled CAPTCHA point should still have type set'); |
---|
764 | |
---|
765 | // Delete CAPTCHA point |
---|
766 | $this->drupalPost(self::CAPTCHA_ADMIN_PATH . '/captcha/captcha_point/'. $captcha_point_form_id .'/delete', array(), t('Delete')); |
---|
767 | $this->assertRaw(t('Deleted CAPTCHA for form %form_id.', array('%form_id' => $captcha_point_form_id)), |
---|
768 | 'Deleting of CAPTCHA point'); |
---|
769 | } |
---|
770 | |
---|
771 | |
---|
772 | |
---|
773 | } |
---|
774 | |
---|
775 | |
---|
776 | |
---|
777 | class CaptchaPersistenceTestCase extends CaptchaBaseWebTestCase { |
---|
778 | |
---|
779 | public static function getInfo() { |
---|
780 | return array( |
---|
781 | 'name' => t('CAPTCHA persistence functionality'), |
---|
782 | 'description' => t('Testing of the CAPTCHA persistence functionality.'), |
---|
783 | 'group' => t('CAPTCHA'), |
---|
784 | ); |
---|
785 | } |
---|
786 | |
---|
787 | /** |
---|
788 | * Set up the persistence and CAPTCHA settings. |
---|
789 | * @param int $persistence the persistence value. |
---|
790 | */ |
---|
791 | private function setUpPersistence($persistence) { |
---|
792 | // Log in as admin |
---|
793 | $this->drupalLogin($this->admin_user); |
---|
794 | // Set persistence. |
---|
795 | $edit = array('captcha_persistence' => $persistence); |
---|
796 | $this->drupalPost(self::CAPTCHA_ADMIN_PATH, $edit, 'Save configuration'); |
---|
797 | // Log admin out. |
---|
798 | $this->drupalLogout(); |
---|
799 | |
---|
800 | // Set the Test123 CAPTCHA on user register and comment form. |
---|
801 | // We have to do this with the function captcha_set_form_id_setting() |
---|
802 | // (because the CATCHA admin form does not show the Test123 option). |
---|
803 | // We also have to do this after all usage of the CAPTCHA admin form |
---|
804 | // (because posting the CAPTCHA admin form would set the CAPTCHA to 'none'). |
---|
805 | captcha_set_form_id_setting('user_login', 'captcha/Test'); |
---|
806 | $this->drupalGet('user'); |
---|
807 | $this->assertCaptchaPresence(TRUE); |
---|
808 | captcha_set_form_id_setting('user_register', 'captcha/Test'); |
---|
809 | $this->drupalGet('user/register'); |
---|
810 | $this->assertCaptchaPresence(TRUE); |
---|
811 | } |
---|
812 | |
---|
813 | protected function assertPreservedCsid($captcha_sid_initial) { |
---|
814 | $captcha_sid = $this->getCaptchaSidFromForm(); |
---|
815 | $this->assertEqual($captcha_sid_initial, $captcha_sid, |
---|
816 | "CAPTCHA session ID should be preserved (expected: $captcha_sid_initial, found: $captcha_sid)."); |
---|
817 | } |
---|
818 | |
---|
819 | protected function assertDifferentCsid($captcha_sid_initial) { |
---|
820 | $captcha_sid = $this->getCaptchaSidFromForm(); |
---|
821 | $this->assertNotEqual($captcha_sid_initial, $captcha_sid, |
---|
822 | "CAPTCHA session ID should be different."); |
---|
823 | } |
---|
824 | |
---|
825 | function testPersistenceAlways(){ |
---|
826 | // Set up of persistence and CAPTCHAs. |
---|
827 | $this->setUpPersistence(CAPTCHA_PERSISTENCE_SHOW_ALWAYS); |
---|
828 | |
---|
829 | // Go to login form and check if there is a CAPTCHA on the login form (look for the title). |
---|
830 | $this->drupalGet('user'); |
---|
831 | $this->assertCaptchaPresence(TRUE); |
---|
832 | $captcha_sid_initial = $this->getCaptchaSidFromForm(); |
---|
833 | |
---|
834 | // Try to with wrong user name and password, but correct CAPTCHA. |
---|
835 | $edit = array( |
---|
836 | 'name' => 'foobar', |
---|
837 | 'pass' => 'bazlaz', |
---|
838 | 'captcha_response' => 'Test 123', |
---|
839 | ); |
---|
840 | $this->drupalPost(NULL, $edit, t('Log in')); |
---|
841 | // Check that there was no error message for the CAPTCHA. |
---|
842 | $this->assertCaptchaResponseAccepted(); |
---|
843 | |
---|
844 | // Name and password were wrong, we should get an updated form with a fresh CAPTCHA. |
---|
845 | $this->assertCaptchaPresence(TRUE); |
---|
846 | $this->assertPreservedCsid($captcha_sid_initial); |
---|
847 | |
---|
848 | // Post from again. |
---|
849 | $this->drupalPost(NULL, $edit, t('Log in')); |
---|
850 | // Check that there was no error message for the CAPTCHA. |
---|
851 | $this->assertCaptchaResponseAccepted(); |
---|
852 | $this->assertPreservedCsid($captcha_sid_initial); |
---|
853 | |
---|
854 | } |
---|
855 | |
---|
856 | function testPersistencePerFormInstance(){ |
---|
857 | // Set up of persistence and CAPTCHAs. |
---|
858 | $this->setUpPersistence(CAPTCHA_PERSISTENCE_SKIP_ONCE_SUCCESSFUL_PER_FORM_INSTANCE); |
---|
859 | |
---|
860 | // Go to login form and check if there is a CAPTCHA on the login form. |
---|
861 | $this->drupalGet('user'); |
---|
862 | $this->assertCaptchaPresence(TRUE); |
---|
863 | $captcha_sid_initial = $this->getCaptchaSidFromForm(); |
---|
864 | |
---|
865 | // Try to with wrong user name and password, but correct CAPTCHA. |
---|
866 | $edit = array( |
---|
867 | 'name' => 'foobar', |
---|
868 | 'pass' => 'bazlaz', |
---|
869 | 'captcha_response' => 'Test 123', |
---|
870 | ); |
---|
871 | $this->drupalPost(NULL, $edit, t('Log in')); |
---|
872 | // Check that there was no error message for the CAPTCHA. |
---|
873 | $this->assertCaptchaResponseAccepted(); |
---|
874 | // There shouldn't be a CAPTCHA on the new form. |
---|
875 | $this->assertCaptchaPresence(FALSE); |
---|
876 | $this->assertPreservedCsid($captcha_sid_initial); |
---|
877 | |
---|
878 | // Start a new form instance/session |
---|
879 | $this->drupalGet('node'); |
---|
880 | $this->drupalGet('user'); |
---|
881 | $this->assertCaptchaPresence(TRUE); |
---|
882 | $this->assertDifferentCsid($captcha_sid_initial); |
---|
883 | |
---|
884 | // Check another form |
---|
885 | $this->drupalGet('user/register'); |
---|
886 | $this->assertCaptchaPresence(TRUE); |
---|
887 | $this->assertDifferentCsid($captcha_sid_initial); |
---|
888 | |
---|
889 | } |
---|
890 | |
---|
891 | function testPersistencePerFormType(){ |
---|
892 | // Set up of persistence and CAPTCHAs. |
---|
893 | $this->setUpPersistence(CAPTCHA_PERSISTENCE_SKIP_ONCE_SUCCESSFUL_PER_FORM_TYPE); |
---|
894 | |
---|
895 | // Go to login form and check if there is a CAPTCHA on the login form. |
---|
896 | $this->drupalGet('user'); |
---|
897 | $this->assertCaptchaPresence(TRUE); |
---|
898 | $captcha_sid_initial = $this->getCaptchaSidFromForm(); |
---|
899 | |
---|
900 | // Try to with wrong user name and password, but correct CAPTCHA. |
---|
901 | $edit = array( |
---|
902 | 'name' => 'foobar', |
---|
903 | 'pass' => 'bazlaz', |
---|
904 | 'captcha_response' => 'Test 123', |
---|
905 | ); |
---|
906 | $this->drupalPost(NULL, $edit, t('Log in')); |
---|
907 | // Check that there was no error message for the CAPTCHA. |
---|
908 | $this->assertCaptchaResponseAccepted(); |
---|
909 | // There shouldn't be a CAPTCHA on the new form. |
---|
910 | $this->assertCaptchaPresence(FALSE); |
---|
911 | $this->assertPreservedCsid($captcha_sid_initial); |
---|
912 | |
---|
913 | // Start a new form instance/session |
---|
914 | $this->drupalGet('node'); |
---|
915 | $this->drupalGet('user'); |
---|
916 | $this->assertCaptchaPresence(FALSE); |
---|
917 | $this->assertDifferentCsid($captcha_sid_initial); |
---|
918 | |
---|
919 | // Check another form |
---|
920 | $this->drupalGet('user/register'); |
---|
921 | $this->assertCaptchaPresence(TRUE); |
---|
922 | $this->assertDifferentCsid($captcha_sid_initial); |
---|
923 | } |
---|
924 | |
---|
925 | function testPersistenceOnlyOnce(){ |
---|
926 | // Set up of persistence and CAPTCHAs. |
---|
927 | $this->setUpPersistence(CAPTCHA_PERSISTENCE_SKIP_ONCE_SUCCESSFUL); |
---|
928 | |
---|
929 | // Go to login form and check if there is a CAPTCHA on the login form. |
---|
930 | $this->drupalGet('user'); |
---|
931 | $this->assertCaptchaPresence(TRUE); |
---|
932 | $captcha_sid_initial = $this->getCaptchaSidFromForm(); |
---|
933 | |
---|
934 | // Try to with wrong user name and password, but correct CAPTCHA. |
---|
935 | $edit = array( |
---|
936 | 'name' => 'foobar', |
---|
937 | 'pass' => 'bazlaz', |
---|
938 | 'captcha_response' => 'Test 123', |
---|
939 | ); |
---|
940 | $this->drupalPost(NULL, $edit, t('Log in')); |
---|
941 | // Check that there was no error message for the CAPTCHA. |
---|
942 | $this->assertCaptchaResponseAccepted(); |
---|
943 | // There shouldn't be a CAPTCHA on the new form. |
---|
944 | $this->assertCaptchaPresence(FALSE); |
---|
945 | $this->assertPreservedCsid($captcha_sid_initial); |
---|
946 | |
---|
947 | // Start a new form instance/session |
---|
948 | $this->drupalGet('node'); |
---|
949 | $this->drupalGet('user'); |
---|
950 | $this->assertCaptchaPresence(FALSE); |
---|
951 | $this->assertDifferentCsid($captcha_sid_initial); |
---|
952 | |
---|
953 | // Check another form |
---|
954 | $this->drupalGet('user/register'); |
---|
955 | $this->assertCaptchaPresence(FALSE); |
---|
956 | $this->assertDifferentCsid($captcha_sid_initial); |
---|
957 | } |
---|
958 | |
---|
959 | } |
---|
960 | |
---|
961 | |
---|
962 | class CaptchaSessionReuseAttackTestCase extends CaptchaBaseWebTestCase { |
---|
963 | |
---|
964 | public static function getInfo() { |
---|
965 | return array( |
---|
966 | 'name' => t('CAPTCHA session reuse attack tests'), |
---|
967 | 'description' => t('Testing of the protection against CAPTCHA session reuse attacks.'), |
---|
968 | 'group' => t('CAPTCHA'), |
---|
969 | ); |
---|
970 | } |
---|
971 | |
---|
972 | /** |
---|
973 | * Assert that the CAPTCHA session ID reuse attack was detected. |
---|
974 | */ |
---|
975 | protected function assertCaptchaSessionIdReuseAttackDetection() { |
---|
976 | $this->assertText(t(CAPTCHA_SESSION_REUSE_ATTACK_ERROR_MESSAGE), |
---|
977 | 'CAPTCHA session ID reuse attack should be detected.', |
---|
978 | 'CAPTCHA'); |
---|
979 | // There should be an error message about wrong response. |
---|
980 | $this->assertText(t(CAPTCHA_WRONG_RESPONSE_ERROR_MESSAGE), |
---|
981 | 'CAPTCHA response should flagged as wrong.', |
---|
982 | 'CAPTCHA'); |
---|
983 | } |
---|
984 | |
---|
985 | function testCaptchaSessionReuseAttackDetectionOnCommentPreview() { |
---|
986 | // Create commentable node |
---|
987 | $node = $this->createNodeWithCommentsEnabled(); |
---|
988 | // Set Test CAPTCHA on comment form. |
---|
989 | captcha_set_form_id_setting(self::COMMENT_FORM_ID, 'captcha/Math'); |
---|
990 | variable_set('captcha_persistence', CAPTCHA_PERSISTENCE_SKIP_ONCE_SUCCESSFUL_PER_FORM_INSTANCE); |
---|
991 | |
---|
992 | // Log in as normal user. |
---|
993 | $this->drupalLogin($this->normal_user); |
---|
994 | |
---|
995 | // Go to comment form of commentable node. |
---|
996 | $this->drupalGet('comment/reply/' . $node->nid); |
---|
997 | $this->assertCaptchaPresence(TRUE); |
---|
998 | |
---|
999 | // Get CAPTCHA session ID and solution of the challenge. |
---|
1000 | $captcha_sid = $this->getCaptchaSidFromForm(); |
---|
1001 | $captcha_token = $this->getCaptchaTokenFromForm(); |
---|
1002 | $solution = $this->getMathCaptchaSolutionFromForm(); |
---|
1003 | |
---|
1004 | // Post the form with the solution. |
---|
1005 | $edit = $this->getCommentFormValues(); |
---|
1006 | $edit['captcha_response'] = $solution; |
---|
1007 | $this->drupalPost(NULL, $edit, t('Preview')); |
---|
1008 | // Answer should be accepted and further CAPTCHA ommitted. |
---|
1009 | $this->assertCaptchaResponseAccepted(); |
---|
1010 | $this->assertCaptchaPresence(FALSE); |
---|
1011 | |
---|
1012 | // Post a new comment, reusing the previous CAPTCHA session. |
---|
1013 | $edit = $this->getCommentFormValues(); |
---|
1014 | $edit['captcha_sid'] = $captcha_sid; |
---|
1015 | $edit['captcha_token'] = $captcha_token; |
---|
1016 | $edit['captcha_response'] = $solution; |
---|
1017 | $this->drupalPost('comment/reply/' . $node->nid, $edit, t('Preview')); |
---|
1018 | // CAPTCHA session reuse attack should be detected. |
---|
1019 | $this->assertCaptchaSessionIdReuseAttackDetection(); |
---|
1020 | // There should be a CAPTCHA. |
---|
1021 | $this->assertCaptchaPresence(TRUE); |
---|
1022 | |
---|
1023 | } |
---|
1024 | |
---|
1025 | function testCaptchaSessionReuseAttackDetectionOnNodeForm() { |
---|
1026 | // Set CAPTCHA on page form. |
---|
1027 | captcha_set_form_id_setting('page_node_form', 'captcha/Math'); |
---|
1028 | variable_set('captcha_persistence', CAPTCHA_PERSISTENCE_SKIP_ONCE_SUCCESSFUL_PER_FORM_INSTANCE); |
---|
1029 | |
---|
1030 | // Log in as normal user. |
---|
1031 | $this->drupalLogin($this->normal_user); |
---|
1032 | |
---|
1033 | // Go to node add form. |
---|
1034 | $this->drupalGet('node/add/page'); |
---|
1035 | $this->assertCaptchaPresence(TRUE); |
---|
1036 | |
---|
1037 | // Get CAPTCHA session ID and solution of the challenge. |
---|
1038 | $captcha_sid = $this->getCaptchaSidFromForm(); |
---|
1039 | $captcha_token = $this->getCaptchaTokenFromForm(); |
---|
1040 | $solution = $this->getMathCaptchaSolutionFromForm(); |
---|
1041 | |
---|
1042 | // Page settings to post, with correct CAPTCHA answer. |
---|
1043 | $edit = $this->getNodeFormValues(); |
---|
1044 | $edit['captcha_response'] = $solution; |
---|
1045 | // Preview the node |
---|
1046 | $this->drupalPost(NULL, $edit, t('Preview')); |
---|
1047 | // Answer should be accepted. |
---|
1048 | $this->assertCaptchaResponseAccepted(); |
---|
1049 | // Check that there is no CAPTCHA after preview. |
---|
1050 | $this->assertCaptchaPresence(FALSE); |
---|
1051 | |
---|
1052 | // Post a new comment, reusing the previous CAPTCHA session. |
---|
1053 | $edit = $this->getNodeFormValues(); |
---|
1054 | $edit['captcha_sid'] = $captcha_sid; |
---|
1055 | $edit['captcha_token'] = $captcha_token; |
---|
1056 | $edit['captcha_response'] = $solution; |
---|
1057 | $this->drupalPost('node/add/page', $edit, t('Preview')); |
---|
1058 | // CAPTCHA session reuse attack should be detected. |
---|
1059 | $this->assertCaptchaSessionIdReuseAttackDetection(); |
---|
1060 | // There should be a CAPTCHA. |
---|
1061 | $this->assertCaptchaPresence(TRUE); |
---|
1062 | |
---|
1063 | } |
---|
1064 | |
---|
1065 | function testCaptchaSessionReuseAttackDetectionOnLoginForm() { |
---|
1066 | // Set CAPTCHA on login form. |
---|
1067 | captcha_set_form_id_setting('user_login', 'captcha/Math'); |
---|
1068 | variable_set('captcha_persistence', CAPTCHA_PERSISTENCE_SKIP_ONCE_SUCCESSFUL_PER_FORM_INSTANCE); |
---|
1069 | |
---|
1070 | // Go to log in form. |
---|
1071 | $this->drupalGet('user'); |
---|
1072 | $this->assertCaptchaPresence(TRUE); |
---|
1073 | |
---|
1074 | // Get CAPTCHA session ID and solution of the challenge. |
---|
1075 | $captcha_sid = $this->getCaptchaSidFromForm(); |
---|
1076 | $captcha_token = $this->getCaptchaTokenFromForm(); |
---|
1077 | $solution = $this->getMathCaptchaSolutionFromForm(); |
---|
1078 | |
---|
1079 | // Log in through form. |
---|
1080 | $edit = array( |
---|
1081 | 'name' => $this->normal_user->name, |
---|
1082 | 'pass' => $this->normal_user->pass_raw, |
---|
1083 | 'captcha_response' => $solution, |
---|
1084 | ); |
---|
1085 | $this->drupalPost(NULL, $edit, t('Log in')); |
---|
1086 | $this->assertCaptchaResponseAccepted(); |
---|
1087 | $this->assertCaptchaPresence(FALSE); |
---|
1088 | // If a "log out" link appears on the page, it is almost certainly because |
---|
1089 | // the login was successful. |
---|
1090 | $pass = $this->assertLink(t('Log out'), 0, t('User %name successfully logged in.', array('%name' => $this->normal_user->name)), t('User login')); |
---|
1091 | |
---|
1092 | // Log out again. |
---|
1093 | $this->drupalLogout(); |
---|
1094 | |
---|
1095 | // Try to log in again, reusing the previous CAPTCHA session. |
---|
1096 | $edit += array( |
---|
1097 | 'captcha_sid' => $captcha_sid, |
---|
1098 | 'captcha_token' => $captcha_token, |
---|
1099 | ); |
---|
1100 | $this->drupalPost('user', $edit, t('Log in')); |
---|
1101 | // CAPTCHA session reuse attack should be detected. |
---|
1102 | $this->assertCaptchaSessionIdReuseAttackDetection(); |
---|
1103 | // There should be a CAPTCHA. |
---|
1104 | $this->assertCaptchaPresence(TRUE); |
---|
1105 | } |
---|
1106 | |
---|
1107 | |
---|
1108 | public function testMultipleCaptchaProtectedFormsOnOnePage() |
---|
1109 | { |
---|
1110 | // Set Test CAPTCHA on comment form and login block |
---|
1111 | captcha_set_form_id_setting(self::COMMENT_FORM_ID, 'captcha/Test'); |
---|
1112 | captcha_set_form_id_setting('user_login_block', 'captcha/Math'); |
---|
1113 | $this->allowCommentPostingForAnonymousVisitors(); |
---|
1114 | |
---|
1115 | // Create a node with comments enabled. |
---|
1116 | $node = $this->createNodeWithCommentsEnabled(); |
---|
1117 | |
---|
1118 | // Preview comment with correct CAPTCHA answer. |
---|
1119 | $edit = $this->getCommentFormValues(); |
---|
1120 | $comment_subject = $edit['subject']; |
---|
1121 | $edit['captcha_response'] = 'Test 123'; |
---|
1122 | $this->drupalPost('comment/reply/' . $node->nid, $edit, t('Preview')); |
---|
1123 | // Post should be accepted: no warnings, |
---|
1124 | // no CAPTCHA reuse detection (which could be used by user log in block). |
---|
1125 | $this->assertCaptchaResponseAccepted(); |
---|
1126 | $this->assertText($comment_subject); |
---|
1127 | |
---|
1128 | } |
---|
1129 | |
---|
1130 | } |
---|
1131 | |
---|
1132 | |
---|
1133 | // Some tricks to debug: |
---|
1134 | // drupal_debug($data) // from devel module |
---|
1135 | // file_put_contents('tmp.simpletest.html', $this->drupalGetContent()); |
---|