From 6349624e3486845d4ff1f596dd3f223a0f2faf76 Mon Sep 17 00:00:00 2001 From: Njogu Amos <29255728+njoguamos@users.noreply.github.com> Date: Sun, 11 Feb 2024 19:43:35 +0300 Subject: [PATCH] Add turnstile validation rule --- .github/workflows/run-tests.yml | 2 +- README.md | 23 ++++++++++++++++++++--- resources/lang/en/turnstile.php | 2 +- src/Rules/TurnstileRule.php | 19 +++++++++++++++++++ src/TurnstileServiceProvider.php | 1 + tests/Pest.php | 21 +++++++++++++++++++++ tests/Rules/TurnstileRuleTest.php | 30 ++++++++++++++++++++++++++++++ 7 files changed, 93 insertions(+), 5 deletions(-) create mode 100644 src/Rules/TurnstileRule.php create mode 100644 tests/Rules/TurnstileRuleTest.php diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 7a96095..6fee013 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -50,4 +50,4 @@ jobs: run: composer show -D - name: Execute tests - run: vendor/bin/pest --verbose + run: vendor/bin/pest diff --git a/README.md b/README.md index d3b2255..393b67c 100644 --- a/README.md +++ b/README.md @@ -73,9 +73,26 @@ Ensure your client side technology submit a turnstile token using a name defined Upon submitting the form, the turnstile token will be validated against turnstile api. If it fails, the request will be redirected back with `status` message. You can handle this message however you want in client side. -### 2. As a validation rule -```text -@TODO: Working on it +### 2. As a validation rule (v2.x.x) + +You can user the inbuilt validation to validate form input + +```php +use NjoguAmos\Turnstile\Rules\TurnstileRule; + +class RegisterRequest extends FormRequest +{ + /** @return array */ + public function rules(): array + { + return [ + # Other fields + 'token' => ['required', new TurnstileRule() ], + ]; + } + + # Other code +} ``` ### 3. Manual diff --git a/resources/lang/en/turnstile.php b/resources/lang/en/turnstile.php index e4d7d9f..9718642 100644 --- a/resources/lang/en/turnstile.php +++ b/resources/lang/en/turnstile.php @@ -1,6 +1,6 @@ 'Turnstile verification failed. Refresh the page and try again.', + 'failed' => "We couldn't verify if you're human. Please refresh the page and try again.", 'no_secret_key' => 'Secret key was not found. Please add the key to your services file.', ]; diff --git a/src/Rules/TurnstileRule.php b/src/Rules/TurnstileRule.php new file mode 100644 index 0000000..da6ee9c --- /dev/null +++ b/src/Rules/TurnstileRule.php @@ -0,0 +1,19 @@ +validate(token: $value)) { + $fail(trans(key: 'turnstile::turnstile.failed')); + } + } +} diff --git a/src/TurnstileServiceProvider.php b/src/TurnstileServiceProvider.php index fcede62..96cd170 100644 --- a/src/TurnstileServiceProvider.php +++ b/src/TurnstileServiceProvider.php @@ -19,6 +19,7 @@ public function configurePackage(Package $package): void ->name(name: 'turnstile') ->hasTranslations() ->hasConfigFile(configFileName: 'turnstile') + ->hasTranslations() ->hasInstallCommand(callable: function (InstallCommand $command) { $command ->startWith(callable: function (InstallCommand $command) { diff --git a/tests/Pest.php b/tests/Pest.php index 96ed0e5..ef8e82d 100644 --- a/tests/Pest.php +++ b/tests/Pest.php @@ -1,5 +1,6 @@ set(key: 'turnstile.secretkey', value: $key); } + + +expect() + ->extend(name: 'toPassWith', extend: function (mixed $value) { + $rule = $this->value; + + if (!$rule instanceof ValidationRule) { + throw new Exception(message: 'Value is not an invokable rule'); + } + + $passed = true; + + $fail = function () use (&$passed) { + $passed = false; + }; + + $rule->validate(attribute: 'attribute', value: $value, fail: $fail); + + expect(value: $passed)->toBeTrue(); + }); diff --git a/tests/Rules/TurnstileRuleTest.php b/tests/Rules/TurnstileRuleTest.php new file mode 100644 index 0000000..2583c5a --- /dev/null +++ b/tests/Rules/TurnstileRuleTest.php @@ -0,0 +1,30 @@ +rule = new TurnstileRule(); +}); + +it(description: 'passes with a valid token', closure: function () { + // Set a key that always passes + setSecretKey(type: 'valid'); + + expect(value:$this->rule)->toPassWith('DUMMY.TOKEN'); +}); + +it(description: 'fails with an valid token', closure: function () { + // Set a key that always passes + setSecretKey(type: 'invalid'); + + expect(value:$this->rule)->not->toPassWith('DUMMY.TOKEN'); +}); + +it(description: 'fails with a spent valid token', closure: function () { + // Set a key that always passes + setSecretKey(type: 'spent'); + + expect(value:$this->rule)->not->toPassWith('DUMMY.TOKEN'); +});