Bonjour chère communauté SF.

Je travaille sur une projet eZPlatform et je dois mettre en place un User Provider.

eZPlatform est un CMS basé sur Symfony 3. Il suffit de voir ça comme un gros bundle.

Je souhaite garder le système de connexion habituel des utilisateurs.

  1. Formulaire de connexion (Login / Mot de passe)
  2. Recherche de l'utilisateur dans la base eZ. C'est un peut comme des utilisateurs dans des entités doctrine.


Mais si l'utilisateur n'existe pas dans la base eZ il faut aller vérifier si il n'est pas disponible sur un service distant.

  1. Formulaire de connexion (Login / Mot de passe)
  2. Recherche de l'utilisateur dans la base eZ. C'est un peut comme des utilisateurs dans des entités doctrine.
  3. Recherche de l'utilisateur dans le service distant.


Le service distant en question à été développé par un autre presta de mon client.
J'ai développé une API qui permet de réinterroger :

Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class MyAPI() {
  public function OAuthPassword($login, $password);
  /* return   {
                "access_token": "aaa",
                "token_type": "bearer",
                "refresh_token": "rrr",
                "expires_in": 4999,
                "scope": "read write trust",
                "service_ticket": "sss",
                "token": "ttt",
                "jti": "jjj"
   } */
  public function OAuthRefreshToken($refresh_token);
  // même retour que OAuthPassword
  public function OAuthCheckToken($access_token);
  public function OAuthPermissions($access_token);
  // Retournent des infos qui me seront utiles plus tard...
Pas de pb avec l'API elle semble répondre comme il faut.
En cas d'erreur elle renvoie des Exception

Puis j'ai suivit ce Tuto : How to Create a custom Authentication Provider https://symfony.com/doc/3.4/security..._provider.html

Mo token :

Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class MyUserToken extends AbstractToken
{
    public $login;
    public $password;
    public $data;
 
    public function __construct(array $roles = array())
    {
        parent::__construct($roles);
 
        // If the user has roles, consider it authenticated
        $this->setAuthenticated(count($roles) > 0);
    }
 
    public function getCredentials()
    {
        return '';
    }
 
    public function ready()
    {
        return $this->login && $this->password;
    }
}
Mon listener

Code : Sélectionner tout - Visualiser dans une fenêtre à part
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
class MyListener implements ListenerInterface
{
    protected $tokenStorage;
    protected $authenticationManager;
 
    public function __construct(TokenStorageInterface $tokenStorage, AuthenticationManagerInterface $authenticationManager)
    {
        $this->tokenStorage = $tokenStorage;
        $this->authenticationManager = $authenticationManager;
    }
 
    public function handle(GetResponseEvent $event)
    {
        $request = $event->getRequest();
 
        $login = $request->request->get('_username');
        $password = $request->request->get('_password');
 
        $token = new MyUserToken();
        $token->login  = $login;
        $token->password  = $password;
 
        try {
            $authToken = $this->authenticationManager->authenticate($token);
            $this->tokenStorage->setToken($authToken);
            return;
        } catch (AuthenticationException $failed) {
            $token = $this->tokenStorage->getToken();
            if ($token instanceof MyUserToken) {
                $this->tokenStorage->setToken(null);
            }
            return;
        }
    }
}
Mon Authentication Provider

Code : Sélectionner tout - Visualiser dans une fenêtre à part
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
class MyAuthenticationProvider implements AuthenticationProviderInterface
{
    private $userProvider; // MyUserProvider
 
    public function __construct(UserProviderInterface $userProvider)
    {
        $this->userProvider = $userProvider;
    }
 
    public function authenticate(TokenInterface $token)
    {
                              // ==== Je pense que mon PB est là ... =====
        if ($token instanceof MyUserToken && $token->ready()) {
            $user = $this->userProvider->getUser($token->login, $token->password);
            $token->setUser($user);
            return $token;
        }
        throw new AuthenticationException('The My authentication failed.');
    }
 
    public function supports(TokenInterface $token)
    {
        return $token instanceof MyUserToken;
    }
}
Mon UserProvider

Code : Sélectionner tout - Visualiser dans une fenêtre à part
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
class MyUserProvider implements UserProviderInterface
{
    /** @var MyAPI  */
    private $api;
 
    public function __construct(MyAPI $api)
    {
        $this->api = $api;
    }
 
    public function supportsClass($class)
    {
        return MyUser::class === $class;
    }
 
    public function loadUserByUsername($username)
    {
        // ==== Ici je ne sais pas quoi faire. Je ne peux pas trouver d'utilisateur à partir de son username ... 
        throw new UsernameNotFoundException("User not found by username");
        return new MyUser($username);
    }
 
    public function getUser($login, $password)
    {
        try {
            $data = $this->api->OAuthPassword($login, $password);
            $user = new MyUser($login);
            $user->setData($data);
            $user->login = $login;
            $user->password = $password;
            $user->data = $data;
            return $user;
        } catch (\Exception $e) {
            throw new UsernameNotFoundException(__METHOD__." User not found. ". $e->getMessage());
        }
    }
 
    public function refreshUser(UserInterface $user)
    {
        if (!$user instanceof MyUser) {
            throw new UnsupportedUserException(
                sprintf('Instances of "%s" are not supported.', get_class($user))
            );
        }
 
        if ($user->getRefreshToken()) {
            $data = $this->api->OAuthRefreshToken($user->getRefreshToken());
            $user->setData($data);
            return $user;
        }
        // Idem je sais pas trop quoi faire ici
    }
}
Ma class User

Code : Sélectionner tout - Visualiser dans une fenêtre à part
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
class MyUser implements UserInterface, EquatableInterface//, EncoderAwareInterface
{
    private $username;
    public $login;
    public $password = null;
    private $permissions = [];
    public $data;
 
    public function __construct($username, array $permissions = array())
    {
        $this->username = $username;
        $this->permissions = $permissions;
    }
 
    public function setPermissions($permissions = array())
    {
        $this->permissions = $permissions;
    }
 
    public function getPermissions()
    {
        return $this->permissions;
    }
 
    public function setData($data = array())
    {
        $this->data = $data;
    }
 
    public function getAccessToken()
    {
        return !empty($this->data['access_token'])  ? $this->data['access_token']  : '';
    }
    public function getRefreshToken()
    {
        return !empty($this->data['refresh_token'])  ? $this->data['refresh_token']  : '';
    }
 
    public function getRoles()
    {
        return array('ROLE_USER', 'ROLE_API_USER');
    }
 
    public function getPassword()
    {
        return $this->password;
    }
 
    public function getSalt()
    {
        return null;
    }
 
    public function getUsername()
    {
        return $this->username;
    }
 
    public function eraseCredentials()
    {
    }
 
    public function isEqualTo(UserInterface $user)
    {
        if (!$user instanceof MyUser) {
            return false;
        }
        return $this->username === $user->getUsername();
    }
}
Et pour finir avec le code : Voici ma factory :

Code : Sélectionner tout - Visualiser dans une fenêtre à part
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
class MySecurityFactory implements SecurityFactoryInterface
{
    public function create(ContainerBuilder $container, $id, $config, $userProvider, $defaultEntryPoint)
    {
        $providerId = 'security.authentication.provider.my.'.$id;
        $container
            ->setDefinition($providerId, new ChildDefinition(MyAuthenticationProvider::class))
            ->setArgument(0, new Reference($userProvider))
            ->setArgument(1, '@my.api') 
        ;
 
        $listenerId = 'security.authentication.listener.my.'.$id;
        $container->setDefinition($listenerId, new ChildDefinition(MyListener::class));
        return array($providerId, $listenerId, $defaultEntryPoint);
    }
 
    public function getPosition()
    {
        return 'pre_auth';
    }
 
    public function getKey()
    {
        return 'my';
    }
 
    public function addConfiguration(NodeDefinition $node)
    {
    }
}
Et la conf :

Code : Sélectionner tout - Visualiser dans une fenêtre à part
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
 
security:
    providers:
        chain_provider:
            chain:
                providers:
                    - ezpublish
                    - my
        my:
            id: my.user_provider
        ezpublish:
            id: ezpublish.security.user_provider
    firewalls:
        my:
            pattern: ^/
            anonymous: ~
            ezpublish_rest_session: ~
            form_login:
                require_previous_session: false
            logout: ~
            my: true # C'est quoi ça ?
 
        ezpublish_front:
            pattern: ^/
            anonymous: ~
            ezpublish_rest_session: ~
            form_login:
                require_previous_session: false
            logout: ~
Voila pour mon code.

Lorsque je ne connecte j'ai le message d'erreur : Bad credentials.