Add very simple CAPTCHA mechanism

This commit is contained in:
Florine W. Dekker 2022-12-18 23:19:45 +01:00
parent f6dea3e283
commit d0e05cc955
Signed by: FWDekker
GPG Key ID: D3DCFAA8A4560BE0
9 changed files with 96 additions and 4 deletions

View File

@ -1,7 +1,7 @@
{
"name": "fwdekker/death-notifier",
"description": "Get notified when a famous person dies.",
"version": "0.19.15", "_comment_version": "Also update version in `package.json`!",
"version": "0.19.16", "_comment_version": "Also update version in `package.json`!",
"type": "project",
"license": "MIT",
"homepage": "https://git.fwdekker.com/tools/death-notifier",

BIN
composer.lock generated

Binary file not shown.

BIN
package-lock.json generated

Binary file not shown.

View File

@ -1,6 +1,6 @@
{
"name": "death-notifier",
"version": "0.19.15", "_comment_version": "Also update version in `composer.json`!",
"version": "0.19.16", "_comment_version": "Also update version in `composer.json`!",
"description": "Get notified when a famous person dies.",
"author": "Florine W. Dekker",
"browser": "dist/bundle.js",

View File

@ -148,6 +148,11 @@
<label for="register-password-toggle">Show password</label>
<br /><br />
<label for="register-captcha">Write the word "pride"</label>
<input id="register-captcha" name="captcha" />
<small id="register-captcha-hint" data-hint-for="register-captcha"
data-hint="To prove that you are probably not a robot."></small>
<button id="register-submit">Create account</button>
</form>
</div>
@ -349,7 +354,6 @@
<label for="update-password-password-new">New password</label>
<input id="update-password-password-new" type="password" name="password_new"
autocomplete="new-password" />
<!-- TODO: Add password strength indicator -->
<small id="update-password-password-new-hint" data-hint-for="update-password-password-new"
data-hint="Use at least 8 characters."></small>

View File

@ -350,7 +350,8 @@ doAfterLoad(() => {
action: "register",
token: csrfToken,
email: $("#register-email").value,
password: $("#register-password").value
password: $("#register-password").value,
captcha: $("#register-captcha").value,
},
registerForm,
() => {

View File

@ -6,6 +6,7 @@ use com\fwdekker\deathnotifier\Action;
use com\fwdekker\deathnotifier\Config;
use com\fwdekker\deathnotifier\mailer\Email;
use com\fwdekker\deathnotifier\mailer\EmailQueue;
use com\fwdekker\deathnotifier\validation\EqualsRule;
use com\fwdekker\deathnotifier\validation\HasStringLengthRule;
use com\fwdekker\deathnotifier\validation\InvalidTypeException;
use com\fwdekker\deathnotifier\validation\InvalidValueException;
@ -64,6 +65,10 @@ class RegisterAction extends Action
"token" => [new IsValidCsrfTokenRule()],
"email" => [new IsEmailRule()],
"password" => [new HasStringLengthRule(UserList::MIN_PASSWORD_LENGTH, UserList::MAX_PASSWORD_LENGTH)],
"captcha" => [new EqualsRule(
"pride",
"Enter the word \"pride\" to prove that you are probably not a robot."
)],
]))->check($inputs);
$this->user_list->transaction(function () use ($inputs) {

View File

@ -0,0 +1,57 @@
<?php
namespace com\fwdekker\deathnotifier\validation;
use com\fwdekker\deathnotifier\Config;
use com\fwdekker\deathnotifier\IllegalStateError;
/**
* Validates that the input equals a pre-defined string.
*/
class EqualsRule extends Rule
{
/**
* @var string the string that the input should equal
*/
private readonly string $expected;
/**
* @var string the message that is returned if the input is incorrect
*/
private readonly string $message_if_incorrect;
/**
* Constructs a new `EqualsRule`.
*
* @param string $expected the string that the input should equal
* @param string $message_if_incorrect the message that is returned if the input is incorrect
*/
public function __construct(string $expected, string $message_if_incorrect)
{
$this->expected = $expected;
$this->message_if_incorrect = $message_if_incorrect;
}
/**
* Validates that the input equals the CLI password.
*
* @param array<int|string, mixed> $inputs the list of inputs in which the value at {@see $key} should be checked
* @param string $key the key in {@see $inputs} of the input to check
* @return void if the checked input equals the CLI password
* @throws InvalidTypeException if the CLI password is a blank string, if the CLI password is at its default
* value, or if the checked input is not set
* @throws InvalidValueException if the checked input does not equal the CLI password
*/
public function check(array $inputs, string $key): void
{
if (!isset($inputs[$key]))
throw new InvalidTypeException("Required input '$key' not set.");
if (!is_string($inputs[$key]))
throw new InvalidTypeException("Input '$key' should be string, but is " . gettype($inputs[$key]) . ".");
if ($inputs[$key] !== $this->expected)
throw new InvalidValueException($this->message_if_incorrect, $key);
}
}

View File

@ -0,0 +1,25 @@
<?php
namespace com\fwdekker\deathnotifier\validation;
use InvalidArgumentException;
/**
* Unit tests for {@see EqualsRule}.
*/
class EqualsRuleTest extends RuleTest
{
public function check_provider(): array
{
$type = InvalidTypeException::class;
$value = InvalidValueException::class;
return [
"exception if input is not set" => [new EqualsRule("word", "message"), null, $type, "Required input 'key' not set."],
"exception if input is not a string" => [new EqualsRule("word", "message"), 738, $type, "Input 'key' should be string, but is integer."],
"exception with custom message if input is incorrect" => [new EqualsRule("word", "message"), "incorrect", $value, "message"],
"no exception if input equals expected phrase" => [new EqualsRule("word", "message"), "word", null, null],
];
}
}