Skip to content
Snippets Groups Projects
Unverified Commit 9b5c8161 authored by Dan Garner's avatar Dan Garner Committed by GitHub
Browse files

Login: add some validation to prior route processing (#2841)

fixes xibosignage/xibo#3556
parent d9685ddf
No related branches found
No related tags found
No related merge requests found
...@@ -181,19 +181,18 @@ class Login extends Base ...@@ -181,19 +181,18 @@ class Login extends Base
* Login * Login
* @param Request $request * @param Request $request
* @param Response $response * @param Response $response
* @return \Psr\Http\Message\ResponseInterface|Response * @return \Slim\Http\Response
* @throws InvalidArgumentException
* @throws \Xibo\Support\Exception\DuplicateEntityException * @throws \Xibo\Support\Exception\DuplicateEntityException
* @throws \Xibo\Support\Exception\InvalidArgumentException
*/ */
public function login(Request $request, Response $response) public function login(Request $request, Response $response): Response
{ {
$parsedRequest = $this->getSanitizer($request->getParsedBody()); $parsedRequest = $this->getSanitizer($request->getParsedBody());
$routeParser = RouteContext::fromRequest($request)->getRouteParser(); $routeParser = RouteContext::fromRequest($request)->getRouteParser();
// Capture the prior route (if there is one) // Capture the prior route (if there is one)
$user = null; $redirect = $this->urlFor($request, 'login');
$redirect = 'login'; $priorRoute = $parsedRequest->getString('priorRoute');
$priorRoute = ($parsedRequest->getString('priorRoute'));
try { try {
// Get our username and password // Get our username and password
...@@ -208,7 +207,9 @@ class Login extends Base ...@@ -208,7 +207,9 @@ class Login extends Base
// Retired user // Retired user
if ($user->retired === 1) { if ($user->retired === 1) {
throw new AccessDeniedException(__('Sorry this account does not exist or does not have permission to access the web portal.')); throw new AccessDeniedException(
__('Sorry this account does not exist or does not have permission to access the web portal.')
);
} }
// Check password // Check password
...@@ -223,19 +224,16 @@ class Login extends Base ...@@ -223,19 +224,16 @@ class Login extends Base
// We are logged in, so complete the login flow // We are logged in, so complete the login flow
$this->completeLoginFlow($user, $request); $this->completeLoginFlow($user, $request);
} } catch (NotFoundException) {
catch (NotFoundException $e) {
throw new AccessDeniedException(__('User not found')); throw new AccessDeniedException(__('User not found'));
} }
$redirect = ($priorRoute == '' || $priorRoute == '/' || stripos($priorRoute, $routeParser->urlFor('login'))) ? $routeParser->urlFor('home') : $priorRoute; $redirect = $this->getRedirect($request, $priorRoute);
} } catch (AccessDeniedException $e) {
catch (AccessDeniedException $e) {
$this->getLog()->warning($e->getMessage()); $this->getLog()->warning($e->getMessage());
$this->getFlash()->addMessage('login_message', __('Username or Password incorrect')); $this->getFlash()->addMessage('login_message', __('Username or Password incorrect'));
$this->getFlash()->addMessage('priorRoute', $priorRoute); $this->getFlash()->addMessage('priorRoute', $priorRoute);
} } catch (ExpiredException $e) {
catch (ExpiredException $e) {
$this->getFlash()->addMessage('priorRoute', $priorRoute); $this->getFlash()->addMessage('priorRoute', $priorRoute);
} }
$this->setNoOutput(true); $this->setNoOutput(true);
...@@ -559,18 +557,16 @@ class Login extends Base ...@@ -559,18 +557,16 @@ class Login extends Base
/** /**
* @param Request $request * @param Request $request
* @param Response $response * @param Response $response
* @return \Psr\Http\Message\ResponseInterface|Response * @return \Slim\Http\Response
* @throws \RobThree\Auth\TwoFactorAuthException * @throws \RobThree\Auth\TwoFactorAuthException
* @throws \Xibo\Support\Exception\NotFoundException * @throws \Xibo\Support\Exception\NotFoundException
*/ */
public function twoFactorAuthValidate(Request $request, Response $response) public function twoFactorAuthValidate(Request $request, Response $response): Response
{ {
$user = $this->userFactory->getByName($_SESSION['tfaUsername']); $user = $this->userFactory->getByName($_SESSION['tfaUsername']);
$result = false; $result = false;
$updatedCodes = []; $updatedCodes = [];
$sanitizedParams = $this->getSanitizer($request->getParams()); $sanitizedParams = $this->getSanitizer($request->getParams());
// prior route
$priorRoute = ($sanitizedParams->getString('priorRoute'));
if (isset($_POST['code'])) { if (isset($_POST['code'])) {
$issuerSettings = $this->getConfig()->getSetting('TWOFACTOR_ISSUER'); $issuerSettings = $this->getConfig()->getSetting('TWOFACTOR_ISSUER');
...@@ -594,7 +590,6 @@ class Login extends Base ...@@ -594,7 +590,6 @@ class Login extends Base
$codes = $user->twoFactorRecoveryCodes; $codes = $user->twoFactorRecoveryCodes;
foreach (json_decode($codes) as $code) { foreach (json_decode($codes) as $code) {
// if the provided recovery code matches one stored in the database, we want to log in the user // if the provided recovery code matches one stored in the database, we want to log in the user
if ($code === $sanitizedParams->getString('recoveryCode')) { if ($code === $sanitizedParams->getString('recoveryCode')) {
$result = true; $result = true;
...@@ -603,14 +598,13 @@ class Login extends Base ...@@ -603,14 +598,13 @@ class Login extends Base
if ($code !== $sanitizedParams->getString('recoveryCode')) { if ($code !== $sanitizedParams->getString('recoveryCode')) {
$updatedCodes[] = $code; $updatedCodes[] = $code;
} }
} }
// recovery codes are one time use, as such we want to update user recovery codes and remove the one that was just used.
// recovery codes are one time use, as such we want to update user recovery codes and remove the one that
// was just used.
$user->updateRecoveryCodes(json_encode($updatedCodes)); $user->updateRecoveryCodes(json_encode($updatedCodes));
} }
$redirect = ($priorRoute == '' || $priorRoute == '/' || stripos($priorRoute, $this->urlFor($request,'login'))) ? $this->urlFor($request,'home') : $priorRoute;
if ($result) { if ($result) {
// We are logged in at this point // We are logged in at this point
$this->completeLoginFlow($user, $request); $this->completeLoginFlow($user, $request);
...@@ -620,7 +614,7 @@ class Login extends Base ...@@ -620,7 +614,7 @@ class Login extends Base
//unset the session tfaUsername //unset the session tfaUsername
unset($_SESSION['tfaUsername']); unset($_SESSION['tfaUsername']);
return $response->withRedirect($redirect); return $response->withRedirect($this->getRedirect($request, $sanitizedParams->getString('priorRoute')));
} else { } else {
$this->getLog()->error('Authentication code incorrect, redirecting to login page'); $this->getLog()->error('Authentication code incorrect, redirecting to login page');
$this->getFlash()->addMessage('login_message', __('Authentication code incorrect')); $this->getFlash()->addMessage('login_message', __('Authentication code incorrect'));
...@@ -632,7 +626,7 @@ class Login extends Base ...@@ -632,7 +626,7 @@ class Login extends Base
* @param \Xibo\Entity\User $user * @param \Xibo\Entity\User $user
* @param Request $request * @param Request $request
*/ */
private function completeLoginFlow($user, Request $request) private function completeLoginFlow(User $user, Request $request): void
{ {
$user->touch(); $user->touch();
...@@ -655,4 +649,36 @@ class Login extends Base ...@@ -655,4 +649,36 @@ class Login extends Base
'UserAgent' => $request->getHeader('User-Agent') 'UserAgent' => $request->getHeader('User-Agent')
]); ]);
} }
/**
* Get a redirect link from the given request and prior route
* validate the prior route by only taking its path
* @param \Slim\Http\ServerRequest $request
* @param string|null $priorRoute
* @return string
*/
private function getRedirect(Request $request, ?string $priorRoute): string
{
$home = $this->urlFor($request, 'home');
// Parse the prior route
$parsedPriorRoute = parse_url($priorRoute);
if (!$parsedPriorRoute) {
$priorRoute = $home;
} else {
$priorRoute = $parsedPriorRoute['path'];
}
// Certain routes always lead home
if ($priorRoute == ''
|| $priorRoute == '/'
|| str_contains($priorRoute, $this->urlFor($request, 'login'))
) {
$redirectTo = $home;
} else {
$redirectTo = $priorRoute;
}
return $redirectTo;
}
} }
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment