From 180ccab466708865727a407c0fa529fa5a7bb488 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9A=D1=80=D0=B8=D0=B2=D0=B8=D1=87=20=D0=A1=D0=B5=D1=80?= =?UTF-8?q?=D0=B3=D0=B5=D0=B9?= Date: Thu, 21 Jul 2022 13:02:32 +0300 Subject: [PATCH] Fix authenticators and dependecy injection --- .../RetailCrmServiceExtension.php | 4 +- Resources/doc/Security.md | 39 ++++++++----------- Security/AbstractClientAuthenticator.php | 7 ---- Security/CallbackClientAuthenticator.php | 3 +- Security/FrontApiClientAuthenticator.php | 3 +- Tests/DataFixtures/User.php | 4 +- .../CallbackClientAuthenticatorTest.php | 17 +++----- .../FrontApiClientAuthenticatorTest.php | 16 +++----- 8 files changed, 36 insertions(+), 57 deletions(-) diff --git a/DependencyInjection/RetailCrmServiceExtension.php b/DependencyInjection/RetailCrmServiceExtension.php index 10d7805..cb0c29f 100644 --- a/DependencyInjection/RetailCrmServiceExtension.php +++ b/DependencyInjection/RetailCrmServiceExtension.php @@ -66,8 +66,8 @@ class RetailCrmServiceExtension extends Extension $container ->register(CallbackValueResolver::class) ->setArguments([ - new Reference($container->getParameter('retail_crm_service.request_schema.callback.serializer')), new Reference('validator'), + new Reference($container->getParameter('retail_crm_service.request_schema.callback.serializer')), $container->getParameter('retail_crm_service.request_schema.callback.supports') ]) ->addTag('controller.argument_value_resolver', ['priority' => 50]) @@ -76,8 +76,8 @@ class RetailCrmServiceExtension extends Extension $container ->register(ClientValueResolver::class) ->setArguments([ - new Reference($container->getParameter('retail_crm_service.request_schema.client.serializer')), new Reference('validator'), + new Reference($container->getParameter('retail_crm_service.request_schema.client.serializer')), $container->getParameter('retail_crm_service.request_schema.client.supports') ]) ->addTag('controller.argument_value_resolver', ['priority' => 50]) diff --git a/Resources/doc/Security.md b/Resources/doc/Security.md index d42c32f..dbb9932 100644 --- a/Resources/doc/Security.md +++ b/Resources/doc/Security.md @@ -23,12 +23,13 @@ security: custom_authenticators: - RetailCrm\ServiceBundle\Security\CallbackClientAuthenticator front: - pattern: ^/(front|login) + pattern: ^/auth provider: connection stateless: false remember_me: secret: '%kernel.secret%' lifetime: 604800 # 1 week in seconds + signature_properties: ['clientId'] always_remember_me: true custom_authenticators: - RetailCrm\ServiceBundle\Security\FrontApiClientAuthenticator @@ -41,33 +42,27 @@ security: - { path: ^/simple-connection, roles: PUBLIC_ACCESS } ``` -To authenticate the user after creating it, you can use the following code +Login controller will be called after the authenticator successfully authenticates the user. You can get the authenticated user, generate a token (or whatever you need to return) and return response: ```php - use App\Entity\Connection; - use App\Services\ConnectionManager; - use Symfony\Component\HttpFoundation\Request; - use Symfony\Component\Security\Http\Authentication\UserAuthenticatorInterface; - use RetailCrm\ServiceBundle\Security\FrontApiClientAuthenticator; + use App\Entity\User; + use Symfony\Component\Security\Http\Attribute\CurrentUser; - class AppController extends AbstractController + class ApiLoginController extends AbstractController { - public function someAction( - Request $request, - Connection $connection, - ConnectionManager $manager, - UserAuthenticatorInterface $userAuthenticator, - FrontApiClientAuthenticator $authenticator - ): Response { - $exist = $manager->search($connection); //get connection - - $userAuthenticator->authenticateUser( - $connection, - $authenticator, - $request - ); + #[Route('/auth', name: 'auth')] + public function auth(#[CurrentUser] ?User $user): Response + { + $token = ...; // somehow create an API token for $user + + return $this->json([ + 'user' => $user->getUserIdentifier(), + 'token' => $token, + ]); } } ``` + +The #[CurrentUser] can only be used in controller arguments to retrieve the authenticated user. In services, you would use getUser(). diff --git a/Security/AbstractClientAuthenticator.php b/Security/AbstractClientAuthenticator.php index f7a809b..55c3960 100644 --- a/Security/AbstractClientAuthenticator.php +++ b/Security/AbstractClientAuthenticator.php @@ -16,8 +16,6 @@ abstract class AbstractClientAuthenticator extends AbstractAuthenticator { public const AUTH_FIELD = 'clientId'; - protected ObjectRepository $userRepository; - public function __construct(private ErrorJsonResponseFactory $errorResponseFactory) { } @@ -38,9 +36,4 @@ abstract class AbstractClientAuthenticator extends AbstractAuthenticator { return null; } - - public function setUserRepository(ObjectRepository $userRepository): void - { - $this->userRepository = $userRepository; - } } diff --git a/Security/CallbackClientAuthenticator.php b/Security/CallbackClientAuthenticator.php index eb3711f..452313c 100644 --- a/Security/CallbackClientAuthenticator.php +++ b/Security/CallbackClientAuthenticator.php @@ -24,8 +24,7 @@ class CallbackClientAuthenticator extends AbstractClientAuthenticator return new SelfValidatingPassport( new UserBadge( - $identifier, - fn ($userIdentifier) => $this->userRepository->findOneBy([static::AUTH_FIELD => $userIdentifier]) + $identifier ), [] ); diff --git a/Security/FrontApiClientAuthenticator.php b/Security/FrontApiClientAuthenticator.php index b92f3f9..d827f2c 100644 --- a/Security/FrontApiClientAuthenticator.php +++ b/Security/FrontApiClientAuthenticator.php @@ -38,8 +38,7 @@ class FrontApiClientAuthenticator extends AbstractClientAuthenticator return new SelfValidatingPassport( new UserBadge( - $identifier, - fn ($userIdentifier) => $this->userRepository->findOneBy([static::AUTH_FIELD => $userIdentifier]), + $identifier ), [new RememberMeBadge()] ); diff --git a/Tests/DataFixtures/User.php b/Tests/DataFixtures/User.php index ad17192..47bd5fd 100644 --- a/Tests/DataFixtures/User.php +++ b/Tests/DataFixtures/User.php @@ -6,6 +6,8 @@ use Symfony\Component\Security\Core\User\UserInterface; class User implements UserInterface { + protected string $clientId = '123'; + public function getRoles(): array { return ["USER"]; @@ -22,6 +24,6 @@ class User implements UserInterface public function getUserIdentifier(): string { - return 'identifier'; + return $this->clientId; } } diff --git a/Tests/Security/CallbackClientAuthenticatorTest.php b/Tests/Security/CallbackClientAuthenticatorTest.php index 9a642f5..eff026b 100644 --- a/Tests/Security/CallbackClientAuthenticatorTest.php +++ b/Tests/Security/CallbackClientAuthenticatorTest.php @@ -12,6 +12,7 @@ use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\Exception\AuthenticationException; +use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge; class CallbackClientAuthenticatorTest extends TestCase { @@ -58,19 +59,13 @@ class CallbackClientAuthenticatorTest extends TestCase $errorResponseFactory = $this->createMock(ErrorJsonResponseFactory::class); $user = new User(); - $userRepository = $this->createMock(ObjectRepository::class); - $userRepository - ->expects(static::once()) - ->method('findOneBy') - ->willReturn($user) - ; - $auth = new CallbackClientAuthenticator($errorResponseFactory); - $auth->setUserRepository($userRepository); - $passport = $auth->authenticate(new Request([], [CallbackClientAuthenticator::AUTH_FIELD => '123'])); - $authUser = $passport->getUser(); - static::assertEquals($user, $authUser); + static::assertTrue($passport->hasBadge(UserBadge::class)); + static::assertEquals( + $user->getUserIdentifier(), + $passport->getBadge(UserBadge::class)->getUserIdentifier() + ); $this->expectException(AuthenticationException::class); $auth->authenticate(new Request()); diff --git a/Tests/Security/FrontApiClientAuthenticatorTest.php b/Tests/Security/FrontApiClientAuthenticatorTest.php index 58c420e..bfec186 100644 --- a/Tests/Security/FrontApiClientAuthenticatorTest.php +++ b/Tests/Security/FrontApiClientAuthenticatorTest.php @@ -13,6 +13,7 @@ use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\Exception\AuthenticationException; use Symfony\Component\Security\Core\Security; +use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge; class FrontApiClientAuthenticatorTest extends TestCase { @@ -65,19 +66,14 @@ class FrontApiClientAuthenticatorTest extends TestCase $security = $this->createMock(Security::class); $user = new User(); - $userRepository = $this->createMock(ObjectRepository::class); - $userRepository - ->expects(static::once()) - ->method('findOneBy') - ->with([FrontApiClientAuthenticator::AUTH_FIELD => '123']) - ->willReturn($user) - ; $auth = new FrontApiClientAuthenticator($errorResponseFactory, $security); - $auth->setUserRepository($userRepository); $passport = $auth->authenticate(new Request([], [FrontApiClientAuthenticator::AUTH_FIELD => '123'])); - $authUser = $passport->getUser(); - static::assertEquals($user, $authUser); + static::assertTrue($passport->hasBadge(UserBadge::class)); + static::assertEquals( + $user->getUserIdentifier(), + $passport->getBadge(UserBadge::class)->getUserIdentifier() + ); $this->expectException(AuthenticationException::class); $auth->authenticate(new Request());