Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 80b205e

Browse files
committed
Created HttpBasicAuthenticator and some Guard traits
1 parent 14ca7f9 commit 80b205e

File tree

5 files changed

+283
-1
lines changed

5 files changed

+283
-1
lines changed
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Security\Core\Authentication\Authenticator;
13+
14+
use Psr\Log\LoggerInterface;
15+
use Symfony\Component\HttpFoundation\Request;
16+
use Symfony\Component\HttpFoundation\Response;
17+
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
18+
use Symfony\Component\Security\Core\Encoder\EncoderFactoryInterface;
19+
use Symfony\Component\Security\Core\Exception\AuthenticationException;
20+
use Symfony\Component\Security\Core\User\UserInterface;
21+
use Symfony\Component\Security\Core\User\UserProviderInterface;
22+
use Symfony\Component\Security\Guard\AuthenticatorInterface;
23+
24+
/**
25+
* @author Wouter de Jong <[email protected]>
26+
*/
27+
class HttpBasicAuthenticator implements AuthenticatorInterface
28+
{
29+
use UserProviderTrait, UsernamePasswordTrait {
30+
UserProviderTrait::getUser as getUserTrait;
31+
}
32+
33+
protected $realmName;
34+
private $userProvider;
35+
private $encoderFactory;
36+
private $logger;
37+
38+
public function __construct(string $realmName, UserProviderInterface $userProvider, EncoderFactoryInterface $encoderFactory, ?LoggerInterface $logger = null)
39+
{
40+
$this->realmName = $realmName;
41+
$this->userProvider = $userProvider;
42+
$this->encoderFactory = $encoderFactory;
43+
$this->logger = $logger;
44+
}
45+
46+
public function start(Request $request, AuthenticationException $authException = null)
47+
{
48+
$response = new Response();
49+
$response->headers->set('WWW-Authenticate', sprintf('Basic realm="%s"', $this->realmName));
50+
$response->setStatusCode(401);
51+
52+
return $response;
53+
}
54+
55+
public function supports(Request $request): bool
56+
{
57+
return $request->headers->has('PHP_AUTH_USER');
58+
}
59+
60+
public function getUser($credentials, UserProviderInterface $userProvider): ?UserInterface
61+
{
62+
return $this->getUserTrait($credentials, $this->userProvider);
63+
}
64+
65+
public function getCredentials(Request $request)
66+
{
67+
return [
68+
'username' => $request->headers->get('PHP_AUTH_USER'),
69+
'password' => $request->headers->get('PHP_AUTH_PW', ''),
70+
];
71+
}
72+
73+
public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey): ?Response
74+
{
75+
return null;
76+
}
77+
78+
public function onAuthenticationFailure(Request $request, AuthenticationException $exception): ?Response
79+
{
80+
if (null !== $this->logger) {
81+
$this->logger->info('Basic authentication failed for user.', ['username' => $request->headers->get('PHP_AUTH_USER'), 'exception' => $exception]);
82+
}
83+
84+
return $this->start($request, $exception);
85+
}
86+
87+
/** @todo not sure if true or false */
88+
public function supportsRememberMe(): bool
89+
{
90+
return true;
91+
}
92+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Security\Core\Authentication\Authenticator;
13+
14+
use Symfony\Component\Security\Core\User\UserInterface;
15+
use Symfony\Component\Security\Core\User\UserProviderInterface;
16+
17+
/**
18+
* @author Wouter de Jong <[email protected]>
19+
*/
20+
trait UserProviderTrait
21+
{
22+
public function getUser($credentials, UserProviderInterface $userProvider): ?UserInterface
23+
{
24+
return $userProvider->loadUserByUsername($credentials['username']);
25+
}
26+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Security\Core\Authentication\Authenticator;
13+
14+
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
15+
use Symfony\Component\Security\Core\Encoder\EncoderFactoryInterface;
16+
use Symfony\Component\Security\Core\Exception\BadCredentialsException;
17+
use Symfony\Component\Security\Core\User\UserInterface;
18+
use Symfony\Component\Security\Guard\Token\GuardTokenInterface;
19+
20+
/**
21+
* @author Wouter de Jong <[email protected]>
22+
*
23+
* @property EncoderFactoryInterface $encoderFactory
24+
*/
25+
trait UsernamePasswordTrait
26+
{
27+
public function checkCredentials($credentials, UserInterface $user): bool
28+
{
29+
if (!$this->encoderFactory instanceof EncoderFactoryInterface) {
30+
throw new \LogicException(\get_class($this).' uses the '.__CLASS__.' trait, which requires an $encoderFactory property to be initialized with an '.EncoderFactoryInterface::class.' implementation.');
31+
}
32+
33+
if ('' === $credentials['password']) {
34+
throw new BadCredentialsException('The presented password cannot be empty.');
35+
}
36+
37+
if (!$this->encoderFactory->getEncoder($user)->isPasswordValid($user->getPassword(), $credentials['password'], null)) {
38+
throw new BadCredentialsException('The presented password is invalid.');
39+
}
40+
41+
return true;
42+
}
43+
44+
public function createAuthenticatedToken(UserInterface $user, $providerKey): GuardTokenInterface
45+
{
46+
return new UsernamePasswordToken($user, null, $providerKey, $user->getRoles());
47+
}
48+
}

src/Symfony/Component/Security/Core/Authentication/Token/UsernamePasswordToken.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,14 @@
1111

1212
namespace Symfony\Component\Security\Core\Authentication\Token;
1313

14+
use Symfony\Component\Security\Guard\Token\GuardTokenInterface;
15+
1416
/**
1517
* UsernamePasswordToken implements a username and password token.
1618
*
1719
* @author Fabien Potencier <[email protected]>
1820
*/
19-
class UsernamePasswordToken extends AbstractToken
21+
class UsernamePasswordToken extends AbstractToken implements GuardTokenInterface
2022
{
2123
private $credentials;
2224
private $providerKey;
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
<?php
2+
3+
namespace Symfony\Component\Security\Core\Tests\Authentication\Authenticator;
4+
5+
use PHPUnit\Framework\MockObject\MockObject;
6+
use PHPUnit\Framework\TestCase;
7+
use Symfony\Component\HttpFoundation\Request;
8+
use Symfony\Component\Security\Core\Authentication\Authenticator\HttpBasicAuthenticator;
9+
use Symfony\Component\Security\Core\Encoder\EncoderFactoryInterface;
10+
use Symfony\Component\Security\Core\Encoder\PasswordEncoderInterface;
11+
use Symfony\Component\Security\Core\Exception\BadCredentialsException;
12+
use Symfony\Component\Security\Core\User\UserInterface;
13+
use Symfony\Component\Security\Core\User\UserProviderInterface;
14+
15+
class HttpBasicAuthenticatorTest extends TestCase
16+
{
17+
/** @var UserProviderInterface|MockObject */
18+
private $userProvider;
19+
/** @var EncoderFactoryInterface|MockObject */
20+
private $encoderFactory;
21+
/** @var PasswordEncoderInterface|MockObject */
22+
private $encoder;
23+
24+
protected function setUp(): void
25+
{
26+
$this->userProvider = $this->getMockBuilder(UserProviderInterface::class)->getMock();
27+
$this->encoderFactory = $this->getMockBuilder(EncoderFactoryInterface::class)->getMock();
28+
$this->encoder = $this->getMockBuilder(PasswordEncoderInterface::class)->getMock();
29+
$this->encoderFactory
30+
->expects($this->any())
31+
->method('getEncoder')
32+
->willReturn($this->encoder);
33+
}
34+
35+
public function testValidUsernameAndPasswordServerParameters()
36+
{
37+
$request = new Request([], [], [], [], [], [
38+
'PHP_AUTH_USER' => 'TheUsername',
39+
'PHP_AUTH_PW' => 'ThePassword',
40+
]);
41+
42+
$guard = new HttpBasicAuthenticator('test', $this->userProvider, $this->encoderFactory);
43+
$credentials = $guard->getCredentials($request);
44+
$this->assertEquals([
45+
'username' => 'TheUsername',
46+
'password' => 'ThePassword',
47+
], $credentials);
48+
49+
$mockedUser = $this->getMockBuilder(UserInterface::class)->getMock();
50+
$mockedUser->expects($this->any())->method('getPassword')->willReturn('ThePassword');
51+
52+
$this->userProvider
53+
->expects($this->any())
54+
->method('loadUserByUsername')
55+
->with('TheUsername')
56+
->willReturn($mockedUser);
57+
58+
$user = $guard->getUser($credentials, $this->userProvider);
59+
$this->assertSame($mockedUser, $user);
60+
61+
$this->encoder
62+
->expects($this->any())
63+
->method('isPasswordValid')
64+
->with('ThePassword', 'ThePassword', null)
65+
->willReturn(true);
66+
67+
$checkCredentials = $guard->checkCredentials($credentials, $user);
68+
$this->assertTrue($checkCredentials);
69+
}
70+
71+
/** @dataProvider provideInvalidPasswords */
72+
public function testInvalidPassword($presentedPassword, $exceptionMessage)
73+
{
74+
$guard = new HttpBasicAuthenticator('test', $this->userProvider, $this->encoderFactory);
75+
76+
$this->encoder
77+
->expects($this->any())
78+
->method('isPasswordValid')
79+
->willReturn(false);
80+
81+
$this->expectException(BadCredentialsException::class);
82+
$this->expectExceptionMessage($exceptionMessage);
83+
84+
$guard->checkCredentials([
85+
'username' => 'TheUsername',
86+
'password' => $presentedPassword,
87+
], $this->getMockBuilder(UserInterface::class)->getMock());
88+
}
89+
90+
public function provideInvalidPasswords()
91+
{
92+
return [
93+
['InvalidPassword', 'The presented password is invalid.'],
94+
['', 'The presented password cannot be empty.'],
95+
];
96+
}
97+
98+
/** @dataProvider provideMissingHttpBasicServerParameters */
99+
public function testHttpBasicServerParametersMissing(array $serverParameters)
100+
{
101+
$request = new Request([], [], [], [], [], $serverParameters);
102+
103+
$guard = new HttpBasicAuthenticator('test', $this->userProvider, $this->encoderFactory);
104+
$this->assertFalse($guard->supports($request));
105+
}
106+
107+
public function provideMissingHttpBasicServerParameters()
108+
{
109+
return [
110+
[[]],
111+
[['PHP_AUTH_PW' => 'ThePassword']],
112+
];
113+
}
114+
}

0 commit comments

Comments
 (0)