Commit 2a7524c8 authored by wundo's avatar wundo Committed by wundo

Issue #3083792 by wundo, JeroenT: Convert simpletest to PHPUnit tests

parent 15cdeea1
......@@ -2,12 +2,258 @@
namespace Drupal\captcha\Tests;
use Drupal\comment\Plugin\Field\FieldType\CommentItemInterface;
use Drupal\comment\Tests\CommentTestTrait;
use Drupal\Core\Session\AccountInterface;
use Drupal\field\Entity\FieldConfig;
use Drupal\simpletest\WebTestBase;
/**
* Tests CAPTCHA session reusing.
*
* @group captcha
*/
class CaptchaSessionReuseAttackTestCase extends CaptchaBaseWebTestCase {
class CaptchaSessionReuseAttackTestCase extends WebTestBase {
use CommentTestTrait;
/**
* Wrong response error message.
*/
const CAPTCHA_WRONG_RESPONSE_ERROR_MESSAGE = 'The answer you entered for the CAPTCHA was not correct.';
/**
* Unknown CSID error message.
*/
const CAPTCHA_UNKNOWN_CSID_ERROR_MESSAGE = 'CAPTCHA validation error: unknown CAPTCHA session ID. Contact the site administrator if this problem persists.';
/**
* Modules to install for this Test class.
*
* @var array
*/
public static $modules = ['captcha', 'comment'];
/**
* User with various administrative permissions.
*
* @var \Drupal\user\Entity\User
*/
protected $adminUser;
/**
* Normal visitor with limited permissions.
*
* @var \Drupal\user\Entity\User
*/
protected $normalUser;
/**
* Form ID of comment form on standard (page) node.
*/
const COMMENT_FORM_ID = 'comment_comment_form';
const LOGIN_HTML_FORM_ID = 'user-login-form';
/**
* Drupal path of the (general) CAPTCHA admin page.
*/
const CAPTCHA_ADMIN_PATH = 'admin/config/people/captcha';
/**
* {@inheritdoc}
*/
public function setUp() {
// Load two modules: the captcha module itself and the comment
// module for testing anonymous comments.
parent::setUp();
module_load_include('inc', 'captcha');
$this->drupalCreateContentType(['type' => 'page']);
// Create a normal user.
$permissions = [
'access comments',
'post comments',
'skip comment approval',
'access content',
'create page content',
'edit own page content',
];
$this->normalUser = $this->drupalCreateUser($permissions);
// Create an admin user.
$permissions[] = 'administer CAPTCHA settings';
$permissions[] = 'skip CAPTCHA';
$permissions[] = 'administer permissions';
$permissions[] = 'administer content types';
$this->adminUser = $this->drupalCreateUser($permissions);
// Open comment for page content type.
$this->addDefaultCommentField('node', 'page');
// Put comments on page nodes on a separate page.
$comment_field = FieldConfig::loadByName('node', 'page', 'comment');
$comment_field->setSetting('form_location', CommentItemInterface::FORM_SEPARATE_PAGE);
$comment_field->save();
/* @var \Drupal\captcha\Entity\CaptchaPoint $captcha_point */
$captcha_point = \Drupal::entityTypeManager()
->getStorage('captcha_point')
->load('user_login_form');
$captcha_point->enable()->save();
$this->config('captcha.settings')
->set('default_challenge', 'captcha/test')
->save();
}
/**
* Assert that the response is accepted.
*
* No "unknown CSID" message, no "CSID reuse attack detection" message,
* No "wrong answer" message.
*/
protected function assertCaptchaResponseAccepted() {
// There should be no error message about unknown CAPTCHA session ID.
$this->assertNoText(self::CAPTCHA_UNKNOWN_CSID_ERROR_MESSAGE,
'CAPTCHA response should be accepted (known CSID).',
'CAPTCHA'
);
// There should be no error message about wrong response.
$this->assertNoText(self::CAPTCHA_WRONG_RESPONSE_ERROR_MESSAGE,
'CAPTCHA response should be accepted (correct response).',
'CAPTCHA'
);
}
/**
* Assert that there is a CAPTCHA on the form or not.
*
* @param bool $presence
* Whether there should be a CAPTCHA or not.
*/
protected function assertCaptchaPresence($presence) {
if ($presence) {
$this->assertText(_captcha_get_description(),
'There should be a CAPTCHA on the form.', 'CAPTCHA'
);
}
else {
$this->assertNoText(_captcha_get_description(),
'There should be no CAPTCHA on the form.', 'CAPTCHA'
);
}
}
/**
* Helper function to generate a form values array for comment forms.
*/
protected function getCommentFormValues() {
$edit = [
'subject[0][value]' => 'comment_subject ' . $this->randomMachineName(32),
'comment_body[0][value]' => 'comment_body ' . $this->randomMachineName(256),
];
return $edit;
}
/**
* Helper function to generate a form values array for node forms.
*/
protected function getNodeFormValues() {
$edit = [
'title[0][value]' => 'node_title ' . $this->randomMachineName(32),
'body[0][value]' => 'node_body ' . $this->randomMachineName(256),
];
return $edit;
}
/**
* Get the CAPTCHA session id from the current form in the browser.
*
* @param null|string $form_html_id
* HTML form id attribute.
*
* @return int
* Captcha SID integer.
*/
protected function getCaptchaSidFromForm($form_html_id = NULL) {
if (!$form_html_id) {
$elements = $this->xpath('//input[@name="captcha_sid"]');
}
else {
$elements = $this->xpath('//form[@id="' . $form_html_id . '"]//input[@name="captcha_sid"]');
}
$captcha_sid = (int) $elements[0]['value'];
return $captcha_sid;
}
/**
* Get the CAPTCHA token from the current form in the browser.
*
* @param null|string $form_html_id
* HTML form id attribute.
*
* @return int
* Captcha token integer.
*/
protected function getCaptchaTokenFromForm($form_html_id = NULL) {
if (!$form_html_id) {
$elements = $this->xpath('//input[@name="captcha_token"]');
}
else {
$elements = $this->xpath('//form[@id="' . $form_html_id . '"]//input[@name="captcha_token"]');
}
$captcha_token = (int) $elements[0]['value'];
return $captcha_token;
}
/**
* Get the solution of the math CAPTCHA from the current form in the browser.
*
* @param null|string $form_html_id
* HTML form id attribute.
*
* @return int
* Calculated Math solution.
*/
protected function getMathCaptchaSolutionFromForm($form_html_id = NULL) {
// Get the math challenge.
if (!$form_html_id) {
$elements = $this->xpath('//div[contains(@class, "form-item-captcha-response")]/span[@class="field-prefix"]');
}
else {
$elements = $this->xpath('//form[@id="' . $form_html_id . '"]//div[contains(@class, "form-item-captcha-response")]/span[@class="field-prefix"]');
}
$this->assert('pass', json_encode($elements));
$challenge = (string) $elements[0];
$this->assert('pass', $challenge);
// Extract terms and operator from challenge.
$matches = [];
preg_match('/\\s*(\\d+)\\s*(-|\\+)\\s*(\\d+)\\s*=\\s*/', $challenge, $matches);
// Solve the challenge.
$a = (int) $matches[1];
$b = (int) $matches[3];
$solution = $matches[2] == '-' ? $a - $b : $a + $b;
return $solution;
}
/**
* Helper function to allow comment posting for anonymous users.
*/
protected function allowCommentPostingForAnonymousVisitors() {
// Enable anonymous comments.
user_role_grant_permissions(AccountInterface::ANONYMOUS_ROLE, [
'access comments',
'post comments',
'skip comment approval',
]);
}
/**
* Assert that the CAPTCHA session ID reuse attack was detected.
......
<?php
namespace Drupal\captcha\Tests;
namespace Drupal\Tests\captcha\Functional;
use Drupal\captcha\Entity\CaptchaPoint;
use Drupal\Core\Url;
......@@ -10,7 +10,7 @@ use Drupal\Core\Url;
*
* @group captcha
*/
class CaptchaAdminTestCase extends CaptchaBaseWebTestCase {
class CaptchaAdminTest extends CaptchaWebTestBase {
/**
* Test access to the admin pages.
......
<?php
namespace Drupal\captcha\Tests;
namespace Drupal\Tests\captcha\Functional;
use Drupal\Tests\captcha\Functional\CaptchaWebTestBase;
/**
* Tests CAPTCHA caching on various pages.
*
* @group captcha
*/
class CaptchaCacheTestCase extends CaptchaBaseWebTestCase {
class CaptchaCacheTest extends CaptchaWebTestBase {
/**
* Modules to install for this Test class.
......@@ -40,11 +42,9 @@ class CaptchaCacheTestCase extends CaptchaBaseWebTestCase {
captcha_set_form_id_setting('user_login_form', 'captcha/Math');
$this->drupalGet('');
$sid = $this->getCaptchaSidFromForm();
$math_challenge = (string) $this->xpath('//span[@class="field-prefix"]')[0];
$this->assertFalse($this->drupalGetHeader('x-drupal-cache'), 'Cache is disabled');
$this->drupalGet('');
$this->assertNotEqual($sid, $this->getCaptchaSidFromForm());
$this->assertNotEqual($math_challenge, (string) $this->xpath('//span[@class="field-prefix"]')[0]);
// Switch challenge to captcha/Test, check the captcha isn't cached.
captcha_set_form_id_setting('user_login_form', 'captcha/Test');
......@@ -57,11 +57,11 @@ class CaptchaCacheTestCase extends CaptchaBaseWebTestCase {
// Switch challenge to image_captcha/Image, check the captcha isn't cached.
captcha_set_form_id_setting('user_login_form', 'image_captcha/Image');
$this->drupalGet('');
$image_path = (string) $this->xpath('//div[@class="details-wrapper"]/img/@src')[0];
$image_path = $this->xpath('//div[@class="details-wrapper"]/img')[0]->getAttribute('src');
$this->assertFalse($this->drupalGetHeader('x-drupal-cache'), 'Cache disabled');
// Check that we get a new image when vising the page again.
$this->drupalGet('');
$this->assertNotEqual($image_path, (string) $this->xpath('//div[@class="details-wrapper"]/img/@src')[0]);
$this->assertNotEqual($image_path, $this->xpath('//div[@class="details-wrapper"]/img')[0]->getAttribute('src'));
// Check image caching, remove the base path since drupalGet() expects the
// internal path.
$this->drupalGet(substr($image_path, strlen($base_path)));
......
<?php
namespace Drupal\captcha\Tests;
namespace Drupal\Tests\captcha\Functional;
use Drupal\Core\Database\Database;
use Drupal\simpletest\WebTestBase;
use Drupal\Tests\BrowserTestBase;
/**
* Tests CAPTCHA cron.
*
* @group captcha
*/
class CaptchaCronTestCase extends WebTestBase {
class CaptchaCronTest extends BrowserTestBase {
/**
* Modules to install for this Test class.
......@@ -78,7 +78,7 @@ class CaptchaCronTestCase extends WebTestBase {
* Test CAPTCHA cron.
*/
public function testCron() {
$this->cronRun();
\Drupal::service('cron')->run();
$connection = Database::getConnection();
$sids = $connection->select('captcha_sessions')
......@@ -88,10 +88,10 @@ class CaptchaCronTestCase extends WebTestBase {
->fetchCol('csid');
// Test if CAPTCHA cron appropriately removes sessions older than a day.
$this->assertTrue(!in_array($this->captchaSessions['remove_sid'], $sids), 'CAPTCHA cron removes captcha session data older than 1 day.');
$this->assertNotContains($this->captchaSessions['remove_sid'], $sids, 'CAPTCHA cron removes captcha session data older than 1 day.');
// Test if CAPTCHA cron appropriately keeps sessions younger than a day.
$this->assertTrue(in_array($this->captchaSessions['remain_sid'], $sids), 'CAPTCHA cron does not remove captcha session data younger than 1 day.');
$this->assertContains($this->captchaSessions['remain_sid'], $sids, 'CAPTCHA cron does not remove captcha session data younger than 1 day.');
}
}
<?php
namespace Drupal\captcha\Tests;
namespace Drupal\Tests\captcha\Functional;
use Drupal\Tests\captcha\Functional\CaptchaWebTestBase;
/**
* Tests CAPTCHA Persistence.
*
* @group captcha
*/
class CaptchaPersistenceTestCase extends CaptchaBaseWebTestCase {
class CaptchaPersistenceTest extends CaptchaWebTestBase {
/**
* Set up the persistence and CAPTCHA settings.
......@@ -78,7 +80,7 @@ class CaptchaPersistenceTestCase extends CaptchaBaseWebTestCase {
'pass' => 'bazlaz',
'captcha_response' => 'Test 123',
];
$this->drupalPostForm(NULL, $edit, t('Log in'), [], [], self::LOGIN_HTML_FORM_ID);
$this->drupalPostForm(NULL, $edit, t('Log in'), [], self::LOGIN_HTML_FORM_ID);
// Check that there was no error message for the CAPTCHA.
$this->assertCaptchaResponseAccepted();
......@@ -88,7 +90,7 @@ class CaptchaPersistenceTestCase extends CaptchaBaseWebTestCase {
$this->assertPreservedCsid($captcha_sid_initial);
// Post from again.
$this->drupalPostForm(NULL, $edit, t('Log in'), [], [], self::LOGIN_HTML_FORM_ID);
$this->drupalPostForm(NULL, $edit, t('Log in'), [], self::LOGIN_HTML_FORM_ID);
// Check that there was no error message for the CAPTCHA.
$this->assertCaptchaResponseAccepted();
$this->assertPreservedCsid($captcha_sid_initial);
......@@ -112,7 +114,7 @@ class CaptchaPersistenceTestCase extends CaptchaBaseWebTestCase {
'pass' => 'bazlaz',
'captcha_response' => 'Test 123',
];
$this->drupalPostForm(NULL, $edit, t('Log in'), [], [], self::LOGIN_HTML_FORM_ID);
$this->drupalPostForm(NULL, $edit, t('Log in'), [], self::LOGIN_HTML_FORM_ID);
// Check that there was no error message for the CAPTCHA.
$this->assertCaptchaResponseAccepted();
// There shouldn't be a CAPTCHA on the new form.
......@@ -149,7 +151,7 @@ class CaptchaPersistenceTestCase extends CaptchaBaseWebTestCase {
'pass' => 'bazlaz',
'captcha_response' => 'Test 123',
];
$this->drupalPostForm(NULL, $edit, t('Log in'), [], [], self::LOGIN_HTML_FORM_ID);
$this->drupalPostForm(NULL, $edit, t('Log in'), [], self::LOGIN_HTML_FORM_ID);
// Check that there was no error message for the CAPTCHA.
$this->assertCaptchaResponseAccepted();
// There shouldn't be a CAPTCHA on the new form.
......@@ -191,7 +193,7 @@ class CaptchaPersistenceTestCase extends CaptchaBaseWebTestCase {
'pass' => 'bazlaz',
'captcha_response' => 'Test 123',
];
$this->drupalPostForm(NULL, $edit, t('Log in'), [], [], self::LOGIN_HTML_FORM_ID);
$this->drupalPostForm(NULL, $edit, t('Log in'), [], self::LOGIN_HTML_FORM_ID);
// Check that there was no error message for the CAPTCHA.
$this->assertCaptchaResponseAccepted();
// There shouldn't be a CAPTCHA on the new form.
......
<?php
namespace Drupal\captcha\Tests;
namespace Drupal\Tests\captcha\Functional;
use Drupal\Core\Entity\Entity\EntityFormDisplay;
use Drupal\Core\Field\FieldStorageDefinitionInterface;
use Drupal\field\Entity\FieldConfig;
use Drupal\field\Entity\FieldStorageConfig;
use Drupal\Tests\captcha\Functional\CaptchaWebTestBase;
/**
* Tests CAPTCHA main test case sensitivity.
*
* @group captcha
*/
class CaptchaTestCase extends CaptchaBaseWebTestCase {
class CaptchaTest extends CaptchaWebTestBase {
/**
* Modules to enable.
......@@ -49,7 +50,7 @@ class CaptchaTestCase extends CaptchaBaseWebTestCase {
'pass' => $user->pass_raw,
'captcha_response' => '?',
];
$this->drupalPostForm(NULL, $edit, t('Log in'), [], [], self::LOGIN_HTML_FORM_ID);
$this->drupalPostForm(NULL, $edit, t('Log in'), [], self::LOGIN_HTML_FORM_ID);
// Check for error message.
$this->assertText(self::CAPTCHA_WRONG_RESPONSE_ERROR_MESSAGE, 'CAPTCHA should block user login form', 'CAPTCHA');
......@@ -88,7 +89,7 @@ class CaptchaTestCase extends CaptchaBaseWebTestCase {
$comment_subject = $edit['subject[0][value]'];
$comment_body = $edit['comment_body[0][value]'];
$edit['captcha_response'] = $captcha_response;
$this->drupalPostForm('comment/reply/node/' . $node->id() . '/comment', $edit, t('Save'), [], [], 'comment-form');
$this->drupalPostForm('comment/reply/node/' . $node->id() . '/comment', $edit, t('Save'), [], 'comment-form');
if ($should_pass) {
// There should be no error message.
......@@ -214,63 +215,6 @@ class CaptchaTestCase extends CaptchaBaseWebTestCase {
$this->assertCaptchaPresence(TRUE);
}
/**
* Tests that the CAPTCHA is not changed on AJAX form rebuilds.
*/
public function testAjaxFormRebuild() {
// Setup captcha point for user edit form.
\Drupal::entityTypeManager()->getStorage('captcha_point')->create([
'id' => 'user_form',
'formId' => 'user_form',
'status' => TRUE,
'captchaType' => 'captcha/Math',
])->save();
// Add multiple text field on user edit form.
$field_storage_config = FieldStorageConfig::create([
'field_name' => 'field_texts',
'type' => 'string',
'entity_type' => 'user',
'cardinality' => FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED,
]);
$field_storage_config->save();
FieldConfig::create([
'field_storage' => $field_storage_config,
'bundle' => 'user',
])->save();
$entity_form_display = EntityFormDisplay::load('user.user.default');
if (!$entity_form_display) {
$entity_form_display = EntityFormDisplay::create([
'targetEntityType' => 'user',
'bundle' => 'user',
'mode' => 'default',
'status' => TRUE,
]);
}
$entity_form_display
->setComponent('field_texts', [
'type' => 'string_textfield',
'weight' => 10,
])
->save();
// Create and login a user.
$user = $this->drupalCreateUser([]);
$this->drupalLogin($user);
// On edit form, add another item and save.
$this->drupalGet("user/{$user->id()}/edit");
$this->drupalPostAjaxForm(NULL, [], 'field_texts_add_more');
$this->drupalPostForm(NULL, [
'captcha_response' => $this->getMathCaptchaSolutionFromForm('user-form'),
], t('Save'));
// No error.
$this->assertText(t('The changes have been saved.'));
}
/**
* Test that forms with IDs exceeding 64 characters can be assigned captchas.
*/
......
<?php
namespace Drupal\captcha\Tests;
namespace Drupal\Tests\captcha\Functional;
use Drupal\comment\Plugin\Field\FieldType\CommentItemInterface;
use Drupal\comment\Tests\CommentTestTrait;
use Drupal\field\Entity\FieldConfig;
use Drupal\simpletest\WebTestBase;
use Drupal\Core\Session\AccountInterface;
use Drupal\field\Entity\FieldConfig;
use Drupal\Tests\BrowserTestBase;
/**
* The TODO list.
......@@ -26,7 +26,7 @@ use Drupal\Core\Session\AccountInterface;
*
* Provides common setup stuff and various helper functions.
*/
abstract class CaptchaBaseWebTestCase extends WebTestBase {
abstract class CaptchaWebTestBase extends BrowserTestBase {
use CommentTestTrait;
......@@ -40,6 +40,18 @@ abstract class CaptchaBaseWebTestCase extends WebTestBase {
*/
const CAPTCHA_UNKNOWN_CSID_ERROR_MESSAGE = 'CAPTCHA validation error: unknown CAPTCHA session ID. Contact the site administrator if this problem persists.';
/**
* Form ID of comment form on standard (page) node.
*/
const COMMENT_FORM_ID = 'comment_comment_form';
const LOGIN_HTML_FORM_ID = 'user-login-form';
/**
* Drupal path of the (general) CAPTCHA admin page.
*/
const CAPTCHA_ADMIN_PATH = 'admin/config/people/captcha';
/**
* Modules to install for this Test class.
*
......@@ -47,7 +59,6 @@ abstract class CaptchaBaseWebTestCase extends WebTestBase {
*/
public static $modules = ['captcha', 'comment'];
/**
* User with various administrative permissions.
*
......@@ -62,18 +73,6 @@ abstract class CaptchaBaseWebTestCase extends WebTestBase {
*/
protected $normalUser;
/**
* Form ID of comment form on standard (page) node.
*/
const COMMENT_FORM_ID = 'comment_comment_form';
const LOGIN_HTML_FORM_ID = 'user-login-form';
/**
* Drupal path of the (general) CAPTCHA admin page.
*/
const CAPTCHA_ADMIN_PATH = 'admin/config/people/captcha';
/**
* {@inheritdoc}
*/
......@@ -192,14 +191,16 @@ abstract class CaptchaBaseWebTestCase extends WebTestBase {
* @return int
* Captcha SID integer.
*/
protected function getCaptchaSidFromForm($form_html_id = NULL) {
protected function getCaptchaSidFromForm($form_html_id = NULL) {
if (!$form_html_id) {
$elements = $this->xpath('//input[@name="captcha_sid"]');
}
else {
$elements = $this->xpath('//form[@id="' . $form_html_id . '"]//input[@name="captcha_sid"]');
}
$captcha_sid = (int) $elements[0]['value'];
$element = current($elements);
$captcha_sid = (int) $element->getValue();
return $captcha_sid;
}
......@@ -220,7 +221,8 @@ abstract class CaptchaBaseWebTestCase extends WebTestBase {
else {
$elements = $this->xpath('//form[@id="' . $form_html_id . '"]//input[@name="captcha_token"]');
}
$captcha_token = (int) $elements[0]['value'];
$element = current($elements);
$captcha_token = (int) $element->getValue();
return $captcha_token;
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment