-
Notifications
You must be signed in to change notification settings - Fork 203
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1782 from ezsystems/ezp26329-rest_session_control…
…ler_refactoring Refactored session rest actions to their own controller (EZP-26329)
- Loading branch information
Showing
5 changed files
with
266 additions
and
142 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
216 changes: 216 additions & 0 deletions
216
eZ/Publish/Core/REST/Server/Controller/SessionController.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,216 @@ | ||
<?php | ||
/** | ||
* @copyright Copyright (C) eZ Systems AS. All rights reserved. | ||
* @license For full copyright and license information view LICENSE file distributed with this source code. | ||
*/ | ||
namespace eZ\Publish\Core\REST\Server\Controller; | ||
|
||
use eZ\Publish\Core\Base\Exceptions\UnauthorizedException; | ||
use eZ\Publish\Core\MVC\Symfony\Security\Authentication\AuthenticatorInterface; | ||
use eZ\Publish\Core\REST\Common\Exceptions\NotFoundException; | ||
use eZ\Publish\Core\REST\Common\Message; | ||
use eZ\Publish\Core\REST\Server\Controller; | ||
use eZ\Publish\Core\REST\Server\Values; | ||
use eZ\Publish\Core\REST\Server\Exceptions; | ||
use Symfony\Component\HttpFoundation\Request; | ||
use Symfony\Component\Security\Csrf\CsrfToken; | ||
use Symfony\Component\Security\Csrf\CsrfTokenManager; | ||
use Symfony\Component\Security\Core\Exception\AccessDeniedException; | ||
use Symfony\Component\Security\Core\Exception\AuthenticationException; | ||
use Symfony\Component\Security\Csrf\TokenStorage\TokenStorageInterface; | ||
|
||
class SessionController extends Controller | ||
{ | ||
/** | ||
* @var \eZ\Publish\Core\MVC\Symfony\Security\Authentication\AuthenticatorInterface | ||
*/ | ||
private $authenticator; | ||
|
||
/** | ||
* @var \Symfony\Component\Security\Csrf\CsrfTokenManager | ||
*/ | ||
private $csrfTokenManager; | ||
|
||
/** | ||
* @var \Symfony\Component\Security\Csrf\TokenStorage\TokenStorageInterface | ||
*/ | ||
private $csrfTokenStorage; | ||
|
||
/** | ||
* @var string | ||
*/ | ||
private $csrfTokenIntention; | ||
|
||
public function __construct( | ||
AuthenticatorInterface $authenticator, | ||
$tokenIntention, | ||
CsrfTokenManager $csrfTokenManager = null, | ||
TokenStorageInterface $csrfTokenStorage = null | ||
) { | ||
$this->authenticator = $authenticator; | ||
$this->csrfTokenIntention = $tokenIntention; | ||
$this->csrfTokenManager = $csrfTokenManager; | ||
$this->csrfTokenStorage = $csrfTokenStorage; | ||
} | ||
|
||
/** | ||
* Creates a new session based on the credentials provided as POST parameters. | ||
* | ||
* @throws \eZ\Publish\Core\Base\Exceptions\UnauthorizedException If the login or password are incorrect or invalid CSRF | ||
* | ||
* @return Values\UserSession|Values\Conflict | ||
*/ | ||
public function createSessionAction(Request $request) | ||
{ | ||
/** @var $sessionInput \eZ\Publish\Core\REST\Server\Values\SessionInput */ | ||
$sessionInput = $this->inputDispatcher->parse( | ||
new Message( | ||
array('Content-Type' => $request->headers->get('Content-Type')), | ||
$request->getContent() | ||
) | ||
); | ||
$request->attributes->set('username', $sessionInput->login); | ||
$request->attributes->set('password', $sessionInput->password); | ||
|
||
try { | ||
$session = $request->getSession(); | ||
if ($session->isStarted() && $this->hasStoredCsrfToken()) { | ||
$this->checkCsrfToken($request); | ||
} | ||
|
||
$token = $this->authenticator->authenticate($request); | ||
$csrfToken = $this->getCsrfToken(); | ||
|
||
return new Values\UserSession( | ||
$token->getUser()->getAPIUser(), | ||
$session->getName(), | ||
$session->getId(), | ||
$csrfToken, | ||
!$token->hasAttribute('isFromSession') | ||
); | ||
} catch (Exceptions\UserConflictException $e) { | ||
// Already logged in with another user, this will be converted to HTTP status 409 | ||
return new Values\Conflict(); | ||
} catch (AuthenticationException $e) { | ||
throw new UnauthorizedException('Invalid login or password', $request->getPathInfo()); | ||
} catch (AccessDeniedException $e) { | ||
throw new UnauthorizedException($e->getMessage(), $request->getPathInfo()); | ||
} | ||
} | ||
|
||
/** | ||
* Refresh given session. | ||
* | ||
* @param string $sessionId | ||
* | ||
* @throws \eZ\Publish\Core\REST\Common\Exceptions\NotFoundException | ||
* | ||
* @return \eZ\Publish\Core\REST\Server\Values\UserSession | ||
*/ | ||
public function refreshSessionAction($sessionId, Request $request) | ||
{ | ||
$session = $request->getSession(); | ||
|
||
if ($session === null || !$session->isStarted() || $session->getId() != $sessionId || !$this->hasStoredCsrfToken()) { | ||
$response = $this->authenticator->logout($request); | ||
$response->setStatusCode(404); | ||
|
||
return $response; | ||
} | ||
|
||
$this->checkCsrfToken($request); | ||
|
||
return new Values\UserSession( | ||
$this->repository->getCurrentUser(), | ||
$session->getName(), | ||
$session->getId(), | ||
$request->headers->get('X-CSRF-Token'), | ||
false | ||
); | ||
} | ||
|
||
/** | ||
* Deletes given session. | ||
* | ||
* @param string $sessionId | ||
* | ||
* @return Values\DeletedUserSession | ||
* | ||
* @throws NotFoundException | ||
*/ | ||
public function deleteSessionAction($sessionId, Request $request) | ||
{ | ||
/** @var $session \Symfony\Component\HttpFoundation\Session\Session */ | ||
$session = $request->getSession(); | ||
if (!$session->isStarted() || $session->getId() != $sessionId || !$this->hasStoredCsrfToken()) { | ||
$response = $this->authenticator->logout($request); | ||
$response->setStatusCode(404); | ||
|
||
return $response; | ||
} | ||
|
||
$this->checkCsrfToken($request); | ||
|
||
return new Values\DeletedUserSession($this->authenticator->logout($request)); | ||
} | ||
|
||
/** | ||
* Tests if a CSRF token is stored. | ||
* | ||
* @return bool | ||
*/ | ||
private function hasStoredCsrfToken() | ||
{ | ||
if (!isset($this->csrfTokenStorage)) { | ||
return true; | ||
} | ||
|
||
return $this->csrfTokenStorage->hasToken($this->csrfTokenIntention); | ||
} | ||
|
||
/** | ||
* Checks the presence / validity of the CSRF token. | ||
* | ||
* @param Request $request | ||
* | ||
* @throws UnauthorizedException if the token is missing or invalid. | ||
*/ | ||
private function checkCsrfToken(Request $request) | ||
{ | ||
if ($this->csrfTokenManager === null) { | ||
return; | ||
} | ||
|
||
$exception = new UnauthorizedException( | ||
'Missing or invalid CSRF token', | ||
$request->getMethod() . ' ' . $request->getPathInfo() | ||
); | ||
|
||
if (!$request->headers->has('X-CSRF-Token')) { | ||
throw $exception; | ||
} | ||
|
||
$csrfToken = new CsrfToken( | ||
$this->csrfTokenIntention, | ||
$request->headers->get('X-CSRF-Token') | ||
); | ||
|
||
if (!$this->csrfTokenManager->isTokenValid($csrfToken)) { | ||
throw $exception; | ||
} | ||
} | ||
|
||
/** | ||
* Returns the csrf token for REST. The token is generated if it doesn't exist. | ||
* | ||
* @return string The csrf token, or an empty string if csrf check is disabled. | ||
*/ | ||
private function getCsrfToken() | ||
{ | ||
if ($this->csrfTokenManager === null) { | ||
return ''; | ||
} | ||
|
||
return $this->csrfTokenManager->getToken($this->csrfTokenIntention)->getValue(); | ||
} | ||
} |
Oops, something went wrong.