Find this useful? Enter your email to receive occasional updates for securing PHP code.
Signing you up...
Thank you for signing up!
PHP Decode
<?php declare(strict_types=1); /** * Passbolt ~ Open source password manager for teams ..
Decoded Output download
<?php
declare(strict_types=1);
/**
* Passbolt ~ Open source password manager for teams
* Copyright (c) Passbolt SA (https://www.passbolt.com)
*
* Licensed under GNU Affero General Public License version 3 of the or any later version.
* For full copyright and license information, please see the LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Passbolt SA (https://www.passbolt.com)
* @license https://opensource.org/licenses/AGPL-3.0 AGPL License
* @link https://www.passbolt.com Passbolt(tm)
* @since 3.11.0
*/
namespace Passbolt\MultiFactorAuthentication\Controller\Duo;
use App\Authenticator\SessionIdentificationServiceInterface;
use App\Error\Exception\FormValidationException;
use App\Model\Entity\AuthenticationToken;
use App\Service\AuthenticationTokens\AuthenticationTokenGetService;
use App\Utility\ExceptionLogger;
use App\Utility\UserAccessControl;
use Cake\Http\Cookie\Cookie;
use Cake\Http\Exception\BadRequestException;
use Cake\Http\Exception\InternalErrorException;
use Cake\Http\Response;
use Cake\Validation\Validation;
use Duo\DuoUniversal\Client;
use Passbolt\MultiFactorAuthentication\Controller\MfaVerifyController;
use Passbolt\MultiFactorAuthentication\Form\Duo\DuoCallbackForm;
use Passbolt\MultiFactorAuthentication\Model\Dto\MfaDuoCallbackDto;
use Passbolt\MultiFactorAuthentication\Service\Duo\MfaDuoLoginService;
use Passbolt\MultiFactorAuthentication\Service\Duo\MfaDuoStateCookieService;
use Passbolt\MultiFactorAuthentication\Service\MfaVerifiedCookieService;
use Passbolt\MultiFactorAuthentication\Utility\MfaSettings;
class DuoVerifyCallbackGetController extends MfaVerifyController
{
/**
* @return void
*/
public function initialize(): void
{
parent::initialize();
$this->loadComponent('Flash');
$this->loadComponent('SanitizeUrl');
}
/**
* Handle Duo setup callback GET request. Redirect the user if the auth token associated to the callback
* contains a redirect property. It is usually the case when a user authenticates to duo on the web application.
*
* @param \App\Authenticator\SessionIdentificationServiceInterface $sessionIdentificationService session ID service
* @param \Duo\DuoUniversal\Client|null $duoSdkClient Duo SDK Client
* @return \Cake\Http\Response|void
*/
public function get(
SessionIdentificationServiceInterface $sessionIdentificationService,
?Client $duoSdkClient = null
) {
$this->_assertRequestNotJson();
$this->_handleVerifiedNotRequired($sessionIdentificationService);
$redirect = $this->_handleInvalidSettings(MfaSettings::PROVIDER_DUO);
if ($redirect) {
return $redirect;
}
$uac = $this->User->getAccessControl();
$cookieToken = $this->consumeAndAssertCookieToken();
try {
// Assert that the callback from Duo is valid and doesn't contain any error
$mfaDuoCallbackDto = $this->getAndAssertMfaDuoCallbackData();
// Consume the authentication token and verify its integrity
$authenticationToken = (new MfaDuoLoginService($duoSdkClient))->login(
$uac,
$mfaDuoCallbackDto,
$cookieToken
);
$this->addMfaVerifiedCookieToResponse($uac, $sessionIdentificationService);
} catch (BadRequestException | FormValidationException $e) {
$redirect = $this->handleErrorFeedback($e, $cookieToken, $uac);
if (!is_null($redirect)) {
return $redirect;
}
// If there was no redirection, re-throw the exception (needed for mobile)
throw $e;
}
$this->disableAutoRender();
$this->redirectIfDefinedInToken($authenticationToken);
}
/**
* Handles the case when an exception is thrown by logging the error, setting a flash message with the
* error descrition and redirecting to the internal redirect path if given.
*
* @param \Throwable $e The throwable/exception thrown
* @param string $token The authentication token's token (id)
* @param \App\Utility\UserAccessControl $uac User Access Control
* @return \Cake\Http\Response|null
*/
private function handleErrorFeedback(
\Throwable $e,
string $token,
UserAccessControl $uac
): ?Response {
// Log the exception and all its backtrace of exception
ExceptionLogger::error($e);
// Retrieve the authentication token
$authenticationToken = (new AuthenticationTokenGetService())
->get($token, $uac->getId(), AuthenticationToken::TYPE_MFA_VERIFY);
// Check that it was not consumed
if (!isset($authenticationToken)) {
return null;
}
// If no redirect parameter was given (redirect is not passed for mobile), no redirection is needed
$redirect = $authenticationToken->getDataValue('redirect');
if (empty($redirect)) {
return null;
}
$this->Flash->error($e->getMessage());
$redirect = $this->SanitizeUrl->sanitize($redirect, ['/mfa/verify']);
$verifyUrl = MfaSettings::get($uac)->getDefaultVerifyUrl(false);
$verifyUrl .= '?redirect=' . $redirect;
return $this->redirect($verifyUrl);
}
/**
* Get the Mfa Duo Callback data from the query and assert them.
*
* @throws \App\Error\Exception\FormValidationException If the data provided on the query does not validate
* @throws \Cake\Http\Exception\BadRequestException If Duo was not able to authenticate the user and provided error details
* @return \Passbolt\MultiFactorAuthentication\Model\Dto\MfaDuoCallbackDto
*/
private function getAndAssertMfaDuoCallbackData(): MfaDuoCallbackDto
{
$mfaDuoCallbackData = $this->getRequest()->getQueryParams();
$mfaDuoCallbackForm = new DuoCallbackForm();
$isValid = $mfaDuoCallbackForm->execute($mfaDuoCallbackData);
$mfaDuoCallbackDto = new MfaDuoCallbackDto($mfaDuoCallbackForm->getData());
if ($mfaDuoCallbackDto->hasError()) {
$msg = __('Unable to authenticate to Duo.');
$msg .= " {$mfaDuoCallbackDto->formatError()}";
throw new BadRequestException($msg);
}
if (!$isValid) {
$msg = __('Unable to validate the Duo callback data.');
throw new FormValidationException($msg, $mfaDuoCallbackForm);
}
return $mfaDuoCallbackDto;
}
/**
* Consume the duo state cookie containing the user authentication token id and assert the format this one.
*
* @return string The token id stored in the cookie
* @throws \Cake\Http\Exception\BadRequestException if the cookie is not defined
* @throws \Cake\Http\Exception\BadRequestException if the cookie value is not a string
* @throws \Cake\Http\Exception\BadRequestException if the cookie value is not a valid uuid
*/
private function consumeAndAssertCookieToken(): string
{
$cookieToken = (new MfaDuoStateCookieService())->readDuoStateCookieValue($this->getRequest());
if (is_null($cookieToken)) {
throw new BadRequestException(__('A Duo state cookie is required.'));
}
$cookieToExpire = new Cookie(MfaDuoStateCookieService::MFA_COOKIE_DUO_STATE);
$this->setResponse($this->getResponse()->withExpiredCookie($cookieToExpire));
if (!is_string($cookieToken)) {
throw new BadRequestException(__('The Duo state cookie value should be a string.'));
} elseif (!Validation::uuid($cookieToken)) {
throw new BadRequestException(__('The Duo state cookie should be a valid UUID.'));
}
return $cookieToken;
}
/**
* Add to the response the MFA verified cookie.
*
* @param \App\Utility\UserAccessControl $uac User access control
* @param \App\Authenticator\SessionIdentificationServiceInterface $sessionIdentificationService session ID service
* @return void
* @throws \Cake\Http\Exception\InternalErrorException if it cannot create MFA cookie
*/
private function addMfaVerifiedCookieToResponse(
UserAccessControl $uac,
SessionIdentificationServiceInterface $sessionIdentificationService
): void {
try {
$cookie = (new MfaVerifiedCookieService())->createDuoMfaVerifiedCookie(
$uac,
$sessionIdentificationService,
$this->getRequest()
);
} catch (\Throwable $e) {
throw new InternalErrorException('Could not create MFA verified cookie.', null, $e);
}
$this->setResponse($this->getResponse()->withCookie($cookie));
}
/**
* Redirect the user if the authentication token contains a redirect path.
*
* @param \App\Model\Entity\AuthenticationToken $authenticationToken The authentication token
* @return void
*/
private function redirectIfDefinedInToken(AuthenticationToken $authenticationToken): void
{
$redirect = $authenticationToken->getDataValue('redirect');
if (!empty($redirect) && substr($redirect, 0, 1) === '/') { // redirect path must start with / (internal link)
$this->redirect($redirect);
}
}
}
?>
Did this file decode correctly?
Original Code
<?php
declare(strict_types=1);
/**
* Passbolt ~ Open source password manager for teams
* Copyright (c) Passbolt SA (https://www.passbolt.com)
*
* Licensed under GNU Affero General Public License version 3 of the or any later version.
* For full copyright and license information, please see the LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Passbolt SA (https://www.passbolt.com)
* @license https://opensource.org/licenses/AGPL-3.0 AGPL License
* @link https://www.passbolt.com Passbolt(tm)
* @since 3.11.0
*/
namespace Passbolt\MultiFactorAuthentication\Controller\Duo;
use App\Authenticator\SessionIdentificationServiceInterface;
use App\Error\Exception\FormValidationException;
use App\Model\Entity\AuthenticationToken;
use App\Service\AuthenticationTokens\AuthenticationTokenGetService;
use App\Utility\ExceptionLogger;
use App\Utility\UserAccessControl;
use Cake\Http\Cookie\Cookie;
use Cake\Http\Exception\BadRequestException;
use Cake\Http\Exception\InternalErrorException;
use Cake\Http\Response;
use Cake\Validation\Validation;
use Duo\DuoUniversal\Client;
use Passbolt\MultiFactorAuthentication\Controller\MfaVerifyController;
use Passbolt\MultiFactorAuthentication\Form\Duo\DuoCallbackForm;
use Passbolt\MultiFactorAuthentication\Model\Dto\MfaDuoCallbackDto;
use Passbolt\MultiFactorAuthentication\Service\Duo\MfaDuoLoginService;
use Passbolt\MultiFactorAuthentication\Service\Duo\MfaDuoStateCookieService;
use Passbolt\MultiFactorAuthentication\Service\MfaVerifiedCookieService;
use Passbolt\MultiFactorAuthentication\Utility\MfaSettings;
class DuoVerifyCallbackGetController extends MfaVerifyController
{
/**
* @return void
*/
public function initialize(): void
{
parent::initialize();
$this->loadComponent('Flash');
$this->loadComponent('SanitizeUrl');
}
/**
* Handle Duo setup callback GET request. Redirect the user if the auth token associated to the callback
* contains a redirect property. It is usually the case when a user authenticates to duo on the web application.
*
* @param \App\Authenticator\SessionIdentificationServiceInterface $sessionIdentificationService session ID service
* @param \Duo\DuoUniversal\Client|null $duoSdkClient Duo SDK Client
* @return \Cake\Http\Response|void
*/
public function get(
SessionIdentificationServiceInterface $sessionIdentificationService,
?Client $duoSdkClient = null
) {
$this->_assertRequestNotJson();
$this->_handleVerifiedNotRequired($sessionIdentificationService);
$redirect = $this->_handleInvalidSettings(MfaSettings::PROVIDER_DUO);
if ($redirect) {
return $redirect;
}
$uac = $this->User->getAccessControl();
$cookieToken = $this->consumeAndAssertCookieToken();
try {
// Assert that the callback from Duo is valid and doesn't contain any error
$mfaDuoCallbackDto = $this->getAndAssertMfaDuoCallbackData();
// Consume the authentication token and verify its integrity
$authenticationToken = (new MfaDuoLoginService($duoSdkClient))->login(
$uac,
$mfaDuoCallbackDto,
$cookieToken
);
$this->addMfaVerifiedCookieToResponse($uac, $sessionIdentificationService);
} catch (BadRequestException | FormValidationException $e) {
$redirect = $this->handleErrorFeedback($e, $cookieToken, $uac);
if (!is_null($redirect)) {
return $redirect;
}
// If there was no redirection, re-throw the exception (needed for mobile)
throw $e;
}
$this->disableAutoRender();
$this->redirectIfDefinedInToken($authenticationToken);
}
/**
* Handles the case when an exception is thrown by logging the error, setting a flash message with the
* error descrition and redirecting to the internal redirect path if given.
*
* @param \Throwable $e The throwable/exception thrown
* @param string $token The authentication token's token (id)
* @param \App\Utility\UserAccessControl $uac User Access Control
* @return \Cake\Http\Response|null
*/
private function handleErrorFeedback(
\Throwable $e,
string $token,
UserAccessControl $uac
): ?Response {
// Log the exception and all its backtrace of exception
ExceptionLogger::error($e);
// Retrieve the authentication token
$authenticationToken = (new AuthenticationTokenGetService())
->get($token, $uac->getId(), AuthenticationToken::TYPE_MFA_VERIFY);
// Check that it was not consumed
if (!isset($authenticationToken)) {
return null;
}
// If no redirect parameter was given (redirect is not passed for mobile), no redirection is needed
$redirect = $authenticationToken->getDataValue('redirect');
if (empty($redirect)) {
return null;
}
$this->Flash->error($e->getMessage());
$redirect = $this->SanitizeUrl->sanitize($redirect, ['/mfa/verify']);
$verifyUrl = MfaSettings::get($uac)->getDefaultVerifyUrl(false);
$verifyUrl .= '?redirect=' . $redirect;
return $this->redirect($verifyUrl);
}
/**
* Get the Mfa Duo Callback data from the query and assert them.
*
* @throws \App\Error\Exception\FormValidationException If the data provided on the query does not validate
* @throws \Cake\Http\Exception\BadRequestException If Duo was not able to authenticate the user and provided error details
* @return \Passbolt\MultiFactorAuthentication\Model\Dto\MfaDuoCallbackDto
*/
private function getAndAssertMfaDuoCallbackData(): MfaDuoCallbackDto
{
$mfaDuoCallbackData = $this->getRequest()->getQueryParams();
$mfaDuoCallbackForm = new DuoCallbackForm();
$isValid = $mfaDuoCallbackForm->execute($mfaDuoCallbackData);
$mfaDuoCallbackDto = new MfaDuoCallbackDto($mfaDuoCallbackForm->getData());
if ($mfaDuoCallbackDto->hasError()) {
$msg = __('Unable to authenticate to Duo.');
$msg .= " {$mfaDuoCallbackDto->formatError()}";
throw new BadRequestException($msg);
}
if (!$isValid) {
$msg = __('Unable to validate the Duo callback data.');
throw new FormValidationException($msg, $mfaDuoCallbackForm);
}
return $mfaDuoCallbackDto;
}
/**
* Consume the duo state cookie containing the user authentication token id and assert the format this one.
*
* @return string The token id stored in the cookie
* @throws \Cake\Http\Exception\BadRequestException if the cookie is not defined
* @throws \Cake\Http\Exception\BadRequestException if the cookie value is not a string
* @throws \Cake\Http\Exception\BadRequestException if the cookie value is not a valid uuid
*/
private function consumeAndAssertCookieToken(): string
{
$cookieToken = (new MfaDuoStateCookieService())->readDuoStateCookieValue($this->getRequest());
if (is_null($cookieToken)) {
throw new BadRequestException(__('A Duo state cookie is required.'));
}
$cookieToExpire = new Cookie(MfaDuoStateCookieService::MFA_COOKIE_DUO_STATE);
$this->setResponse($this->getResponse()->withExpiredCookie($cookieToExpire));
if (!is_string($cookieToken)) {
throw new BadRequestException(__('The Duo state cookie value should be a string.'));
} elseif (!Validation::uuid($cookieToken)) {
throw new BadRequestException(__('The Duo state cookie should be a valid UUID.'));
}
return $cookieToken;
}
/**
* Add to the response the MFA verified cookie.
*
* @param \App\Utility\UserAccessControl $uac User access control
* @param \App\Authenticator\SessionIdentificationServiceInterface $sessionIdentificationService session ID service
* @return void
* @throws \Cake\Http\Exception\InternalErrorException if it cannot create MFA cookie
*/
private function addMfaVerifiedCookieToResponse(
UserAccessControl $uac,
SessionIdentificationServiceInterface $sessionIdentificationService
): void {
try {
$cookie = (new MfaVerifiedCookieService())->createDuoMfaVerifiedCookie(
$uac,
$sessionIdentificationService,
$this->getRequest()
);
} catch (\Throwable $e) {
throw new InternalErrorException('Could not create MFA verified cookie.', null, $e);
}
$this->setResponse($this->getResponse()->withCookie($cookie));
}
/**
* Redirect the user if the authentication token contains a redirect path.
*
* @param \App\Model\Entity\AuthenticationToken $authenticationToken The authentication token
* @return void
*/
private function redirectIfDefinedInToken(AuthenticationToken $authenticationToken): void
{
$redirect = $authenticationToken->getDataValue('redirect');
if (!empty($redirect) && substr($redirect, 0, 1) === '/') { // redirect path must start with / (internal link)
$this->redirect($redirect);
}
}
}
Function Calls
None |
Stats
MD5 | 373be51115c3d3ce4f5508bb626ff685 |
Eval Count | 0 |
Decode Time | 95 ms |