Regenerate CAPTCHA phrase using a custom request with cachebust to avoid caching

[MAILPOET-6038]
This commit is contained in:
Jan Jakes
2024-07-30 15:36:12 +02:00
committed by Aschepikov
parent 758cb09a77
commit ee6e22efa3
5 changed files with 52 additions and 5 deletions

View File

@@ -72,21 +72,37 @@ jQuery(($) => {
}
async function updateCaptcha(e?: Event) {
const captchaSessionId = document.querySelector<HTMLInputElement>(
'#mailpoet_captcha_form input[name="data[captcha_session_id]"]',
)?.value;
const image = document.querySelector('img.mailpoet_captcha');
const audio = document.querySelector<HTMLAudioElement>(
'.mailpoet_captcha_player',
);
if (!image || !audio) {
if (!captchaSessionId || !image || !audio) {
return false;
}
// page reload invalidates CAPTCHA, but we can do it with AJAX
await fetch(window.location.href);
const cachebust = `${new Date().getTime()}`;
// regenerate captcha phrase
const url = new URL(window.location.href.split('?')[0]);
url.searchParams.set('mailpoet_router', '');
url.searchParams.set('mailpoet_page', 'subscriptions');
url.searchParams.set('endpoint', 'subscription');
url.searchParams.set('action', 'captchaRefresh');
url.searchParams.set(
'data',
btoa(JSON.stringify({ captcha_session_id: captchaSessionId })),
);
url.searchParams.set('cachebust', cachebust);
await fetch(url);
// image
if (image) {
const imageUrl = new URL(image.getAttribute('src'));
imageUrl.searchParams.set('cachebust', `${new Date().getTime()}`);
imageUrl.searchParams.set('cachebust', cachebust);
image.setAttribute('src', imageUrl.toString());
}
@@ -94,7 +110,7 @@ jQuery(($) => {
if (audio) {
const audioSource = audio.querySelector('source');
const audioUrl = new URL(audioSource.getAttribute('src'));
audioUrl.searchParams.set('cachebust', `${new Date().getTime()}`);
audioUrl.searchParams.set('cachebust', cachebust);
audioSource.setAttribute('src', audioUrl.toString());
audio.load();
}

View File

@@ -13,6 +13,7 @@ class Subscription {
const ACTION_CAPTCHA = 'captcha';
const ACTION_CAPTCHA_IMAGE = 'captchaImage';
const ACTION_CAPTCHA_AUDIO = 'captchaAudio';
const ACTION_CAPTCHA_REFRESH = 'captchaRefresh';
const ACTION_CONFIRM = 'confirm';
const ACTION_MANAGE = 'manage';
const ACTION_UNSUBSCRIBE = 'unsubscribe';
@@ -23,6 +24,7 @@ class Subscription {
self::ACTION_CAPTCHA,
self::ACTION_CAPTCHA_IMAGE,
self::ACTION_CAPTCHA_AUDIO,
self::ACTION_CAPTCHA_REFRESH,
self::ACTION_CONFIRM,
self::ACTION_MANAGE,
self::ACTION_UNSUBSCRIBE,
@@ -82,6 +84,14 @@ class Subscription {
exit;
}
public function captchaRefresh($data): void {
$captchaSessionId = $data['captcha_session_id'] ?? null;
if (!$captchaSessionId) {
return;
}
$this->captchaRenderer->refreshPhrase($captchaSessionId);
}
public function confirm($data) {
$subscription = $this->initSubscriptionPage(UserSubscription\Pages::ACTION_CONFIRM, $data);
$subscription->confirm();

View File

@@ -75,6 +75,10 @@ class CaptchaRenderer {
$builder->output();
}
public function refreshPhrase(string $sessionId): string {
return $this->phrase->createPhrase($sessionId);
}
private function getPhrase(string $sessionId): string {
$phrase = $this->phrase->getPhrase($sessionId);
if (!$phrase) {

View File

@@ -6,6 +6,7 @@ use Codeception\Stub;
use Codeception\Stub\Expected;
use MailPoet\Router\Endpoints\Subscription;
use MailPoet\Subscription\Captcha\CaptchaRenderer;
use MailPoet\Subscription\Captcha\CaptchaSession;
use MailPoet\Subscription\Pages;
use MailPoet\Util\Request;
use MailPoet\WP\Functions as WPFunctions;
@@ -58,4 +59,13 @@ class SubscriptionTest extends \MailPoetTest {
$subscription = new Subscription($pages, $this->wp, $this->captchaRenderer, $this->request);
$subscription->unsubscribe($this->data);
}
public function testItRefreshesCaptcha(): void {
$captchaSession = $this->diContainer->get(CaptchaSession::class);
$captchaSession->setCaptchaHash('123', ['phrase' => 'abc']);
$subscription = new Subscription($this->make(Pages::class), $this->wp, $this->captchaRenderer, $this->request);
$subscription->captchaRefresh(['captcha_session_id' => '123']);
$this->assertNotEquals('abc', $captchaSession->getCaptchaHash('123')['phrase']);
}
}

View File

@@ -30,4 +30,11 @@ class CaptchaRendererTest extends \MailPoetTest {
$this->getActualOutputForAssertion()
);
}
public function testItRefreshesPhrase(): void {
$sessionId = '123';
$this->session->setCaptchaHash($sessionId, ['phrase' => 'abc']);
$this->testee->refreshPhrase($sessionId);
$this->assertNotEquals('abc', $this->session->getCaptchaHash($sessionId)['phrase']);
}
}