From c57c3fe5e056c40b3adb68b185936037d67266b6 Mon Sep 17 00:00:00 2001 From: Lee Peuker Date: Thu, 30 Jun 2022 15:51:26 +0200 Subject: [PATCH 1/7] Add remember me option to frontend --- src/Application/User/Service/Login.php | 6 +----- templates/page/login.html.twig | 18 +++++++++++++----- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/src/Application/User/Service/Login.php b/src/Application/User/Service/Login.php index 4642aac7..8c8aeb35 100644 --- a/src/Application/User/Service/Login.php +++ b/src/Application/User/Service/Login.php @@ -26,11 +26,7 @@ public function authenticate(string $password, bool $rememberMe) : void session_destroy(); ini_set('session.cookie_lifetime', '2419200'); ini_set('session.gc_maxlifetime', '2419200'); - session_start( - [ - 'cookie_lifetime' => 2419200, - ] - ); + session_start(['cookie_lifetime' => 2419200]); } session_regenerate_id(); diff --git a/templates/page/login.html.twig b/templates/page/login.html.twig index 9b740f03..55a4eefc 100644 --- a/templates/page/login.html.twig +++ b/templates/page/login.html.twig @@ -14,12 +14,20 @@ Invalid password {% endif %} + + +
- - -
+ + +
+ +
+
- -
+ + {% endblock %} From 989c6ee3d97e0a21b87119d71ea88ec34a59d242 Mon Sep 17 00:00:00 2001 From: Lee Peuker Date: Thu, 30 Jun 2022 17:28:54 +0200 Subject: [PATCH 2/7] Replace session with token for user authentication --- .../20220630135944_AddUserAuthTokenTable.php | 31 +++++++++++++ src/Application/SessionService.php | 19 +++++++- src/Application/User/Repository.php | 33 +++++++++++++ .../User/Service/Authentication.php | 46 +++++++++++++++++++ src/Application/User/Service/Login.php | 22 ++++----- src/Factory.php | 5 +- .../AuthenticationController.php | 28 ++++++----- src/HttpController/ExportController.php | 2 +- src/HttpController/HistoryController.php | 6 +-- src/HttpController/Letterboxd.php | 2 +- src/HttpController/MovieController.php | 2 +- src/HttpController/PlexController.php | 6 +-- src/HttpController/SettingsController.php | 2 +- src/HttpController/SyncTmdbController.php | 2 +- src/HttpController/SyncTraktController.php | 2 +- src/ValueObject/DateTime.php | 5 ++ 16 files changed, 171 insertions(+), 42 deletions(-) create mode 100644 db/migrations/20220630135944_AddUserAuthTokenTable.php create mode 100644 src/Application/User/Service/Authentication.php diff --git a/db/migrations/20220630135944_AddUserAuthTokenTable.php b/db/migrations/20220630135944_AddUserAuthTokenTable.php new file mode 100644 index 00000000..0686781f --- /dev/null +++ b/db/migrations/20220630135944_AddUserAuthTokenTable.php @@ -0,0 +1,31 @@ +execute( + <<execute( + <<authenticationService->isValidToken($_COOKIE['id']) === true) { + return true; + } + + if (empty($_COOKIE['id']) === false) { + unset($_COOKIE['id']); + setcookie('id', '', -1); + } + + return false; } } diff --git a/src/Application/User/Repository.php b/src/Application/User/Repository.php index 2a8e4891..a19b9020 100644 --- a/src/Application/User/Repository.php +++ b/src/Application/User/Repository.php @@ -3,6 +3,7 @@ namespace Movary\Application\User; use Doctrine\DBAL\Connection; +use Movary\ValueObject\DateTime; class Repository { @@ -12,6 +13,27 @@ public function __construct(private readonly Connection $dbConnection) { } + public function createAuthToken(string $token, DateTime $expirationDate) : void + { + $this->dbConnection->insert( + 'user_auth_token', + [ + 'token' => $token, + 'expiration_date' => (string)$expirationDate, + ] + ); + } + + public function deleteAuthToken(string $token) : void + { + $this->dbConnection->delete( + 'user_auth_token', + [ + 'token' => $token, + ] + ); + } + public function fetchAdminUser() : Entity { $data = $this->dbConnection->fetchAssociative('SELECT * FROM `user` WHERE `id` = ?', [self::ADMIN_USER_IO]); @@ -23,6 +45,17 @@ public function fetchAdminUser() : Entity return Entity::createFromArray($data); } + public function findAuthTokenExpirationDate(string $token) : ?DateTime + { + $expirationDate = $this->dbConnection->fetchOne('SELECT `expiration_date` FROM `user_auth_token` WHERE `token` = ?', [$token]); + + if ($expirationDate === false) { + return null; + } + + return DateTime::createFromString($expirationDate); + } + public function setPlexWebhookId(?string $plexWebhookId) : void { $this->dbConnection->update( diff --git a/src/Application/User/Service/Authentication.php b/src/Application/User/Service/Authentication.php new file mode 100644 index 00000000..48d2d9cf --- /dev/null +++ b/src/Application/User/Service/Authentication.php @@ -0,0 +1,46 @@ +repository->deleteAuthToken($token); + } + + public function generateToken(?DateTime $expirationDate = null) : string + { + if ($expirationDate === null) { + $expirationDate = DateTime::createFromString(date('Y-m-d H:i:s', strtotime('+1 day', time()))); + } + + $token = bin2hex(random_bytes(16)); + + $this->repository->createAuthToken($token, $expirationDate); + + return $token; + } + + public function isValidToken(string $token) : bool + { + $tokenExpirationDate = $this->repository->findAuthTokenExpirationDate($token); + + if ($tokenExpirationDate === null || $tokenExpirationDate->isAfter(DateTime::create()) === false) { + if ($tokenExpirationDate !== null) { + $this->repository->deleteAuthToken($token); + } + + return false; + } + + return true; + } +} diff --git a/src/Application/User/Service/Login.php b/src/Application/User/Service/Login.php index 8c8aeb35..5ef5f79c 100644 --- a/src/Application/User/Service/Login.php +++ b/src/Application/User/Service/Login.php @@ -4,17 +4,17 @@ use Movary\Application\User\Exception\InvalidPassword; use Movary\Application\User\Repository; +use Movary\ValueObject\DateTime; class Login { - private Repository $userRepository; - - public function __construct(Repository $userRepository) - { - $this->userRepository = $userRepository; + public function __construct( + private readonly Repository $userRepository, + private readonly Authentication $authenticationService + ) { } - public function authenticate(string $password, bool $rememberMe) : void + public function login(string $password, bool $rememberMe) : void { $user = $this->userRepository->fetchAdminUser(); @@ -22,15 +22,13 @@ public function authenticate(string $password, bool $rememberMe) : void throw InvalidPassword::create(); } + $expirationTime = DateTime::createFromString(date('Y-m-d H:i:s', strtotime('+1 day'))); if ($rememberMe === true) { - session_destroy(); - ini_set('session.cookie_lifetime', '2419200'); - ini_set('session.gc_maxlifetime', '2419200'); - session_start(['cookie_lifetime' => 2419200]); + $expirationTime = DateTime::createFromString(date('Y-m-d H:i:s', strtotime('+30 day'))); } - session_regenerate_id(); + $token = $this->authenticationService->generateToken(DateTime::createFromString((string)$expirationTime)); - $_SESSION['user']['id'] = $user->getId(); + setcookie('id', $token, (int)$expirationTime->format('U')); } } diff --git a/src/Factory.php b/src/Factory.php index def3ff5e..b03ca854 100644 --- a/src/Factory.php +++ b/src/Factory.php @@ -10,12 +10,9 @@ use Movary\Api\Tmdb; use Movary\Api\Trakt; use Movary\Api\Trakt\Cache\User\Movie\Watched; -use Movary\Application\Movie; -use Movary\Application\Service\Tmdb\SyncMovie; use Movary\Application\SessionService; use Movary\Application\SyncLog; use Movary\Command; -use Movary\HttpController\PlexController; use Movary\HttpController\SettingsController; use Movary\ValueObject\Config; use Movary\ValueObject\Http\Request; @@ -130,7 +127,7 @@ public static function createTwigEnvironment(ContainerInterface $container) : Tw { $twig = new Twig\Environment($container->get(Twig\Loader\LoaderInterface::class)); - $twig->addGlobal('loggedIn', $container->get(SessionService::class)->isCurrentUserLoggedIn()); + $twig->addGlobal('loggedIn', $container->get(SessionService::class)->isUserAuthenticated()); return $twig; } diff --git a/src/HttpController/AuthenticationController.php b/src/HttpController/AuthenticationController.php index 744e348e..0c325f09 100644 --- a/src/HttpController/AuthenticationController.php +++ b/src/HttpController/AuthenticationController.php @@ -2,6 +2,7 @@ namespace Movary\HttpController; +use Movary\Application\SessionService; use Movary\Application\User\Exception\InvalidPassword; use Movary\Application\User\Service; use Movary\ValueObject\Http\Header; @@ -12,19 +13,17 @@ class AuthenticationController { - private Environment $twig; - - private Service\Login $userLoginService; - - public function __construct(Environment $twig, Service\Login $userLoginService) - { - $this->twig = $twig; - $this->userLoginService = $userLoginService; + public function __construct( + private readonly Environment $twig, + private readonly Service\Login $userLoginService, + private readonly Service\Authentication $authenticationService, + private readonly SessionService $sessionService, + ) { } public function login(Request $request) : Response { - if (isset($_SESSION['user']) === true) { + if ($this->sessionService->isUserAuthenticated() === true) { return Response::create( StatusCode::createSeeOther(), null, @@ -33,7 +32,7 @@ public function login(Request $request) : Response } try { - $this->userLoginService->authenticate( + $this->userLoginService->login( $request->getPostParameters()['password'], isset($request->getPostParameters()['rememberMe']) === true ); @@ -50,9 +49,14 @@ public function login(Request $request) : Response public function logout() : Response { - unset($_SESSION['user']); session_regenerate_id(); + if (isset($_COOKIE['id']) === true) { + $this->authenticationService->deleteToken($_COOKIE['id']); + unset($_COOKIE['id']); + setcookie('id', '', -1); + } + return Response::create( StatusCode::createSeeOther(), null, @@ -62,7 +66,7 @@ public function logout() : Response public function renderLoginPage() : Response { - if (isset($_SESSION['user']) === true) { + if ($this->sessionService->isUserAuthenticated() === true) { return Response::create( StatusCode::createSeeOther(), null, diff --git a/src/HttpController/ExportController.php b/src/HttpController/ExportController.php index de8ab187..bc2a0b01 100644 --- a/src/HttpController/ExportController.php +++ b/src/HttpController/ExportController.php @@ -17,7 +17,7 @@ public function __construct( public function getCsvExport(Request $request) : Response { - if ($this->sessionService->isCurrentUserLoggedIn() === false) { + if ($this->sessionService->isUserAuthenticated() === false) { return Response::createFoundRedirect('/login'); } diff --git a/src/HttpController/HistoryController.php b/src/HttpController/HistoryController.php index ad7c1353..ec765b79 100644 --- a/src/HttpController/HistoryController.php +++ b/src/HttpController/HistoryController.php @@ -33,7 +33,7 @@ public function __construct( public function deleteHistoryEntry(Request $request) : Response { - if ($this->sessionService->isCurrentUserLoggedIn() === false) { + if ($this->sessionService->isUserAuthenticated() === false) { return Response::createFoundRedirect('/'); } @@ -50,7 +50,7 @@ public function deleteHistoryEntry(Request $request) : Response public function logMovie(Request $request) : Response { - if ($this->sessionService->isCurrentUserLoggedIn() === false) { + if ($this->sessionService->isUserAuthenticated() === false) { return Response::createFoundRedirect('/'); } @@ -106,7 +106,7 @@ public function renderHistory(Request $request) : Response public function renderLogMoviePage(Request $request) : Response { - if ($this->sessionService->isCurrentUserLoggedIn() === false) { + if ($this->sessionService->isUserAuthenticated() === false) { return Response::createFoundRedirect('/'); } diff --git a/src/HttpController/Letterboxd.php b/src/HttpController/Letterboxd.php index 781d3d88..d845a75e 100644 --- a/src/HttpController/Letterboxd.php +++ b/src/HttpController/Letterboxd.php @@ -21,7 +21,7 @@ public function __construct( public function uploadRatingCsv(Request $httpRequest) : Response { - if ($this->sessionService->isCurrentUserLoggedIn() === false) { + if ($this->sessionService->isUserAuthenticated() === false) { return Response::createFoundRedirect('/'); } diff --git a/src/HttpController/MovieController.php b/src/HttpController/MovieController.php index 05e69e52..55455cc4 100644 --- a/src/HttpController/MovieController.php +++ b/src/HttpController/MovieController.php @@ -37,7 +37,7 @@ public function renderPage(Request $request) : Response public function updateRating(Request $request) : Response { - if ($this->sessionService->isCurrentUserLoggedIn() === false) { + if ($this->sessionService->isUserAuthenticated() === false) { return Response::createFoundRedirect('/'); } diff --git a/src/HttpController/PlexController.php b/src/HttpController/PlexController.php index 54a9f4d1..1d665a12 100644 --- a/src/HttpController/PlexController.php +++ b/src/HttpController/PlexController.php @@ -26,7 +26,7 @@ public function __construct( public function deletePlexWebhookId() : Response { - if ($this->sessionService->isCurrentUserLoggedIn() === false) { + if ($this->sessionService->isUserAuthenticated() === false) { return Response::createFoundRedirect('/'); } @@ -37,7 +37,7 @@ public function deletePlexWebhookId() : Response public function getPlexWebhookId() : Response { - if ($this->sessionService->isCurrentUserLoggedIn() === false) { + if ($this->sessionService->isUserAuthenticated() === false) { return Response::createFoundRedirect('/'); } @@ -91,7 +91,7 @@ public function handlePlexWebhook(Request $request) : Response public function regeneratePlexWebhookId() : Response { - if ($this->sessionService->isCurrentUserLoggedIn() === false) { + if ($this->sessionService->isUserAuthenticated() === false) { return Response::createFoundRedirect('/'); } diff --git a/src/HttpController/SettingsController.php b/src/HttpController/SettingsController.php index 8939f00c..ec092c2a 100644 --- a/src/HttpController/SettingsController.php +++ b/src/HttpController/SettingsController.php @@ -20,7 +20,7 @@ public function __construct( public function render() : Response { - if ($this->sessionService->isCurrentUserLoggedIn() === false) { + if ($this->sessionService->isUserAuthenticated() === false) { return Response::createFoundRedirect('/'); } diff --git a/src/HttpController/SyncTmdbController.php b/src/HttpController/SyncTmdbController.php index 76deff52..37e03ff1 100644 --- a/src/HttpController/SyncTmdbController.php +++ b/src/HttpController/SyncTmdbController.php @@ -16,7 +16,7 @@ public function __construct( public function execute() : Response { - if ($this->sessionService->isCurrentUserLoggedIn() === false) { + if ($this->sessionService->isUserAuthenticated() === false) { return Response::createFoundRedirect('/'); } diff --git a/src/HttpController/SyncTraktController.php b/src/HttpController/SyncTraktController.php index 22491ed2..3ad44b6f 100644 --- a/src/HttpController/SyncTraktController.php +++ b/src/HttpController/SyncTraktController.php @@ -14,7 +14,7 @@ public function __construct( public function execute() : Response { - if ($this->sessionService->isCurrentUserLoggedIn() === false) { + if ($this->sessionService->isUserAuthenticated() === false) { return Response::createFoundRedirect('/'); } diff --git a/src/ValueObject/DateTime.php b/src/ValueObject/DateTime.php index 483e97b1..7d425891 100644 --- a/src/ValueObject/DateTime.php +++ b/src/ValueObject/DateTime.php @@ -47,6 +47,11 @@ public function format(string $format) : string return (new \DateTime($this->dateTime))->format($format); } + public function isAfter(DateTime $dateTimeToCompare) : bool + { + return $this->dateTime > $dateTimeToCompare->dateTime; + } + public function isEqual(DateTime $lastUpdated) : bool { return (string)$this === (string)$lastUpdated; From b553651687edc3a4f926f99985ed7fa09c682739 Mon Sep 17 00:00:00 2001 From: Lee Peuker Date: Thu, 30 Jun 2022 17:47:33 +0200 Subject: [PATCH 3/7] Cleanup --- .../User/Service/Authentication.php | 56 ++++++++++++++----- src/Application/User/Service/Login.php | 34 ----------- .../AuthenticationController.php | 3 +- 3 files changed, 44 insertions(+), 49 deletions(-) delete mode 100644 src/Application/User/Service/Login.php diff --git a/src/Application/User/Service/Authentication.php b/src/Application/User/Service/Authentication.php index 48d2d9cf..0aea3188 100644 --- a/src/Application/User/Service/Authentication.php +++ b/src/Application/User/Service/Authentication.php @@ -2,6 +2,7 @@ namespace Movary\Application\User\Service; +use Movary\Application\User\Exception\InvalidPassword; use Movary\Application\User\Repository; use Movary\ValueObject\DateTime; @@ -16,19 +17,6 @@ public function deleteToken(string $token) : void $this->repository->deleteAuthToken($token); } - public function generateToken(?DateTime $expirationDate = null) : string - { - if ($expirationDate === null) { - $expirationDate = DateTime::createFromString(date('Y-m-d H:i:s', strtotime('+1 day', time()))); - } - - $token = bin2hex(random_bytes(16)); - - $this->repository->createAuthToken($token, $expirationDate); - - return $token; - } - public function isValidToken(string $token) : bool { $tokenExpirationDate = $this->repository->findAuthTokenExpirationDate($token); @@ -43,4 +31,46 @@ public function isValidToken(string $token) : bool return true; } + + public function login(string $password, bool $rememberMe) : void + { + $user = $this->repository->fetchAdminUser(); + + if (password_verify($password, $user->getPasswordHash()) === false) { + throw InvalidPassword::create(); + } + + $expirationDate = $this->createExpirationDate(); + if ($rememberMe === true) { + $expirationDate = $this->createExpirationDate(30); + } + + $token = $this->generateToken(DateTime::createFromString((string)$expirationDate)); + + setcookie('id', $token, (int)$expirationDate->format('U')); + } + + private function createExpirationDate(int $days = 1) : DateTime + { + $timestamp = strtotime('+' . $days . ' day'); + + if ($timestamp === false) { + throw new \RuntimeException('Could not generate timestamp for auth token expiration date.'); + } + + return DateTime::createFromString(date('Y-m-d H:i:s', $timestamp)); + } + + private function generateToken(?DateTime $expirationDate = null) : string + { + if ($expirationDate === null) { + $expirationDate = $this->createExpirationDate(); + } + + $token = bin2hex(random_bytes(16)); + + $this->repository->createAuthToken($token, $expirationDate); + + return $token; + } } diff --git a/src/Application/User/Service/Login.php b/src/Application/User/Service/Login.php deleted file mode 100644 index 5ef5f79c..00000000 --- a/src/Application/User/Service/Login.php +++ /dev/null @@ -1,34 +0,0 @@ -userRepository->fetchAdminUser(); - - if (password_verify($password, $user->getPasswordHash()) === false) { - throw InvalidPassword::create(); - } - - $expirationTime = DateTime::createFromString(date('Y-m-d H:i:s', strtotime('+1 day'))); - if ($rememberMe === true) { - $expirationTime = DateTime::createFromString(date('Y-m-d H:i:s', strtotime('+30 day'))); - } - - $token = $this->authenticationService->generateToken(DateTime::createFromString((string)$expirationTime)); - - setcookie('id', $token, (int)$expirationTime->format('U')); - } -} diff --git a/src/HttpController/AuthenticationController.php b/src/HttpController/AuthenticationController.php index 0c325f09..7dab50a4 100644 --- a/src/HttpController/AuthenticationController.php +++ b/src/HttpController/AuthenticationController.php @@ -15,7 +15,6 @@ class AuthenticationController { public function __construct( private readonly Environment $twig, - private readonly Service\Login $userLoginService, private readonly Service\Authentication $authenticationService, private readonly SessionService $sessionService, ) { @@ -32,7 +31,7 @@ public function login(Request $request) : Response } try { - $this->userLoginService->login( + $this->authenticationService->login( $request->getPostParameters()['password'], isset($request->getPostParameters()['rememberMe']) === true ); From a60a10e581e251c2a6d912ac11ea215c857d9388 Mon Sep 17 00:00:00 2001 From: Lee Peuker Date: Thu, 30 Jun 2022 18:00:16 +0200 Subject: [PATCH 4/7] CLeanup --- src/Application/SessionService.php | 26 --------------- .../User/Service/Authentication.php | 32 ++++++++++++++----- src/Factory.php | 6 ++-- .../AuthenticationController.php | 11 ++++--- src/HttpController/ExportController.php | 6 ++-- src/HttpController/HistoryController.php | 10 +++--- src/HttpController/Letterboxd.php | 6 ++-- src/HttpController/MovieController.php | 6 ++-- src/HttpController/PlexController.php | 10 +++--- src/HttpController/SettingsController.php | 6 ++-- src/HttpController/SyncTmdbController.php | 8 ++--- src/HttpController/SyncTraktController.php | 6 ++-- 12 files changed, 61 insertions(+), 72 deletions(-) delete mode 100644 src/Application/SessionService.php diff --git a/src/Application/SessionService.php b/src/Application/SessionService.php deleted file mode 100644 index 990d9c2b..00000000 --- a/src/Application/SessionService.php +++ /dev/null @@ -1,26 +0,0 @@ -authenticationService->isValidToken($_COOKIE['id']) === true) { - return true; - } - - if (empty($_COOKIE['id']) === false) { - unset($_COOKIE['id']); - setcookie('id', '', -1); - } - - return false; - } -} diff --git a/src/Application/User/Service/Authentication.php b/src/Application/User/Service/Authentication.php index 0aea3188..38ad9acf 100644 --- a/src/Application/User/Service/Authentication.php +++ b/src/Application/User/Service/Authentication.php @@ -17,19 +17,20 @@ public function deleteToken(string $token) : void $this->repository->deleteAuthToken($token); } - public function isValidToken(string $token) : bool + public function isUserAuthenticated() : bool { - $tokenExpirationDate = $this->repository->findAuthTokenExpirationDate($token); + $token = filter_input(INPUT_COOKIE, 'id'); - if ($tokenExpirationDate === null || $tokenExpirationDate->isAfter(DateTime::create()) === false) { - if ($tokenExpirationDate !== null) { - $this->repository->deleteAuthToken($token); - } + if (empty($token) === false && $this->isValidToken($token) === true) { + return true; + } - return false; + if (empty($token) === false) { + unset($_COOKIE['id']); + setcookie('id', '', -1); } - return true; + return false; } public function login(string $password, bool $rememberMe) : void @@ -73,4 +74,19 @@ private function generateToken(?DateTime $expirationDate = null) : string return $token; } + + private function isValidToken(string $token) : bool + { + $tokenExpirationDate = $this->repository->findAuthTokenExpirationDate($token); + + if ($tokenExpirationDate === null || $tokenExpirationDate->isAfter(DateTime::create()) === false) { + if ($tokenExpirationDate !== null) { + $this->repository->deleteAuthToken($token); + } + + return false; + } + + return true; + } } diff --git a/src/Factory.php b/src/Factory.php index b03ca854..3dc35a53 100644 --- a/src/Factory.php +++ b/src/Factory.php @@ -10,8 +10,8 @@ use Movary\Api\Tmdb; use Movary\Api\Trakt; use Movary\Api\Trakt\Cache\User\Movie\Watched; -use Movary\Application\SessionService; use Movary\Application\SyncLog; +use Movary\Application\User\Service\Authentication; use Movary\Command; use Movary\HttpController\SettingsController; use Movary\ValueObject\Config; @@ -93,7 +93,7 @@ public static function createSettingsController(ContainerInterface $container, C return new SettingsController( $container->get(Twig\Environment::class), $container->get(SyncLog\Repository::class), - $container->get(SessionService::class), + $container->get(Authentication::class), $applicationVersion ); } @@ -127,7 +127,7 @@ public static function createTwigEnvironment(ContainerInterface $container) : Tw { $twig = new Twig\Environment($container->get(Twig\Loader\LoaderInterface::class)); - $twig->addGlobal('loggedIn', $container->get(SessionService::class)->isUserAuthenticated()); + $twig->addGlobal('loggedIn', $container->get(Authentication::class)->isUserAuthenticated()); return $twig; } diff --git a/src/HttpController/AuthenticationController.php b/src/HttpController/AuthenticationController.php index 7dab50a4..eab25121 100644 --- a/src/HttpController/AuthenticationController.php +++ b/src/HttpController/AuthenticationController.php @@ -16,13 +16,12 @@ class AuthenticationController public function __construct( private readonly Environment $twig, private readonly Service\Authentication $authenticationService, - private readonly SessionService $sessionService, ) { } public function login(Request $request) : Response { - if ($this->sessionService->isUserAuthenticated() === true) { + if ($this->authenticationService->isUserAuthenticated() === true) { return Response::create( StatusCode::createSeeOther(), null, @@ -50,8 +49,10 @@ public function logout() : Response { session_regenerate_id(); - if (isset($_COOKIE['id']) === true) { - $this->authenticationService->deleteToken($_COOKIE['id']); + $token = filter_input(INPUT_COOKIE, 'id'); + + if ($token !== null) { + $this->authenticationService->deleteToken($token); unset($_COOKIE['id']); setcookie('id', '', -1); } @@ -65,7 +66,7 @@ public function logout() : Response public function renderLoginPage() : Response { - if ($this->sessionService->isUserAuthenticated() === true) { + if ($this->authenticationService->isUserAuthenticated() === true) { return Response::create( StatusCode::createSeeOther(), null, diff --git a/src/HttpController/ExportController.php b/src/HttpController/ExportController.php index bc2a0b01..a175fe4c 100644 --- a/src/HttpController/ExportController.php +++ b/src/HttpController/ExportController.php @@ -3,21 +3,21 @@ namespace Movary\HttpController; use Movary\Application\ExportService; -use Movary\Application\SessionService; +use Movary\Application\User\Service\Authentication; use Movary\ValueObject\Http\Request; use Movary\ValueObject\Http\Response; class ExportController { public function __construct( - private readonly SessionService $sessionService, + private readonly Authentication $authenticationService, private readonly ExportService $exportService ) { } public function getCsvExport(Request $request) : Response { - if ($this->sessionService->isUserAuthenticated() === false) { + if ($this->authenticationService->isUserAuthenticated() === false) { return Response::createFoundRedirect('/login'); } diff --git a/src/HttpController/HistoryController.php b/src/HttpController/HistoryController.php index ec765b79..994e62ec 100644 --- a/src/HttpController/HistoryController.php +++ b/src/HttpController/HistoryController.php @@ -6,7 +6,7 @@ use Movary\Application\Movie; use Movary\Application\Movie\History\Service\Select; use Movary\Application\Service\Tmdb\SyncMovie; -use Movary\Application\SessionService; +use Movary\Application\User\Service\Authentication; use Movary\Util\Json; use Movary\ValueObject\Date; use Movary\ValueObject\DateTime; @@ -27,13 +27,13 @@ public function __construct( private readonly Tmdb\Api $tmdbApi, private readonly Movie\Api $movieApi, private readonly SyncMovie $tmdbMovieSyncService, - private readonly SessionService $sessionService + private readonly Authentication $authenticationService ) { } public function deleteHistoryEntry(Request $request) : Response { - if ($this->sessionService->isUserAuthenticated() === false) { + if ($this->authenticationService->isUserAuthenticated() === false) { return Response::createFoundRedirect('/'); } @@ -50,7 +50,7 @@ public function deleteHistoryEntry(Request $request) : Response public function logMovie(Request $request) : Response { - if ($this->sessionService->isUserAuthenticated() === false) { + if ($this->authenticationService->isUserAuthenticated() === false) { return Response::createFoundRedirect('/'); } @@ -106,7 +106,7 @@ public function renderHistory(Request $request) : Response public function renderLogMoviePage(Request $request) : Response { - if ($this->sessionService->isUserAuthenticated() === false) { + if ($this->authenticationService->isUserAuthenticated() === false) { return Response::createFoundRedirect('/'); } diff --git a/src/HttpController/Letterboxd.php b/src/HttpController/Letterboxd.php index d845a75e..666395e7 100644 --- a/src/HttpController/Letterboxd.php +++ b/src/HttpController/Letterboxd.php @@ -3,7 +3,7 @@ namespace Movary\HttpController; use Movary\Application\Service\Letterboxd\SyncRatings; -use Movary\Application\SessionService; +use Movary\Application\User\Service\Authentication; use Movary\ValueObject\Http\Header; use Movary\ValueObject\Http\Request; use Movary\ValueObject\Http\Response; @@ -15,13 +15,13 @@ class Letterboxd public function __construct( private readonly SyncRatings $syncRatings, private readonly LoggerInterface $logger, - private readonly SessionService $sessionService, + private readonly Authentication $authenticationService, ) { } public function uploadRatingCsv(Request $httpRequest) : Response { - if ($this->sessionService->isUserAuthenticated() === false) { + if ($this->authenticationService->isUserAuthenticated() === false) { return Response::createFoundRedirect('/'); } diff --git a/src/HttpController/MovieController.php b/src/HttpController/MovieController.php index 55455cc4..58c1c062 100644 --- a/src/HttpController/MovieController.php +++ b/src/HttpController/MovieController.php @@ -3,7 +3,7 @@ namespace Movary\HttpController; use Movary\Application\Movie; -use Movary\Application\SessionService; +use Movary\Application\User\Service\Authentication; use Movary\ValueObject\Http\Request; use Movary\ValueObject\Http\Response; use Movary\ValueObject\Http\StatusCode; @@ -15,7 +15,7 @@ class MovieController public function __construct( private readonly Environment $twig, private readonly Movie\Api $movieApi, - private readonly SessionService $sessionService + private readonly Authentication $authenticationService ) { } @@ -37,7 +37,7 @@ public function renderPage(Request $request) : Response public function updateRating(Request $request) : Response { - if ($this->sessionService->isUserAuthenticated() === false) { + if ($this->authenticationService->isUserAuthenticated() === false) { return Response::createFoundRedirect('/'); } diff --git a/src/HttpController/PlexController.php b/src/HttpController/PlexController.php index 1d665a12..a69a2755 100644 --- a/src/HttpController/PlexController.php +++ b/src/HttpController/PlexController.php @@ -4,8 +4,8 @@ use Movary\Application\Movie; use Movary\Application\Service\Tmdb\SyncMovie; -use Movary\Application\SessionService; use Movary\Application\User\Api; +use Movary\Application\User\Service\Authentication; use Movary\Util\Json; use Movary\ValueObject\Date; use Movary\ValueObject\Http\Request; @@ -20,13 +20,13 @@ public function __construct( private readonly Movie\Api $movieApi, private readonly SyncMovie $tmdbMovieSyncService, private readonly Api $userApi, - private readonly SessionService $sessionService + private readonly Authentication $authenticationService ) { } public function deletePlexWebhookId() : Response { - if ($this->sessionService->isUserAuthenticated() === false) { + if ($this->authenticationService->isUserAuthenticated() === false) { return Response::createFoundRedirect('/'); } @@ -37,7 +37,7 @@ public function deletePlexWebhookId() : Response public function getPlexWebhookId() : Response { - if ($this->sessionService->isUserAuthenticated() === false) { + if ($this->authenticationService->isUserAuthenticated() === false) { return Response::createFoundRedirect('/'); } @@ -91,7 +91,7 @@ public function handlePlexWebhook(Request $request) : Response public function regeneratePlexWebhookId() : Response { - if ($this->sessionService->isUserAuthenticated() === false) { + if ($this->authenticationService->isUserAuthenticated() === false) { return Response::createFoundRedirect('/'); } diff --git a/src/HttpController/SettingsController.php b/src/HttpController/SettingsController.php index ec092c2a..d902e4b2 100644 --- a/src/HttpController/SettingsController.php +++ b/src/HttpController/SettingsController.php @@ -2,8 +2,8 @@ namespace Movary\HttpController; -use Movary\Application\SessionService; use Movary\Application\SyncLog\Repository; +use Movary\Application\User\Service\Authentication; use Movary\ValueObject\Http\Response; use Movary\ValueObject\Http\StatusCode; use Twig\Environment; @@ -13,14 +13,14 @@ class SettingsController public function __construct( private readonly Environment $twig, private readonly Repository $syncLogRepository, - private readonly SessionService $sessionService, + private readonly Authentication $authenticationService, private readonly ?string $applicationVersion = null, ) { } public function render() : Response { - if ($this->sessionService->isUserAuthenticated() === false) { + if ($this->authenticationService->isUserAuthenticated() === false) { return Response::createFoundRedirect('/'); } diff --git a/src/HttpController/SyncTmdbController.php b/src/HttpController/SyncTmdbController.php index 37e03ff1..457fd6b0 100644 --- a/src/HttpController/SyncTmdbController.php +++ b/src/HttpController/SyncTmdbController.php @@ -2,21 +2,19 @@ namespace Movary\HttpController; -use Movary\Application\SessionService; -use Movary\ValueObject\Http\Header; +use Movary\Application\User\Service\Authentication; use Movary\ValueObject\Http\Response; -use Movary\ValueObject\Http\StatusCode; class SyncTmdbController { public function __construct( - private readonly SessionService $sessionService + private readonly Authentication $authenticationService ) { } public function execute() : Response { - if ($this->sessionService->isUserAuthenticated() === false) { + if ($this->authenticationService->isUserAuthenticated() === false) { return Response::createFoundRedirect('/'); } diff --git a/src/HttpController/SyncTraktController.php b/src/HttpController/SyncTraktController.php index 3ad44b6f..d8430a08 100644 --- a/src/HttpController/SyncTraktController.php +++ b/src/HttpController/SyncTraktController.php @@ -2,19 +2,19 @@ namespace Movary\HttpController; -use Movary\Application\SessionService; +use Movary\Application\User\Service\Authentication; use Movary\ValueObject\Http\Response; class SyncTraktController { public function __construct( - private readonly SessionService $sessionService + private readonly Authentication $authenticationService ) { } public function execute() : Response { - if ($this->sessionService->isUserAuthenticated() === false) { + if ($this->authenticationService->isUserAuthenticated() === false) { return Response::createFoundRedirect('/'); } From fca43173b695c79f397fbe1253ea873d053e736c Mon Sep 17 00:00:00 2001 From: Lee Peuker Date: Thu, 30 Jun 2022 18:03:20 +0200 Subject: [PATCH 5/7] Cleanup --- .../User/Service/Authentication.php | 18 +++++++++++++----- .../AuthenticationController.php | 8 -------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/Application/User/Service/Authentication.php b/src/Application/User/Service/Authentication.php index 38ad9acf..df757a3c 100644 --- a/src/Application/User/Service/Authentication.php +++ b/src/Application/User/Service/Authentication.php @@ -8,6 +8,10 @@ class Authentication { + private const AUTHENTICATION_COOKIE_NAME = 'id'; + + private const MAX_EXPIRATION_AGE_IN_DAYS = 30; + public function __construct(private readonly Repository $repository) { } @@ -19,15 +23,15 @@ public function deleteToken(string $token) : void public function isUserAuthenticated() : bool { - $token = filter_input(INPUT_COOKIE, 'id'); + $token = filter_input(INPUT_COOKIE, self::AUTHENTICATION_COOKIE_NAME); if (empty($token) === false && $this->isValidToken($token) === true) { return true; } if (empty($token) === false) { - unset($_COOKIE['id']); - setcookie('id', '', -1); + unset($_COOKIE[self::AUTHENTICATION_COOKIE_NAME]); + setcookie(self::AUTHENTICATION_COOKIE_NAME, '', -1); } return false; @@ -35,6 +39,10 @@ public function isUserAuthenticated() : bool public function login(string $password, bool $rememberMe) : void { + if ($this->isUserAuthenticated() === true) { + return; + } + $user = $this->repository->fetchAdminUser(); if (password_verify($password, $user->getPasswordHash()) === false) { @@ -43,12 +51,12 @@ public function login(string $password, bool $rememberMe) : void $expirationDate = $this->createExpirationDate(); if ($rememberMe === true) { - $expirationDate = $this->createExpirationDate(30); + $expirationDate = $this->createExpirationDate(self::MAX_EXPIRATION_AGE_IN_DAYS); } $token = $this->generateToken(DateTime::createFromString((string)$expirationDate)); - setcookie('id', $token, (int)$expirationDate->format('U')); + setcookie(self::AUTHENTICATION_COOKIE_NAME, $token, (int)$expirationDate->format('U')); } private function createExpirationDate(int $days = 1) : DateTime diff --git a/src/HttpController/AuthenticationController.php b/src/HttpController/AuthenticationController.php index eab25121..868a9cfb 100644 --- a/src/HttpController/AuthenticationController.php +++ b/src/HttpController/AuthenticationController.php @@ -21,14 +21,6 @@ public function __construct( public function login(Request $request) : Response { - if ($this->authenticationService->isUserAuthenticated() === true) { - return Response::create( - StatusCode::createSeeOther(), - null, - [Header::createLocation($_SERVER['HTTP_REFERER'])] - ); - } - try { $this->authenticationService->login( $request->getPostParameters()['password'], From 5b045fd54328bad4f38e9d67baf1709db7ec4065 Mon Sep 17 00:00:00 2001 From: Lee Peuker Date: Thu, 30 Jun 2022 18:05:18 +0200 Subject: [PATCH 6/7] Cleanup --- src/Application/User/Service/Authentication.php | 13 +++++++++++++ src/HttpController/AuthenticationController.php | 10 +--------- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/src/Application/User/Service/Authentication.php b/src/Application/User/Service/Authentication.php index df757a3c..f9159033 100644 --- a/src/Application/User/Service/Authentication.php +++ b/src/Application/User/Service/Authentication.php @@ -59,6 +59,19 @@ public function login(string $password, bool $rememberMe) : void setcookie(self::AUTHENTICATION_COOKIE_NAME, $token, (int)$expirationDate->format('U')); } + public function logout() : void + { + $token = filter_input(INPUT_COOKIE, 'id'); + + if ($token !== null) { + $this->deleteToken($token); + unset($_COOKIE[self::AUTHENTICATION_COOKIE_NAME]); + setcookie(self::AUTHENTICATION_COOKIE_NAME, '', -1); + } + + session_regenerate_id(); + } + private function createExpirationDate(int $days = 1) : DateTime { $timestamp = strtotime('+' . $days . ' day'); diff --git a/src/HttpController/AuthenticationController.php b/src/HttpController/AuthenticationController.php index 868a9cfb..7639bd14 100644 --- a/src/HttpController/AuthenticationController.php +++ b/src/HttpController/AuthenticationController.php @@ -39,15 +39,7 @@ public function login(Request $request) : Response public function logout() : Response { - session_regenerate_id(); - - $token = filter_input(INPUT_COOKIE, 'id'); - - if ($token !== null) { - $this->authenticationService->deleteToken($token); - unset($_COOKIE['id']); - setcookie('id', '', -1); - } + $this->authenticationService->logout(); return Response::create( StatusCode::createSeeOther(), From 452dfbe4d7a11c0eb0135f9f0404d7edbb868a68 Mon Sep 17 00:00:00 2001 From: Lee Peuker Date: Fri, 1 Jul 2022 10:55:45 +0200 Subject: [PATCH 7/7] Expire login cookie with session end without remember me option --- src/Application/User/Service/Authentication.php | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/Application/User/Service/Authentication.php b/src/Application/User/Service/Authentication.php index f9159033..cc8cef18 100644 --- a/src/Application/User/Service/Authentication.php +++ b/src/Application/User/Service/Authentication.php @@ -49,14 +49,17 @@ public function login(string $password, bool $rememberMe) : void throw InvalidPassword::create(); } - $expirationDate = $this->createExpirationDate(); + $authTokenExpirationDate = $this->createExpirationDate(); + $cookieExpiration = 0; + if ($rememberMe === true) { - $expirationDate = $this->createExpirationDate(self::MAX_EXPIRATION_AGE_IN_DAYS); + $authTokenExpirationDate = $this->createExpirationDate(self::MAX_EXPIRATION_AGE_IN_DAYS); + $cookieExpiration = (int)$authTokenExpirationDate->format('U'); } - $token = $this->generateToken(DateTime::createFromString((string)$expirationDate)); + $token = $this->generateToken(DateTime::createFromString((string)$authTokenExpirationDate)); - setcookie(self::AUTHENTICATION_COOKIE_NAME, $token, (int)$expirationDate->format('U')); + setcookie(self::AUTHENTICATION_COOKIE_NAME, $token, $cookieExpiration); } public function logout() : void