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 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155
|
use MyBundle\Entity\MyUser;
use MyBundle\Services\MyApi;
use Symfony\Bridge\Monolog\Logger;
use Symfony\Component\Security\Core\Authentication\Provider\UserAuthenticationProvider;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Core\User\UserCheckerInterface;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Exception\BadCredentialsException;
use Symfony\Component\Security\Core\Exception\AuthenticationServiceException;
use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
class MyUserAuthenticationProvider extends UserAuthenticationProvider
{
private $providerKey;
private $userProvider;
/** @var MyApi */
private $api;
/** @var Logger */
private $logger;
public function __construct(MyApi $api, UserProviderInterface $userProvider, UserCheckerInterface $userChecker, $providerKey, $hideUserNotFoundExceptions = true, $logger)
{
parent::__construct($userChecker, $providerKey, $hideUserNotFoundExceptions);
$this->providerKey = $providerKey;
$this->userProvider = $userProvider;
$this->api = $api;
$this->logger = $logger;
}
/*
* Cette méthode réalise l'authentification, mais en réalité celle-ci a déjà été faite dans la méthode retrieveUser.
* En effet, la classe mère UserAuthenticationProvider contient une méthode authenticate() qui fait appel d'abord à retrieveUser()
* et ensuite à checkAuthentication().
*
* retrieveUser() doit renvoyer un objet UserInterface.
* Pour cela, le service web doit être contacté, et ce service web a besoin du login et du mot de passe en paramètres d'entrée.
*
* Le service vérifie la correspondance login et mot de passe, et si c'est OK il renvoie les données du client
* Ainsi, la méthode retrieveUser() réalise déjà une sorte d'authentification (dans le sens où login et mot de passe sont vérifiés à ce moment),
* mais ce n'est pas dans cette méthode que le système de sécurité de Symfony va être informé du succès ou de l'échec de l'authentification.
*
* Ce n'est qu'à l'exécution de checkAuthentication() que l'authentification proprement dite est faite.
* Cependant, dans le cas où retrieveUser() a retourné un objet UserInterface, l'authentification dans checkAuthentication() sera forcément un succès.
*
* Et si retrieveUser() n'a pas retourné d'objet UserInterface, c'est qu'il n'existe pas de client avec login et le mot de passe fournis par la personne qui s'est connectée.
*
* Dans ce cas retrieveUser() aura renvoyé une exception qui sera gérée par la méthode authenticate() de la classe mère UserAuthencationProvider et checkAuthentication() ne sera même pas exécutée.
*
* checkAuthentication() est donc en quelque sorte inutile : soit retrieveUser() renvoie un objet UserInterface, et dans ce cas checkAuthentication()
* permettra forcément l'authentification, soit retrieveUser() lance une exception, et dans ce cas checkAuthentication() n'est pas exécutée.
* Cependant, pour le respect du système d'authentification tel qu'il a été pensé par Symfony, cette méthode est tout de même implémentée.
*/
/**
* Does additional checks on the user and token (like validating the credentials).
*
* @param UserInterface $user
* @param UsernamePasswordToken $token
*
* @throws AuthenticationException if the credentials could not be validated
*/
protected function checkAuthentication(UserInterface $user, UsernamePasswordToken $token)
{
$this->logger->debug(__METHOD__, ['$user'=>$user, '$token' => $token]);
$currentUser = $token->getUser();
if ($currentUser instanceof UserInterface) {
if ($currentUser->getPassword() !== $user->getPassword())
{
throw new BadCredentialsException('Les identifiants ont été modifiés à partir d\'une autre session.');
}
} else {
if (!$presentedPassword = $token->getCredentials()) {
throw new BadCredentialsException('Le mot de passe ne peut pas être vide.');
}
$login = $token->getUsername();
$password = $presentedPassword;
try {
$this->api->OAuthPassword($login, $password);
} catch (\Exception $e) {
throw new BadCredentialsException("Identifiants invalides ($login, $password). ". $e->getMessage(), 0, $e);
}
}
}
/**
* Retrieves the user from an implementation-specific location.
*
* @param string $username The username to retrieve
* @param UsernamePasswordToken $token The Token
*
* @return MyUser|UserInterface The user
*
* @throws UsernameNotFoundException if a User cannot be found by its username (+password)
* @throws AuthenticationServiceException thrown when an authentication request could not be processed due to a system problem.
* @throws AuthenticationException if the credentials could not be validated
*/
protected function retrieveUser($username, UsernamePasswordToken $token)
{
$this->logger->debug(__METHOD__, ['$username'=>$username, '$token' => $token]);
if ($username == 'NONE_PROVIDED') {
throw new BadCredentialsException('Bad credentials : $username == NONE_PROVIDED');
}
// I'm doubtful about the usefulness of this part...
$user = $token->getUser();
if ($user instanceof UserInterface) {
$this->logger->debug('$user est déjà une instance de UserInterface. On le retourne.', ['$user'=>$user]);
return $user;
}
$this->logger->debug('$user n\'est pas une instance de UserInterface', ['$user'=>$user]);
// End doubtful
$login = $username;
$password = $token->getCredentials();
try {
$data1 = $this->api->OAuthPassword($login, $password);
$access_token = $data1['access_token'];
} catch (\Exception $e) {
// UsernameNotFoundException is thrown if a User cannot be found by its username. extends AuthenticationException
$n = new UsernameNotFoundException('Utilisateur non trouvé par le WS. '. $e->getMessage(), 0, $e);
$n->setUsername($login);
throw $n;
}
try {
$data2 = $this->api->OAuthCheckToken($access_token);
$data3 = $this->api->OAuthPermissions($access_token);
$data = array_merge($data1, $data2 , $data3);
$user = new MyUser($login);
$user->setPassword($password);
$user->setData($data);
return $user;
} catch (\Exception $e) {
// AuthenticationServiceException is thrown when an authentication request could not be processed due to a system problem. extends AuthenticationException
throw new AuthenticationServiceException('Access token invalide. '. $e->getMessage(), 0, $e);
}
}
public function supports(TokenInterface $token)
{
return $token instanceof UsernamePasswordToken;
}
} |
Partager