1
0
mirror of synced 2024-11-24 22:06:06 +03:00

Update authenticators

This commit is contained in:
Кривич Сергей 2022-07-20 13:58:16 +03:00
parent f591629786
commit 422e86f22e
6 changed files with 70 additions and 231 deletions

View File

@ -22,19 +22,10 @@ abstract class AbstractClientAuthenticator extends AbstractAuthenticator
$this->errorResponseFactory = $errorResponseFactory; $this->errorResponseFactory = $errorResponseFactory;
} }
/**
* {@inheritdoc }
*/
abstract public function supports(Request $request): ?bool; abstract public function supports(Request $request): ?bool;
/**
* {@inheritdoc }
*/
abstract public function authenticate(Request $request): Passport; abstract public function authenticate(Request $request): Passport;
/**
* {@inheritdoc }
*/
public function onAuthenticationFailure(Request $request, AuthenticationException $exception): ?Response public function onAuthenticationFailure(Request $request, AuthenticationException $exception): ?Response
{ {
$error = new Error(); $error = new Error();
@ -43,9 +34,6 @@ abstract class AbstractClientAuthenticator extends AbstractAuthenticator
return $this->errorResponseFactory->create($error,Response::HTTP_FORBIDDEN); return $this->errorResponseFactory->create($error,Response::HTTP_FORBIDDEN);
} }
/**
* {@inheritdoc }
*/
public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $providerKey): ?Response public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $providerKey): ?Response
{ {
return null; return null;

View File

@ -2,37 +2,38 @@
namespace RetailCrm\ServiceBundle\Security; namespace RetailCrm\ServiceBundle\Security;
use Doctrine\Persistence\ObjectRepository;
use RetailCrm\ServiceBundle\Response\ErrorJsonResponseFactory;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
use Symfony\Component\Security\Http\Authenticator\Passport\Passport; use Symfony\Component\Security\Http\Authenticator\Passport\Passport;
use Symfony\Component\Security\Http\Authenticator\Passport\SelfValidatingPassport;
class CallbackClientAuthenticator extends AbstractClientAuthenticator class CallbackClientAuthenticator extends AbstractClientAuthenticator
{ {
/** public function __construct(
* {@inheritdoc } ErrorJsonResponseFactory $errorResponseFactory,
*/ private ObjectRepository $repository,
) {
parent::__construct($errorResponseFactory);
}
public function supports(Request $request): bool public function supports(Request $request): bool
{ {
return $request->request->has(static::AUTH_FIELD) || $request->query->has(static::AUTH_FIELD); return $request->request->has(static::AUTH_FIELD) || $request->query->has(static::AUTH_FIELD);
} }
/**
* {@inheritdoc }
*/
public function supportsRememberMe(): bool
{
return false;
}
/**
* {@inheritdoc }
*/
public function authenticate(Request $request): Passport public function authenticate(Request $request): Passport
{ {
$identifier = $request->request->get(static::AUTH_FIELD); $identifier = $request->request->get(static::AUTH_FIELD);
if (null === $identifier) {
throw new AuthenticationException('Request does not contain authentication data');
}
return new SelfValidatingPassport( return new SelfValidatingPassport(
new UserBadge($identifier, function ($userIdentifier) { new UserBadge($identifier, function ($userIdentifier) {
return $this->repository->findByIdentifier($userIdentifier); return $this->repository->findOneBy([static::AUTH_FIELD => $userIdentifier]);
}), }),
[] []
); );

View File

@ -2,9 +2,10 @@
namespace RetailCrm\ServiceBundle\Security; namespace RetailCrm\ServiceBundle\Security;
use App\Repository\ConnectionRepository; use Doctrine\Persistence\ObjectRepository;
use RetailCrm\ServiceBundle\Response\ErrorJsonResponseFactory; use RetailCrm\ServiceBundle\Response\ErrorJsonResponseFactory;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\Security; use Symfony\Component\Security\Core\Security;
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\RememberMeBadge; use Symfony\Component\Security\Http\Authenticator\Passport\Badge\RememberMeBadge;
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge; use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
@ -13,23 +14,14 @@ use Symfony\Component\Security\Http\Authenticator\Passport\SelfValidatingPasspor
class FrontApiClientAuthenticator extends AbstractClientAuthenticator class FrontApiClientAuthenticator extends AbstractClientAuthenticator
{ {
private $security;
private $repository;
public function __construct( public function __construct(
ErrorJsonResponseFactory $errorResponseFactory, ErrorJsonResponseFactory $errorResponseFactory,
Security $security, private Security $security,
ConnectionRepository $repository private ObjectRepository $repository
) { ) {
parent::__construct($errorResponseFactory); parent::__construct($errorResponseFactory);
$this->security = $security;
$this->repository = $repository;
} }
/**
* {@inheritdoc }
*/
public function supports(Request $request): bool public function supports(Request $request): bool
{ {
if ($this->security->getUser()) { if ($this->security->getUser()) {
@ -39,16 +31,16 @@ class FrontApiClientAuthenticator extends AbstractClientAuthenticator
return $request->request->has(static::AUTH_FIELD); return $request->request->has(static::AUTH_FIELD);
} }
/**
* {@inheritdoc }
*/
public function authenticate(Request $request): Passport public function authenticate(Request $request): Passport
{ {
$identifier = $request->request->get(static::AUTH_FIELD); $identifier = $request->request->get(static::AUTH_FIELD);
if (null === $identifier) {
throw new AuthenticationException('Request does not contain authentication data');
}
return new SelfValidatingPassport( return new SelfValidatingPassport(
new UserBadge($identifier, function ($userIdentifier) { new UserBadge($identifier, function ($userIdentifier) {
return $this->repository->findByIdentifier($userIdentifier); return $this->repository->findOneBy([static::AUTH_FIELD => $userIdentifier]);
}), }),
[new RememberMeBadge()] [new RememberMeBadge()]
); );

View File

@ -4,11 +4,6 @@ namespace RetailCrm\ServiceBundle\Tests\DataFixtures;
use Symfony\Component\Security\Core\User\UserInterface; use Symfony\Component\Security\Core\User\UserInterface;
/**
* Class User
*
* @package RetailCrm\ServiceBundle\Tests\DataFixtures
*/
class User implements UserInterface class User implements UserInterface
{ {
public function getRoles(): array public function getRoles(): array
@ -16,16 +11,6 @@ class User implements UserInterface
return ["USER"]; return ["USER"];
} }
public function getPassword(): string
{
return "123";
}
public function getSalt(): string
{
return "salt";
}
public function getUsername(): string public function getUsername(): string
{ {
return "user"; return "user";
@ -34,4 +19,9 @@ class User implements UserInterface
public function eraseCredentials(): void public function eraseCredentials(): void
{ {
} }
public function getUserIdentifier(): string
{
return 'identifier';
}
} }

View File

@ -2,6 +2,7 @@
namespace RetailCrm\ServiceBundle\Tests\Security; namespace RetailCrm\ServiceBundle\Tests\Security;
use Doctrine\Persistence\ObjectRepository;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
use RetailCrm\ServiceBundle\Response\ErrorJsonResponseFactory; use RetailCrm\ServiceBundle\Response\ErrorJsonResponseFactory;
use RetailCrm\ServiceBundle\Security\CallbackClientAuthenticator; use RetailCrm\ServiceBundle\Security\CallbackClientAuthenticator;
@ -11,83 +12,9 @@ use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException; use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\User\UserProviderInterface;
/**
* Class CallbackClientAuthenticatorTest
*
* @package RetailCrm\ServiceBundle\Tests\Security
*/
class CallbackClientAuthenticatorTest extends TestCase class CallbackClientAuthenticatorTest extends TestCase
{ {
public function testStart(): void
{
$errorResponseFactory = $this->createMock(ErrorJsonResponseFactory::class);
$errorResponseFactory
->expects(static::once())
->method('create')
->willReturn(
new JsonResponse(['message' => 'Authentication required'], Response::HTTP_UNAUTHORIZED)
);
$auth = new CallbackClientAuthenticator($errorResponseFactory);
$result = $auth->start(new Request(), new AuthenticationException());
static::assertInstanceOf(JsonResponse::class, $result);
static::assertEquals(Response::HTTP_UNAUTHORIZED, $result->getStatusCode());
}
public function testGetCredentials(): void
{
$errorResponseFactory = $this->createMock(ErrorJsonResponseFactory::class);
$auth = new CallbackClientAuthenticator($errorResponseFactory);
$result = $auth->getCredentials(new Request([], [CallbackClientAuthenticator::AUTH_FIELD => '123']));
static::assertEquals('123', $result);
$result = $auth->getCredentials(new Request([CallbackClientAuthenticator::AUTH_FIELD => '123']));
static::assertEquals('123', $result);
}
public function testCheckCredentials(): void
{
$errorResponseFactory = $this->createMock(ErrorJsonResponseFactory::class);
$user = new class implements UserInterface {
public function getRoles(): array
{
return ["USER"];
}
public function getPassword(): string
{
return "123";
}
public function getSalt(): string
{
return "salt";
}
public function getUsername(): string
{
return "user";
}
public function eraseCredentials(): void
{
}
};
$auth = new CallbackClientAuthenticator($errorResponseFactory);
$result = $auth->checkCredentials(new Request(), $user);
static::assertTrue($result);
}
public function testOnAuthenticationFailure(): void public function testOnAuthenticationFailure(): void
{ {
$errorResponseFactory = $this->createMock(ErrorJsonResponseFactory::class); $errorResponseFactory = $this->createMock(ErrorJsonResponseFactory::class);
@ -101,7 +28,8 @@ class CallbackClientAuthenticatorTest extends TestCase
) )
); );
$auth = new CallbackClientAuthenticator($errorResponseFactory); $userRepository = $this->createMock(ObjectRepository::class);
$auth = new CallbackClientAuthenticator($errorResponseFactory, $userRepository);
$result = $auth->onAuthenticationFailure(new Request(), new AuthenticationException()); $result = $auth->onAuthenticationFailure(new Request(), new AuthenticationException());
static::assertInstanceOf(JsonResponse::class, $result); static::assertInstanceOf(JsonResponse::class, $result);
@ -112,7 +40,8 @@ class CallbackClientAuthenticatorTest extends TestCase
{ {
$errorResponseFactory = $this->createMock(ErrorJsonResponseFactory::class); $errorResponseFactory = $this->createMock(ErrorJsonResponseFactory::class);
$auth = new CallbackClientAuthenticator($errorResponseFactory); $userRepository = $this->createMock(ObjectRepository::class);
$auth = new CallbackClientAuthenticator($errorResponseFactory, $userRepository);
$result = $auth->supports(new Request([], [CallbackClientAuthenticator::AUTH_FIELD => '123'])); $result = $auth->supports(new Request([], [CallbackClientAuthenticator::AUTH_FIELD => '123']));
static::assertTrue($result); static::assertTrue($result);
@ -126,32 +55,26 @@ class CallbackClientAuthenticatorTest extends TestCase
static::assertFalse($result); static::assertFalse($result);
} }
public function testSupportsRememberMe(): void public function testAuthenticate(): void
{
$errorResponseFactory = $this->createMock(ErrorJsonResponseFactory::class);
$auth = new CallbackClientAuthenticator($errorResponseFactory);
$result = $auth->supportsRememberMe();
static::assertFalse($result);
}
public function testGetUser(): void
{ {
$errorResponseFactory = $this->createMock(ErrorJsonResponseFactory::class); $errorResponseFactory = $this->createMock(ErrorJsonResponseFactory::class);
$user = new User(); $user = new User();
$auth = new CallbackClientAuthenticator($errorResponseFactory);
$userProvider = $this->createMock(UserProviderInterface::class); $userRepository = $this->createMock(ObjectRepository::class);
$userProvider $userRepository
->expects(static::once()) ->expects(static::once())
->method('loadUserByUsername') ->method('findOneBy')
->with('clientId')
->willReturn($user) ->willReturn($user)
; ;
$result = $auth->getUser('clientId', $userProvider); $auth = new CallbackClientAuthenticator($errorResponseFactory, $userRepository);
static::assertEquals($user, $result);
$passport = $auth->authenticate(new Request([], [CallbackClientAuthenticator::AUTH_FIELD => '123']));
$authUser = $passport->getUser();
static::assertEquals($user, $authUser);
$this->expectException(AuthenticationException::class);
$auth->authenticate(new Request());
} }
public function testOnAuthenticationSuccess(): void public function testOnAuthenticationSuccess(): void
@ -159,7 +82,8 @@ class CallbackClientAuthenticatorTest extends TestCase
$errorResponseFactory = $this->createMock(ErrorJsonResponseFactory::class); $errorResponseFactory = $this->createMock(ErrorJsonResponseFactory::class);
$request = $this->createMock(Request::class); $request = $this->createMock(Request::class);
$token = $this->createMock(TokenInterface::class); $token = $this->createMock(TokenInterface::class);
$auth = new CallbackClientAuthenticator($errorResponseFactory); $userRepository = $this->createMock(ObjectRepository::class);
$auth = new CallbackClientAuthenticator($errorResponseFactory, $userRepository);
$result = $auth->onAuthenticationSuccess($request, $token, 'key'); $result = $auth->onAuthenticationSuccess($request, $token, 'key');

View File

@ -2,6 +2,7 @@
namespace RetailCrm\ServiceBundle\Tests\Security; namespace RetailCrm\ServiceBundle\Tests\Security;
use Doctrine\Persistence\ObjectRepository;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
use RetailCrm\ServiceBundle\Response\ErrorJsonResponseFactory; use RetailCrm\ServiceBundle\Response\ErrorJsonResponseFactory;
use RetailCrm\ServiceBundle\Security\FrontApiClientAuthenticator; use RetailCrm\ServiceBundle\Security\FrontApiClientAuthenticator;
@ -12,59 +13,9 @@ use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException; use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\Security; use Symfony\Component\Security\Core\Security;
use Symfony\Component\Security\Core\User\UserProviderInterface;
/**
* Class FrontApiClientAuthenticatorTest
*
* @package RetailCrm\ServiceBundle\Tests\Security
*/
class FrontApiClientAuthenticatorTest extends TestCase class FrontApiClientAuthenticatorTest extends TestCase
{ {
public function testStart(): void
{
$errorResponseFactory = $this->createMock(ErrorJsonResponseFactory::class);
$errorResponseFactory
->expects(static::once())
->method('create')
->willReturn(
new JsonResponse(['message' => 'Authentication required'], Response::HTTP_UNAUTHORIZED)
);
$security = $this->createMock(Security::class);
$auth = new FrontApiClientAuthenticator($errorResponseFactory, $security);
$result = $auth->start(new Request(), new AuthenticationException());
static::assertInstanceOf(JsonResponse::class, $result);
static::assertEquals(Response::HTTP_UNAUTHORIZED, $result->getStatusCode());
}
public function testGetCredentials(): void
{
$errorResponseFactory = $this->createMock(ErrorJsonResponseFactory::class);
$security = $this->createMock(Security::class);
$auth = new FrontApiClientAuthenticator($errorResponseFactory, $security);
$result = $auth->getCredentials(new Request([], [FrontApiClientAuthenticator::AUTH_FIELD => '123']));
static::assertEquals('123', $result);
$result = $auth->getCredentials(new Request([FrontApiClientAuthenticator::AUTH_FIELD => '123']));
static::assertEquals('123', $result);
}
public function testCheckCredentials(): void
{
$errorResponseFactory = $this->createMock(ErrorJsonResponseFactory::class);
$security = $this->createMock(Security::class);
$auth = new FrontApiClientAuthenticator($errorResponseFactory, $security);
$result = $auth->checkCredentials(new Request(), new User());
static::assertTrue($result);
}
public function testOnAuthenticationFailure(): void public function testOnAuthenticationFailure(): void
{ {
$errorResponseFactory = $this->createMock(ErrorJsonResponseFactory::class); $errorResponseFactory = $this->createMock(ErrorJsonResponseFactory::class);
@ -78,9 +29,9 @@ class FrontApiClientAuthenticatorTest extends TestCase
) )
); );
$security = $this->createMock(Security::class); $security = $this->createMock(Security::class);
$userRepository = $this->createMock(ObjectRepository::class);
$auth = new FrontApiClientAuthenticator($errorResponseFactory, $security); $auth = new FrontApiClientAuthenticator($errorResponseFactory, $security, $userRepository);
$result = $auth->start(new Request(), new AuthenticationException()); $result = $auth->onAuthenticationFailure(new Request(), new AuthenticationException());
static::assertInstanceOf(JsonResponse::class, $result); static::assertInstanceOf(JsonResponse::class, $result);
static::assertEquals(Response::HTTP_FORBIDDEN, $result->getStatusCode()); static::assertEquals(Response::HTTP_FORBIDDEN, $result->getStatusCode());
@ -91,8 +42,8 @@ class FrontApiClientAuthenticatorTest extends TestCase
$errorResponseFactory = $this->createMock(ErrorJsonResponseFactory::class); $errorResponseFactory = $this->createMock(ErrorJsonResponseFactory::class);
$security = $this->createMock(Security::class); $security = $this->createMock(Security::class);
$security->method('getUser')->willReturn(new User()); $security->method('getUser')->willReturn(new User());
$userRepository = $this->createMock(ObjectRepository::class);
$auth = new FrontApiClientAuthenticator($errorResponseFactory, $security); $auth = new FrontApiClientAuthenticator($errorResponseFactory, $security, $userRepository);
$result = $auth->supports(new Request()); $result = $auth->supports(new Request());
static::assertFalse($result); static::assertFalse($result);
@ -103,42 +54,34 @@ class FrontApiClientAuthenticatorTest extends TestCase
$errorResponseFactory = $this->createMock(ErrorJsonResponseFactory::class); $errorResponseFactory = $this->createMock(ErrorJsonResponseFactory::class);
$security = $this->createMock(Security::class); $security = $this->createMock(Security::class);
$security->method('getUser')->willReturn(null); $security->method('getUser')->willReturn(null);
$userRepository = $this->createMock(ObjectRepository::class);
$auth = new FrontApiClientAuthenticator($errorResponseFactory, $security); $auth = new FrontApiClientAuthenticator($errorResponseFactory, $security, $userRepository);
$result = $auth->supports(new Request([], [FrontApiClientAuthenticator::AUTH_FIELD => '123'])); $result = $auth->supports(new Request([], [FrontApiClientAuthenticator::AUTH_FIELD => '123']));
static::assertTrue($result); static::assertTrue($result);
} }
public function testSupportsRememberMe(): void public function testAuthenticate(): void
{
$errorResponseFactory = $this->createMock(ErrorJsonResponseFactory::class);
$security = $this->createMock(Security::class);
$auth = new FrontApiClientAuthenticator($errorResponseFactory, $security);
$result = $auth->supportsRememberMe();
static::assertTrue($result);
}
public function testGetUser(): void
{ {
$errorResponseFactory = $this->createMock(ErrorJsonResponseFactory::class); $errorResponseFactory = $this->createMock(ErrorJsonResponseFactory::class);
$security = $this->createMock(Security::class); $security = $this->createMock(Security::class);
$user = new User(); $user = new User();
$auth = new FrontApiClientAuthenticator($errorResponseFactory, $security); $userRepository = $this->createMock(ObjectRepository::class);
$userRepository
$userProvider = $this->createMock(UserProviderInterface::class);
$userProvider
->expects(static::once()) ->expects(static::once())
->method('loadUserByUsername') ->method('findOneBy')
->with('clientId') ->with([FrontApiClientAuthenticator::AUTH_FIELD => '123'])
->willReturn($user) ->willReturn($user)
; ;
$auth = new FrontApiClientAuthenticator($errorResponseFactory, $security, $userRepository);
$result = $auth->getUser('clientId', $userProvider); $passport = $auth->authenticate(new Request([], [FrontApiClientAuthenticator::AUTH_FIELD => '123']));
static::assertEquals($user, $result); $authUser = $passport->getUser();
static::assertEquals($user, $authUser);
$this->expectException(AuthenticationException::class);
$auth->authenticate(new Request());
} }
public function testOnAuthenticationSuccess(): void public function testOnAuthenticationSuccess(): void
@ -147,7 +90,8 @@ class FrontApiClientAuthenticatorTest extends TestCase
$security = $this->createMock(Security::class); $security = $this->createMock(Security::class);
$request = $this->createMock(Request::class); $request = $this->createMock(Request::class);
$token = $this->createMock(TokenInterface::class); $token = $this->createMock(TokenInterface::class);
$auth = new FrontApiClientAuthenticator($errorResponseFactory, $security); $userRepository = $this->createMock(ObjectRepository::class);
$auth = new FrontApiClientAuthenticator($errorResponseFactory, $security, $userRepository);
$result = $auth->onAuthenticationSuccess($request, $token, 'key'); $result = $auth->onAuthenticationSuccess($request, $token, 'key');