Skip to content

Commit

Permalink
fix: don't apply recaptcha requirements when it's not configured
Browse files Browse the repository at this point in the history
Fixes #47
  • Loading branch information
davwheat committed Feb 20, 2024
1 parent 841ebc0 commit e6ad39b
Show file tree
Hide file tree
Showing 14 changed files with 349 additions and 247 deletions.
14 changes: 6 additions & 8 deletions extend.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,14 @@

return [
(new Extend\Frontend('forum'))
->js(__DIR__.'/js/dist/forum.js')
->css(__DIR__.'/resources/less/forum.less'),
->js(__DIR__ . '/js/dist/forum.js')
->css(__DIR__ . '/resources/less/forum.less'),

(new Extend\Frontend('admin'))
->js(__DIR__.'/js/dist/admin.js')
->css(__DIR__.'/resources/less/admin.less'),
->js(__DIR__ . '/js/dist/admin.js')
->css(__DIR__ . '/resources/less/admin.less'),

new Extend\Locales(__DIR__.'/resources/locale'),
new Extend\Locales(__DIR__ . '/resources/locale'),

(new Extend\Settings())
->default('fof-recaptcha.signup', true)
Expand All @@ -47,9 +47,7 @@
->post('/fof/recaptcha/test', 'fof-recaptcha.test', Api\Controller\TestReCaptchaController::class),

(new Extend\ApiSerializer(ForumSerializer::class))
->attribute('postWithoutCaptcha', function (ForumSerializer $serializer) {
return $serializer->getActor()->hasPermission('fof-recaptcha.postWithoutCaptcha');
}),
->attributes(ForumAttributes::class),

(new Extend\Validator(RecaptchaValidator::class))
->configure(AddValidatorRule::class),
Expand Down
434 changes: 224 additions & 210 deletions js/package-lock.json

Large diffs are not rendered by default.

8 changes: 4 additions & 4 deletions js/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@
"@flarum/prettier-config": "^1.0.0",
"external-load": "^1.0.0",
"flarum-tsconfig": "^1.0.2",
"flarum-webpack-config": "^2.0.0",
"webpack": "^5.80.0",
"webpack-cli": "^5.0.1"
"flarum-webpack-config": "^2.0.2",
"webpack": "^5.90.3",
"webpack-cli": "^5.1.4"
},
"devDependencies": {
"prettier": "^3.0.3"
"prettier": "^3.2.5"
},
"scripts": {
"dev": "webpack --mode development --watch",
Expand Down
7 changes: 7 additions & 0 deletions js/src/@types/shims.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export * from 'flarum/forum/ForumApplication';

declare module 'flarum/forum/ForumApplication' {
export default interface ForumApplication {
recaptchaLoaded: boolean;
}
}
6 changes: 5 additions & 1 deletion js/src/forum/extendAuthModals.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export const addRecaptchaToAuthModal = <T extends typeof ForgotPasswordModal | t
const isEnabled = () => !!app.forum.attribute(`fof-recaptcha.${type}`);

extend(modal.prototype, 'oninit', function () {
if (!app.forum.attribute('fof-recaptcha.configured')) return;
if (!isEnabled()) return;

this.recaptcha = new RecaptchaState(
Expand All @@ -40,18 +41,21 @@ export const addRecaptchaToAuthModal = <T extends typeof ForgotPasswordModal | t
});

extend(modal.prototype, dataMethod, function (data) {
if (!app.forum.attribute('fof-recaptcha.configured')) return;
if (!isEnabled()) return;

data['g-recaptcha-response'] = this.recaptcha.getResponse();
});

extend(modal.prototype, 'fields', function (fields) {
if (!app.forum.attribute('fof-recaptcha.configured')) return;
if (!isEnabled()) return;

fields.add('recaptcha', <Recaptcha state={this.recaptcha} />, -5);
});

extend(modal.prototype, 'onerror', function (_, error) {
if (!app.forum.attribute('fof-recaptcha.configured')) return;
if (!isEnabled()) return;

this.recaptcha.reset();
Expand All @@ -63,7 +67,7 @@ export const addRecaptchaToAuthModal = <T extends typeof ForgotPasswordModal | t
});

override(modal.prototype, 'onsubmit', function (original, e) {
if (isEnabled() && this.recaptcha.isInvisible() && !e.isRecaptchaSecondStep) {
if (app.forum.attribute('fof-recaptcha.configured') && isEnabled() && this.recaptcha.isInvisible() && !e.isRecaptchaSecondStep) {
// When recaptcha is invisible, onsubmit will be called two times
// First time with normal event, we will call recaptcha.execute
// Second time is called from recaptcha callback with a special isRecaptcha attribute
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import Recaptcha from '../common/components/Recaptcha';

export default function (Composer) {
extend(Composer.prototype, 'oninit', function () {
if (app.forum.attribute('postWithoutCaptcha')) {
if (!app.forum.attribute('fof-recaptcha.configured') || app.forum.attribute('postWithoutCaptcha')) {
return;
}

Expand All @@ -19,15 +19,15 @@ export default function (Composer) {
});

extend(Composer.prototype, 'data', function (data) {
if (app.forum.attribute('postWithoutCaptcha')) {
if (!app.forum.attribute('fof-recaptcha.configured') || app.forum.attribute('postWithoutCaptcha')) {
return;
}

data['g-recaptcha-response'] = this.recaptcha.getResponse();
});

extend(Composer.prototype, 'headerItems', function (fields) {
if (app.forum.attribute('postWithoutCaptcha')) {
if (!app.forum.attribute('fof-recaptcha.configured') || app.forum.attribute('postWithoutCaptcha')) {
return;
}

Expand All @@ -42,15 +42,18 @@ export default function (Composer) {

// There's no onerror handler on composer classes, but we can react to loaded which is called after errors
extend(Composer.prototype, 'loaded', function () {
if (app.forum.attribute('postWithoutCaptcha')) {
if (!app.forum.attribute('fof-recaptcha.configured') || app.forum.attribute('postWithoutCaptcha')) {
return;
}

this.recaptcha.reset();
});

override(Composer.prototype, 'onsubmit', function (original, argument1) {
if (!app.forum.attribute('postWithoutCaptcha') && this.recaptcha.isInvisible() && argument1 !== 'recaptchaSecondStep') {
if (
app.forum.attribute('fof-recaptcha.configured') ||
(!app.forum.attribute('postWithoutCaptcha') && this.recaptcha.isInvisible() && argument1 !== 'recaptchaSecondStep')
) {
this.loading = true;
this.recaptcha.execute();
return;
Expand Down
File renamed without changes.
26 changes: 12 additions & 14 deletions js/tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,17 +1,15 @@
{
// Use Flarum's tsconfig as a starting point
"extends": "flarum-tsconfig",
// This will match all .ts, .tsx, .d.ts, .js, .jsx files in your `src` folder
// and also tells your Typescript server to read core's global typings for
// access to `dayjs` and `$` in the global namespace.
"include": ["src/**/*", "../vendor/flarum/core/js/dist-typings/@types/**/*"],
"compilerOptions": {
// This will output typings to `dist-typings`
"declarationDir": "./dist-typings",
"baseUrl": ".",
"paths": {
"flarum/*": ["../vendor/flarum/core/js/dist-typings/*"]
}
// Use Flarum's tsconfig as a starting point
"extends": "flarum-tsconfig",
// This will match all .ts, .tsx, .d.ts, .js, .jsx files in your `src` folder
// and also tells your Typescript server to read core's global typings for
// access to `dayjs` and `$` in the global namespace.
"include": ["src/**/*", "../vendor/flarum/core/js/dist-typings/@types/**/*"],
"compilerOptions": {
// This will output typings to `dist-typings`
"declarationDir": "./dist-typings",
"paths": {
"flarum/*": ["../vendor/flarum/core/js/dist-typings/*"]
}
}
}
36 changes: 36 additions & 0 deletions src/ForumAttributes.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?php

/*
* This file is part of fof/recaptcha.
*
* Copyright (c) FriendsOfFlarum.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace FoF\ReCaptcha;

use Flarum\Api\Serializer\ForumSerializer;
use Flarum\Settings\SettingsRepositoryInterface;

class ForumAttributes
{
/**
* @var SettingsRepositoryInterface
*/
protected $settings;

function __construct(SettingsRepositoryInterface $settings)
{
$this->settings = $settings;
}

public function __invoke(ForumSerializer $serializer, $model, array $attributes): array
{
$attributes['fof-recaptcha.configured'] = Utils::isExtensionSetup($this->settings);
$attributes['postWithoutCaptcha'] = $serializer->getActor()->hasPermission('fof-recaptcha.postWithoutCaptcha');

return $attributes;
}
}
8 changes: 7 additions & 1 deletion src/Listeners/AddValidatorRule.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
use Flarum\Foundation\AbstractValidator;
use Flarum\Settings\SettingsRepositoryInterface;
use FoF\ReCaptcha\ReCaptcha\GuzzleRequestMethod;
use FoF\ReCaptcha\Utils;
use Illuminate\Validation\Validator;
use ReCaptcha\ReCaptcha;

Expand All @@ -36,6 +37,10 @@ public function __construct(SettingsRepositoryInterface $settings)

public function __invoke(AbstractValidator $flarumValidator, Validator $validator)
{
if (!Utils::isExtensionSetup($this->settings)) {
return;
}

$secret = $this->settings->get('fof-recaptcha.credentials.secret');

$validator->addExtension(
Expand All @@ -50,7 +55,8 @@ function ($attribute, $value, $parameters) use ($validator, $secret) {
if (!empty($verification->getErrorCodes())) {
$validator->setCustomMessages(
[
'recaptcha' => resolve('translator')->trans('validation.recaptcha-unknown', ['errors' => implode(', ', $verification->getErrorCodes())])]
'recaptcha' => resolve('translator')->trans('validation.recaptcha-unknown', ['errors' => implode(', ', $verification->getErrorCodes())])
]
);
}

Expand Down
5 changes: 5 additions & 0 deletions src/Listeners/RegisterValidate.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

use Flarum\Settings\SettingsRepositoryInterface;
use Flarum\User\Event\Saving;
use FoF\ReCaptcha\Utils;
use FoF\ReCaptcha\Validators\RecaptchaValidator;
use Illuminate\Support\Arr;

Expand All @@ -39,6 +40,10 @@ public function __construct(RecaptchaValidator $validator, SettingsRepositoryInt

public function handle(Saving $event)
{
if (!Utils::isExtensionSetup($this->settings)) {
return;
}

// We also check for the actor's admin status, so that we can allow admins to create users from the admin panel without recaptcha blocking the action.
if (!$event->user->exists && $this->settings->get('fof-recaptcha.signup') && !$event->actor->isAdmin()) {
$this->validator->assertValid([
Expand Down
13 changes: 11 additions & 2 deletions src/Listeners/ReplyPostValidate.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
namespace FoF\ReCaptcha\Listeners;

use Flarum\Post\Event\Saving;
use Flarum\Settings\SettingsRepositoryInterface;
use FoF\ReCaptcha\Utils;
use FoF\ReCaptcha\Validators\RecaptchaValidator;
use Illuminate\Support\Arr;

Expand All @@ -23,15 +25,22 @@ class ReplyPostValidate
protected $validator;

/**
* @param RecaptchaValidator $validator
* @var SettingsRepositoryInterface
*/
public function __construct(RecaptchaValidator $validator)
protected $settings;

public function __construct(RecaptchaValidator $validator, SettingsRepositoryInterface $settings)
{
$this->validator = $validator;
$this->settings = $settings;
}

public function handle(Saving $event)
{
if (!Utils::isExtensionSetup($this->settings)) {
return;
}

if (!$event->post->exists) {
// If it's a new discussion, the reCAPTCHA is already validated in discussion saving event
// When this code runs, the discussion already exists, and the number has not been assigned to the post yet
Expand Down
13 changes: 11 additions & 2 deletions src/Listeners/StartDiscussionValidate.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
namespace FoF\ReCaptcha\Listeners;

use Flarum\Discussion\Event\Saving;
use Flarum\Settings\SettingsRepositoryInterface;
use FoF\ReCaptcha\Utils;
use FoF\ReCaptcha\Validators\RecaptchaValidator;
use Illuminate\Support\Arr;

Expand All @@ -23,15 +25,22 @@ class StartDiscussionValidate
protected $validator;

/**
* @param RecaptchaValidator $validator
* @var SettingsRepositoryInterface
*/
public function __construct(RecaptchaValidator $validator)
protected $settings;

public function __construct(RecaptchaValidator $validator, SettingsRepositoryInterface $settings)
{
$this->validator = $validator;
$this->settings = $settings;
}

public function handle(Saving $event)
{
if (!Utils::isExtensionSetup($this->settings)) {
return;
}

if (!$event->discussion->exists) {
if ($event->actor->hasPermission('fof-recaptcha.postWithoutCaptcha')) {
return;
Expand Down
13 changes: 13 additions & 0 deletions src/Utils.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

namespace FoF\ReCaptcha;

use Flarum\Settings\SettingsRepositoryInterface;

class Utils
{
static function isExtensionSetup(SettingsRepositoryInterface $settings): bool
{
return $settings->get('fof-recaptcha.credentials.site', "") !== "" && $settings->get('fof-recaptcha.credentials.secret', "") !== "";
}
}

0 comments on commit e6ad39b

Please sign in to comment.