Regenerate CAPTCHA phrase using a custom request with cachebust to avoid caching
[MAILPOET-6038]
This commit is contained in:
@@ -72,21 +72,37 @@ jQuery(($) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function updateCaptcha(e?: Event) {
|
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 image = document.querySelector('img.mailpoet_captcha');
|
||||||
const audio = document.querySelector<HTMLAudioElement>(
|
const audio = document.querySelector<HTMLAudioElement>(
|
||||||
'.mailpoet_captcha_player',
|
'.mailpoet_captcha_player',
|
||||||
);
|
);
|
||||||
if (!image || !audio) {
|
|
||||||
|
if (!captchaSessionId || !image || !audio) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// page reload invalidates CAPTCHA, but we can do it with AJAX
|
const cachebust = `${new Date().getTime()}`;
|
||||||
await fetch(window.location.href);
|
|
||||||
|
// 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
|
// image
|
||||||
if (image) {
|
if (image) {
|
||||||
const imageUrl = new URL(image.getAttribute('src'));
|
const imageUrl = new URL(image.getAttribute('src'));
|
||||||
imageUrl.searchParams.set('cachebust', `${new Date().getTime()}`);
|
imageUrl.searchParams.set('cachebust', cachebust);
|
||||||
image.setAttribute('src', imageUrl.toString());
|
image.setAttribute('src', imageUrl.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -94,7 +110,7 @@ jQuery(($) => {
|
|||||||
if (audio) {
|
if (audio) {
|
||||||
const audioSource = audio.querySelector('source');
|
const audioSource = audio.querySelector('source');
|
||||||
const audioUrl = new URL(audioSource.getAttribute('src'));
|
const audioUrl = new URL(audioSource.getAttribute('src'));
|
||||||
audioUrl.searchParams.set('cachebust', `${new Date().getTime()}`);
|
audioUrl.searchParams.set('cachebust', cachebust);
|
||||||
audioSource.setAttribute('src', audioUrl.toString());
|
audioSource.setAttribute('src', audioUrl.toString());
|
||||||
audio.load();
|
audio.load();
|
||||||
}
|
}
|
||||||
|
@@ -13,6 +13,7 @@ class Subscription {
|
|||||||
const ACTION_CAPTCHA = 'captcha';
|
const ACTION_CAPTCHA = 'captcha';
|
||||||
const ACTION_CAPTCHA_IMAGE = 'captchaImage';
|
const ACTION_CAPTCHA_IMAGE = 'captchaImage';
|
||||||
const ACTION_CAPTCHA_AUDIO = 'captchaAudio';
|
const ACTION_CAPTCHA_AUDIO = 'captchaAudio';
|
||||||
|
const ACTION_CAPTCHA_REFRESH = 'captchaRefresh';
|
||||||
const ACTION_CONFIRM = 'confirm';
|
const ACTION_CONFIRM = 'confirm';
|
||||||
const ACTION_MANAGE = 'manage';
|
const ACTION_MANAGE = 'manage';
|
||||||
const ACTION_UNSUBSCRIBE = 'unsubscribe';
|
const ACTION_UNSUBSCRIBE = 'unsubscribe';
|
||||||
@@ -23,6 +24,7 @@ class Subscription {
|
|||||||
self::ACTION_CAPTCHA,
|
self::ACTION_CAPTCHA,
|
||||||
self::ACTION_CAPTCHA_IMAGE,
|
self::ACTION_CAPTCHA_IMAGE,
|
||||||
self::ACTION_CAPTCHA_AUDIO,
|
self::ACTION_CAPTCHA_AUDIO,
|
||||||
|
self::ACTION_CAPTCHA_REFRESH,
|
||||||
self::ACTION_CONFIRM,
|
self::ACTION_CONFIRM,
|
||||||
self::ACTION_MANAGE,
|
self::ACTION_MANAGE,
|
||||||
self::ACTION_UNSUBSCRIBE,
|
self::ACTION_UNSUBSCRIBE,
|
||||||
@@ -82,6 +84,14 @@ class Subscription {
|
|||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function captchaRefresh($data): void {
|
||||||
|
$captchaSessionId = $data['captcha_session_id'] ?? null;
|
||||||
|
if (!$captchaSessionId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$this->captchaRenderer->refreshPhrase($captchaSessionId);
|
||||||
|
}
|
||||||
|
|
||||||
public function confirm($data) {
|
public function confirm($data) {
|
||||||
$subscription = $this->initSubscriptionPage(UserSubscription\Pages::ACTION_CONFIRM, $data);
|
$subscription = $this->initSubscriptionPage(UserSubscription\Pages::ACTION_CONFIRM, $data);
|
||||||
$subscription->confirm();
|
$subscription->confirm();
|
||||||
|
@@ -75,6 +75,10 @@ class CaptchaRenderer {
|
|||||||
$builder->output();
|
$builder->output();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function refreshPhrase(string $sessionId): string {
|
||||||
|
return $this->phrase->createPhrase($sessionId);
|
||||||
|
}
|
||||||
|
|
||||||
private function getPhrase(string $sessionId): string {
|
private function getPhrase(string $sessionId): string {
|
||||||
$phrase = $this->phrase->getPhrase($sessionId);
|
$phrase = $this->phrase->getPhrase($sessionId);
|
||||||
if (!$phrase) {
|
if (!$phrase) {
|
||||||
|
@@ -6,6 +6,7 @@ use Codeception\Stub;
|
|||||||
use Codeception\Stub\Expected;
|
use Codeception\Stub\Expected;
|
||||||
use MailPoet\Router\Endpoints\Subscription;
|
use MailPoet\Router\Endpoints\Subscription;
|
||||||
use MailPoet\Subscription\Captcha\CaptchaRenderer;
|
use MailPoet\Subscription\Captcha\CaptchaRenderer;
|
||||||
|
use MailPoet\Subscription\Captcha\CaptchaSession;
|
||||||
use MailPoet\Subscription\Pages;
|
use MailPoet\Subscription\Pages;
|
||||||
use MailPoet\Util\Request;
|
use MailPoet\Util\Request;
|
||||||
use MailPoet\WP\Functions as WPFunctions;
|
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 = new Subscription($pages, $this->wp, $this->captchaRenderer, $this->request);
|
||||||
$subscription->unsubscribe($this->data);
|
$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']);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -30,4 +30,11 @@ class CaptchaRendererTest extends \MailPoetTest {
|
|||||||
$this->getActualOutputForAssertion()
|
$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']);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user