Skip to content

Commit

Permalink
fix: fix api 404 in cms
Browse files Browse the repository at this point in the history
refactor!: use branded config name
  • Loading branch information
OldStarchy committed Dec 7, 2022
1 parent 9eaaf02 commit dd73db3
Show file tree
Hide file tree
Showing 7 changed files with 221 additions and 135 deletions.
7 changes: 5 additions & 2 deletions _config/config.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
Name: twilioauthenticatorconfig
Name: OTPAuthenticator
---
# Register the method
SilverStripe\MFA\Service\MethodRegistry:
Expand All @@ -15,4 +15,7 @@ SilverStripe\Core\Injector\Injector:

SilverStripe\MFA\Authenticator\LoginHandler:
extensions:
- XD\OTPAuthenticator\Extensions\LoginHandlerExtension
- XD\OTPAuthenticator\Extensions\LoginHandlerExtension
SilverStripe\MFA\Controller\AdminRegistrationController:
extensions:
- XD\OTPAuthenticator\Extensions\AdminRegistrationControllerExtension
2 changes: 1 addition & 1 deletion client/dist/js/bundle.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion client/src/lib/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
*/
const api = (endpoint, method = 'GET', body = undefined, headers = {}) => (
fetch(
endpoint,
location.origin + location.pathname.replace(/(^|\/)[^\/]+\/?$/, '$1') + endpoint,
{
body,
credentials: 'same-origin',
Expand Down
7 changes: 7 additions & 0 deletions src/Exceptions/OTPException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?php

namespace XD\OTPAuthenticator\Exceptions;

class OTPException extends \Exception
{
}
32 changes: 32 additions & 0 deletions src/Extensions/AdminRegistrationControllerExtension.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php

namespace XD\OTPAuthenticator\Extensions;

use SilverStripe\Control\HTTPRequest;
use SilverStripe\MFA\Controller\AdminRegistrationController;

/**
* @property AdminRegistrationController owner
*/
class AdminRegistrationControllerExtension extends OTPExtension
{
private static $url_handlers = [
'POST otp/registerto' => 'handleRegisterTo',
'GET otp/resendcode' => 'handleResendCode',
];

private static $allowed_actions = [
'handleRegisterTo',
'handleResendCode',
];

public function handleRegisterTo(HTTPRequest $request)
{
return parent::handleRegisterTo($request);
}

public function handleResendCode(HTTPRequest $request)
{
return parent::handleResendCode($request);
}
}
140 changes: 9 additions & 131 deletions src/Extensions/LoginHandlerExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,153 +2,31 @@

namespace XD\OTPAuthenticator\Extensions;

use Exception;
use SilverStripe\Control\HTTPRequest;
use SilverStripe\Core\Extension;
use SilverStripe\MFA\Authenticator\LoginHandler;
use SilverStripe\MFA\RequestHandler\BaseHandlerTrait;
use SilverStripe\MFA\Service\MethodRegistry;
use SilverStripe\Security\Security;
use XD\OTPAuthenticator\Method;
use XD\OTPAuthenticator\TOTPAware;

/**
* @property LoginHandler owner
*/
class LoginHandlerExtension extends Extension
class LoginHandlerExtension extends OTPExtension
{
use BaseHandlerTrait;
use TOTPAware;

// todo add methods to schema ?
private static $url_handlers = [
'POST mfa/otp/registerto' => 'registerOTPSendTo',
'GET mfa/otp/resendcode' => 'resendOTPCode',
'POST mfa/otp/registerto' => 'handleRegisterTo',
'GET mfa/otp/resendcode' => 'handleResendCode',
];

private static $allowed_actions = [
'registerOTPSendTo',
'resendOTPCode',
'handleRegisterTo',
'handleResendCode',
];

/**
* Register the send to
*/
public function registerOTPSendTo(HTTPRequest $request)
{
$data = json_decode($request->getBody(), true);
$store = $this->getStore();
// $member = $store && $store->getMember() ? $store->getMember() : Security::getCurrentUser();
$methodSegment = $store->getMethod();
$method = MethodRegistry::singleton()->getMethodByURLSegment($methodSegment);

if (!$method instanceof Method) {
return $this->owner->jsonResponse([
'error' => _t(
Method::class . '.INVALID_METHOD',
'This endpoint should only be used by the OTP method',
),
]);
}

// get the sender provider
$sendProvider = $method->getSendProvider();
$fieldLabel = $sendProvider->getFieldLabel();

if (!isset($data['sendTo'])) {
return $this->owner->jsonResponse([
'error' => _t(Method::class . '.NO_TO', 'No {fieldLabel} provided', null, [
'fieldLabel' => $fieldLabel,
]),
]);
}

$to = $data['sendTo'];
$additional = $data['additional'];
if (!$sendProvider->validate($to, $additional)) {
return $this->owner->jsonResponse([
'error' => _t(Method::class . '.INVALID_TO', 'No valid {fieldLabel} provided', null, [
'fieldLabel' => $fieldLabel,
]),
]);
}

$store->addState([
'sendTo' => $to,
'additional' => $additional,
]);

// get the totp code
$code = $this->getCode($store);

try {
$sendProvider->send($code, $to);
} catch (Exception $ex) {
return $this->owner->jsonResponse([
'error' => _t(Method::class . '.INVALID_TO', 'No valid {fieldLabel} provided', null, [
'fieldLabel' => $fieldLabel,
]),
]);
}

return $this->owner->jsonResponse([
'view' => 'VALIDATE_CODE',
'obfuscatedTo' => $sendProvider->obfuscateTo($to),
]);
}

/**
* Resend the sms code
*/
public function resendOTPCode(HTTPRequest $request)
public function handleRegisterTo(HTTPRequest $request)
{
$store = $this->getStore();
$member = $store->getMember() ?: Security::getCurrentUser();
$methodSegment = $store->getMethod();
$method = MethodRegistry::singleton()->getMethodByURLSegment($methodSegment);

if (!$method instanceof Method) {
return $this->owner->jsonResponse([
'error' => _t(
Method::class . '.INVALID_METHOD',
'This endpoint should only be used by the OTP method',
),
]);
}

$sendProvider = $method->getSendProvider();
$fieldLabel = $sendProvider->getFieldLabel();

if (!$member || !($to = $member->getOTPSendTo())) {
return $this->owner->jsonResponse([
'error' => _t(Method::class . '.NO_MEMBER', "We couldn't find a {fieldLabel} in your account", null, [
'fieldLabel' => $fieldLabel,
]),
]);
}

// get the totp code
$code = $this->getCode($store);

try {
$sent = $sendProvider->send($code, $to);
} catch (Exception $ex) {
return $this->owner->jsonResponse([
'error' => _t(Method::class . '.INVALID_TO', 'No valid {fieldLabel} provided', null, [
'fieldLabel' => $fieldLabel,
]),
]);
}

return $this->owner->jsonResponse(array_filter([
'sent' => $sent,
'error' => !$sent ? _t(__CLASS__ . '.COULD_NOT_SEND_CODE', 'Could not send code') : null,
]));
return parent::handleRegisterTo($request);
}

// without the store is inaccesible
public function getRequest()
public function handleResendCode(HTTPRequest $request)
{
return $this->owner->getRequest();
return parent::handleResendCode($request);
}
}
166 changes: 166 additions & 0 deletions src/Extensions/OTPExtension.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
<?php

namespace XD\OTPAuthenticator\Extensions;

use Exception;
use SilverStripe\Control\HTTPRequest;
use SilverStripe\Control\HTTPResponse;
use SilverStripe\Core\Extension;
use SilverStripe\MFA\RequestHandler\BaseHandlerTrait;
use SilverStripe\MFA\Service\MethodRegistry;
use SilverStripe\Security\Security;
use XD\OTPAuthenticator\Exceptions\OTPException;
use XD\OTPAuthenticator\Method;
use XD\OTPAuthenticator\TOTPAware;

class OTPExtension extends Extension
{
use BaseHandlerTrait;
use TOTPAware;

protected function handleRegisterTo(HTTPRequest $request)
{
$data = json_decode($request->getBody(), true);

$sendTo = isset($data['sendTo']) ? $data['sendTo'] : null;
$additional = isset($data['additional']) ? $data['additional'] : null;

try {
$obfuscatedTo = $this->registerOTPSendTo($sendTo, $additional);

return static::jsonResponse([
'view' => 'VALIDATE_CODE',
'obfuscatedTo' => $obfuscatedTo,
]);
} catch (OTPException $e) {
return static::jsonResponse([
'error' => $e->getMessage(),
]);
}
}

protected function handleResendCode(HTTPRequest $request)
{
try {
$sent = $this->resendOTPCode();

return static::jsonResponse(array_filter([
'sent' => $sent,
'error' => !$sent ? _t(__CLASS__ . '.COULD_NOT_SEND_CODE', 'Could not send code') : null,
]));
} catch (OTPException $e) {
return static::jsonResponse([
'error' => $e->getMessage(),
]);
}
}

protected function registerOTPSendTo($to, $additional)
{
$store = $this->getStore();
// $member = $store && $store->getMember() ? $store->getMember() : Security::getCurrentUser();
$methodSegment = $store->getMethod();
$method = MethodRegistry::singleton()->getMethodByURLSegment($methodSegment);

if (!$method instanceof Method) {
return throw new OTPException(_t(
Method::class . '.INVALID_METHOD',
'This endpoint should only be used by the OTP method',
));
}

$sendProvider = $method->getSendProvider();
$fieldLabel = $sendProvider->getFieldLabel();

if (!$to) {
return new OTPException(_t(Method::class . '.NO_TO', 'No {fieldLabel} provided', null, [
'fieldLabel' => $fieldLabel,
]));
}

if (!$sendProvider->validate($to, $additional)) {
return new OTPException(_t(Method::class . '.INVALID_TO', 'No valid {fieldLabel} provided', null, [
'fieldLabel' => $fieldLabel,
]));
}

$store->addState([
'sendTo' => $to,
'additional' => $additional,
]);

$totpCode = $this->getCode($store);

try {
$sendProvider->send($totpCode, $to);
} catch (Exception $ex) {
return new OTPException(_t(Method::class . '.INVALID_TO', 'No valid {fieldLabel} provided', null, [
'fieldLabel' => $fieldLabel,
]));
}

return $sendProvider->obfuscateTo($to);
}

protected function resendOTPCode()
{
$store = $this->getStore();
$member = $store->getMember() ?: Security::getCurrentUser();
$methodSegment = $store->getMethod();
$method = MethodRegistry::singleton()->getMethodByURLSegment($methodSegment);

if (!$method instanceof Method) {
return new OTPException(_t(
Method::class . '.INVALID_METHOD',
'This endpoint should only be used by the OTP method',
));
}

$sendProvider = $method->getSendProvider();
$fieldLabel = $sendProvider->getFieldLabel();

if (!$member || !($to = $member->getOTPSendTo())) {
return new OTPException(_t(
Method::class . '.NO_MEMBER',
"We couldn't find a {fieldLabel} in your account",
null,
[
'fieldLabel' => $fieldLabel,
],
));
}

// get the totp code
$code = $this->getCode($store);

try {
$sent = $sendProvider->send($code, $to);
} catch (Exception $ex) {
return new OTPException(_t(Method::class . '.INVALID_TO', 'No valid {fieldLabel} provided', null, [
'fieldLabel' => $fieldLabel,
]), 0, $ex);
}

return $sent;
}

// without the store is inaccesible
public function getRequest()
{
return $this->owner->getRequest();
}

/**
* Respond with the given array as a JSON response
*
* @param array $response
* @param int $code The HTTP response code to set on the response
* @return HTTPResponse
*/
public static function jsonResponse(array $response, int $code = 200): HTTPResponse
{
return HTTPResponse::create(json_encode($response))
->addHeader('Content-Type', 'application/json')
->setStatusCode($code);
}
}

0 comments on commit dd73db3

Please sign in to comment.