-
-
Notifications
You must be signed in to change notification settings - Fork 188
/
PersistedUsernamePasswordProvider.php
126 lines (109 loc) · 4.45 KB
/
PersistedUsernamePasswordProvider.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
<?php
namespace Neos\Flow\Security\Authentication\Provider;
/*
* This file is part of the Neos.Flow package.
*
* (c) Contributors of the Neos Project - www.neos.io
*
* This package is Open Source Software. For the full copyright and license
* information, please view the LICENSE file which was distributed with this
* source code.
*/
use Neos\Flow\Annotations as Flow;
use Neos\Flow\Security\Account;
use Neos\Flow\Security\AccountRepository;
use Neos\Flow\Security\Authentication\Token\UsernamePasswordTokenInterface;
use Neos\Flow\Security\Authentication\TokenInterface;
use Neos\Flow\Security\Context;
use Neos\Flow\Security\Cryptography\PrecomposedHashProvider;
use Neos\Flow\Security\Cryptography\HashService;
use Neos\Flow\Security\Exception\UnsupportedAuthenticationTokenException;
/**
* An authentication provider that authenticates
* Neos\Flow\Security\Authentication\Token\UsernamePassword tokens.
* The accounts are stored in the Content Repository.
*/
class PersistedUsernamePasswordProvider extends AbstractProvider
{
/**
* @var AccountRepository
* @Flow\Inject
*/
protected $accountRepository;
/**
* @var HashService
* @Flow\Inject
*/
protected $hashService;
/**
* @var Context
* @Flow\Inject
*/
protected $securityContext;
/**
* @var \Neos\Flow\Persistence\PersistenceManagerInterface
* @Flow\Inject
*/
protected $persistenceManager;
/**
* The PrecomposedHashProvider has to be injected non-lazy to prevent timing differences
*
* @var PrecomposedHashProvider
* @Flow\Inject(lazy=false)
*/
protected $precomposedHashProvider;
/**
* Returns the class names of the tokens this provider can authenticate.
*
* @return array
*/
public function getTokenClassNames()
{
return [UsernamePasswordTokenInterface::class];
}
/**
* Checks the given token for validity and sets the token authentication status
* accordingly (success, wrong credentials or no credentials given).
*
* @param TokenInterface $authenticationToken The token to be authenticated
* @return void
* @throws UnsupportedAuthenticationTokenException
* @throws \Neos\Flow\Persistence\Exception\IllegalObjectTypeException
* @throws \Neos\Flow\Security\Exception\InvalidAuthenticationStatusException
*/
public function authenticate(TokenInterface $authenticationToken)
{
if (!($authenticationToken instanceof UsernamePasswordTokenInterface)) {
throw new UnsupportedAuthenticationTokenException(sprintf('This provider cannot authenticate the given token. The token must implement %s', UsernamePasswordTokenInterface::class), 1217339840);
}
/** @var $account Account */
$account = null;
if ($authenticationToken->getAuthenticationStatus() !== TokenInterface::AUTHENTICATION_SUCCESSFUL) {
$authenticationToken->setAuthenticationStatus(TokenInterface::NO_CREDENTIALS_GIVEN);
}
$username = $authenticationToken->getUsername();
$password = $authenticationToken->getPassword();
if ($username === '' || $password === '') {
return;
}
$providerName = $this->options['lookupProviderName'] ?? $this->name;
$this->securityContext->withoutAuthorizationChecks(function () use ($username, &$account, $providerName) {
$account = $this->accountRepository->findActiveByAccountIdentifierAndAuthenticationProviderName($username, $providerName);
});
$authenticationToken->setAuthenticationStatus(TokenInterface::WRONG_CREDENTIALS);
if ($account === null) {
// validate anyways (with a precomposed hash) in order to prevent timing attacks on this provider
$this->hashService->validatePassword($password, $this->precomposedHashProvider->getPrecomposedHash());
return;
}
if ($this->hashService->validatePassword($password, $account->getCredentialsSource())) {
$account->authenticationAttempted(TokenInterface::AUTHENTICATION_SUCCESSFUL);
$authenticationToken->setAuthenticationStatus(TokenInterface::AUTHENTICATION_SUCCESSFUL);
$authenticationToken->setAccount($account);
} else {
$account->authenticationAttempted(TokenInterface::WRONG_CREDENTIALS);
}
$this->accountRepository->update($account);
$this->persistenceManager->whitelistObject($account);
}
}