From 38bb2530eb3700ac439995fc89fabf7b17750d43 Mon Sep 17 00:00:00 2001 From: Alexey Skobkin Date: Thu, 21 Jul 2022 02:05:04 +0300 Subject: [PATCH] #27 App\Api\V1\Controller\SecurityController upgraded due to changes in ApiTokenAuthenticator. framework.router.utf8 set to true. --- config/packages/framework.yaml | 3 ++ .../V1/Controller/AbstractApiController.php | 2 +- src/Api/V1/Controller/SecurityController.php | 32 +++++++------------ src/Security/ApiTokenAuthenticator.php | 25 ++++++++++----- 4 files changed, 32 insertions(+), 30 deletions(-) diff --git a/config/packages/framework.yaml b/config/packages/framework.yaml index 968d6e0..c9a2e08 100644 --- a/config/packages/framework.yaml +++ b/config/packages/framework.yaml @@ -7,6 +7,9 @@ framework: # https://symfony.com/doc/5.4/deployment/proxies.html#but-what-if-the-ip-of-my-reverse-proxy-changes-constantly trusted_proxies: '%env(TRUSTED_PROXIES)%' + router: + utf8: true + # Enables session support. Note that the session will ONLY be started if you read or write from it. # Remove or comment this section to explicitly disable session support. session: diff --git a/src/Api/V1/Controller/AbstractApiController.php b/src/Api/V1/Controller/AbstractApiController.php index c6a91d2..c6ee90b 100644 --- a/src/Api/V1/Controller/AbstractApiController.php +++ b/src/Api/V1/Controller/AbstractApiController.php @@ -11,7 +11,7 @@ abstract class AbstractApiController extends AbstractController { protected const DEFAULT_SERIALIZER_GROUPS = ['api']; - protected function createJsonResponse($data, array $groups = [], int $code = Response::HTTP_OK, string $message = null, string $status = ''): JsonResponse + protected function createJsonResponse($data = null, array $groups = [], int $code = Response::HTTP_OK, string $message = null, string $status = ''): JsonResponse { return $this->json(new ApiResponse($data, $code, $message, $status), $code, [], [ 'groups' => array_merge(self::DEFAULT_SERIALIZER_GROUPS,$groups), diff --git a/src/Api/V1/Controller/SecurityController.php b/src/Api/V1/Controller/SecurityController.php index 50f55b7..6a61b88 100644 --- a/src/Api/V1/Controller/SecurityController.php +++ b/src/Api/V1/Controller/SecurityController.php @@ -5,11 +5,10 @@ namespace App\Api\V1\Controller; use App\Entity\{ApiToken, User}; use App\Repository\{ApiTokenRepository, UserRepository}; -use App\Security\Token\AuthenticatedApiToken; +use App\Security\ApiTokenAuthenticator; use Doctrine\ORM\EntityManagerInterface; use Symfony\Component\HttpFoundation\{JsonResponse, Request}; -use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; -use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface; +use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface; class SecurityController extends AbstractApiController { @@ -18,18 +17,18 @@ class SecurityController extends AbstractApiController EntityManagerInterface $em, UserRepository $userRepo, ApiTokenRepository $tokenRepo, - UserPasswordEncoderInterface $passwordEncoder + UserPasswordHasherInterface $passwordHasher, ): JsonResponse { $username = $request->request->get('username'); $password = $request->request->get('password'); /** @var User $user */ if (null === $user = $userRepo->findOneBy(['username' => $username])) { - return $this->createJsonResponse(null, [], JsonResponse::HTTP_UNAUTHORIZED, 'User not found'); + return $this->createJsonResponse(code: JsonResponse::HTTP_UNAUTHORIZED, message: 'User not found'); } - if (!$passwordEncoder->isPasswordValid($user, $password)) { - return $this->createJsonResponse(null, [], JsonResponse::HTTP_UNAUTHORIZED, 'Invalid password'); + if (!$passwordHasher->isPasswordValid($user, $password)) { + return $this->createJsonResponse(code: JsonResponse::HTTP_UNAUTHORIZED, message: 'Invalid password'); } $apiToken = new ApiToken($user); @@ -38,28 +37,19 @@ class SecurityController extends AbstractApiController try { $em->flush(); } catch (\Exception $ex) { - return $this->createJsonResponse(null, [], JsonResponse::HTTP_INTERNAL_SERVER_ERROR, 'Token persisting error'); + return $this->createJsonResponse(code: JsonResponse::HTTP_INTERNAL_SERVER_ERROR, message: 'Token persisting error'); } return $this->createJsonResponse($apiToken->getKey()); } - public function logout(TokenStorageInterface $tokenStorage, ApiTokenRepository $apiTokenRepo, EntityManagerInterface $em): JsonResponse - { - if (null === $token = $tokenStorage->getToken()) { - return $this->createJsonResponse(null,[],JsonResponse::HTTP_INTERNAL_SERVER_ERROR, 'Can\'t retrieve user token.'); - } - - if (!$token instanceof AuthenticatedApiToken) { - return $this->createJsonResponse(null, [], JsonResponse::HTTP_INTERNAL_SERVER_ERROR, 'Invalid session token type retrieved.'); - } - - if (null === $tokenKey = $token->getTokenKey()) { - return $this->createJsonResponse(null,[],JsonResponse::HTTP_INTERNAL_SERVER_ERROR, 'Can\'t retrieve token key from the session.'); + public function logout(Request $request, ApiTokenRepository $apiTokenRepo, EntityManagerInterface $em): JsonResponse { + if (null === $tokenKey = ApiTokenAuthenticator::getTokenKeyFromRequest($request)) { + return $this->createJsonResponse(null, code:JsonResponse::HTTP_INTERNAL_SERVER_ERROR, message: 'No API token provided.'); } if (null === $apiToken = $apiTokenRepo->findOneBy(['key' => $tokenKey])) { - return $this->createJsonResponse(null,[],JsonResponse::HTTP_INTERNAL_SERVER_ERROR, 'API token with such key not found in the database.'); + return $this->createJsonResponse(null, code:JsonResponse::HTTP_INTERNAL_SERVER_ERROR, message: 'API token with such key not found in the database.'); } $em->remove($apiToken); diff --git a/src/Security/ApiTokenAuthenticator.php b/src/Security/ApiTokenAuthenticator.php index 1454693..9c9a3f5 100644 --- a/src/Security/ApiTokenAuthenticator.php +++ b/src/Security/ApiTokenAuthenticator.php @@ -32,18 +32,12 @@ class ApiTokenAuthenticator extends AbstractAuthenticator */ public function supports(Request $request): bool { - // Let's also support cookies and query params for some cases like torrent clients. - return $request->headers->has(self::TOKEN_HEADER) || - $request->cookies->has(self::TOKEN_HEADER) || - $request->query->has(self::TOKEN_HEADER); + return static::requestHasToken($request); } public function authenticate(Request $request): Passport { - $tokenKey = $request?->headers?->get(self::TOKEN_HEADER) ?: - $request?->cookies?->get(self::TOKEN_HEADER) ?: - $request?->query?->get(self::TOKEN_HEADER) - ; + $tokenKey = static::getTokenKeyFromRequest($request); if (null === $tokenKey) { throw new CustomUserMessageAuthenticationException('No API token provided'); @@ -73,4 +67,19 @@ class ApiTokenAuthenticator extends AbstractAuthenticator return new JsonResponse($json, JsonResponse::HTTP_UNAUTHORIZED, json: true); } + + public static function getTokenKeyFromRequest(Request $request): ?string + { + $request?->headers?->get(self::TOKEN_HEADER) ?: + $request?->cookies?->get(self::TOKEN_HEADER) ?: + $request?->query?->get(self::TOKEN_HEADER); + } + + public static function requestHasToken(Request $request): bool + { + // Let's also support cookies and query params for some cases like torrent clients. + return $request->headers->has(self::TOKEN_HEADER) || + $request->cookies->has(self::TOKEN_HEADER) || + $request->query->has(self::TOKEN_HEADER); + } }