Utiliser le système de requête de DynamoDB avec Symfony2 est très simple, notre objectif est ici d’utiliser DynamoDB en tant que Provider de données d’authentification , ce qui peut s’avérer beaucoup plus sensible.

Attention, le code ci-desssous est un « exemple » , une simple introduction a l’implémentation des Fournisseurs d’utilisateurs Personnalisé.

Pour les besoins de l’exemple , les mots de passe sont stockés en clair dans le DynamoDB…. Autant dire qu’il ne faut jamais l’appliquer en production 🙂

DynamoDB ?

Si vous vous posez encore la question, c’est que vous n’avez pas encore lu notre précédent article

DynamoDB_LogoDynamoDB n’est pas compatible avec Doctrine (comme la plupart des base de données orientées Document jusque la), cette contrainte oblige de passer par un système d’authentification un peu différent de celui proposé par Symfony2 par défaut.

Symfony2 / AWS SDK

Pour démarrer, il faut tout simplement installer le SDK AWS dans l’environnement Symfony2, le plus simple étant d’utiliser le composer.

1
2
3
4
5
"require":{
——.....
——"aws/aws-sdk-php":"2.*",
——.....
}

Pour simplifier les appels a l’API , il est judicieux d’installer par la même occasion le bundle platiniumpixs

1
2
3
4
5
"require":{
——.....
——"platinumpixs/aws-symfony2-bundle": "dev-master"
——.....
}

Et pour finir , on rajoute ce bundle dans le fichier appkernel.php. Ce bundle a simplement pour objectif de faciliter l’appel aux API AWS en proposant des services d’authentification AWS.

1
new PlatinumPixs\Aws\PlatinumPixsAwsBundle(),

La phase d’installation sur la partie AWS est quasiment terminé. Il ne nous reste plus qu’a rajouter les éléments de configurations. Il suffit d’ajouter l’élément suivant dans le fichier de configuration (config.yml dans notre cas) :

1
2
3
4
5
platinum_pixs_aws:
—base:
——region: us-east-1
——key: AKIAIDKHUDZD5IA
——secret: 5gP67KO8I45F8ALc7rOYpDRcURi/besOKe

Symfony2 / Authentification et sécurité

La gestion de la Sécurité et de l’authentification sur Symfony2 est un processus a la fois simple et assez complet. La documentation officielle vous permettra de comprendre un peu mieux les bases de ce processus.

L’objectif est de définir une source de données externe pour permettre aux utilisateurs stockés dans DynamoDB de se connecter. La difficulté principale, étant que Doctrine2 ne support pas DynamoDB en tant qu’ORM , nous sommes donc obligé de spécifier un nouvelle source de données en tant que Fournisseur d’utilisateur Personnalisé (Custom User Provider). 

La documentation fournie par Symfony2 est très bonne et vous permettra de comprendre les mécaniques décrites ci-dessous.

Configuration du User Entity

Pour cet exemple, nous allons définir un User Entity customisé afin de gérer les utilisateurs de notre application. Ce User Entity doit nécessairement implémenter l’élément UserInterface qui est une classe.

Vu que nous n’utilisons pas Doctrine , la classe WebserviceUser doit être placée dans le répertoire Ynote/UserBundle/Security/User , c’est un service séparé qui est séparé au niveau des répertoires pour des raisons de commodités.

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
<?php

//Ynote/UserBundle/Security/User/WebserviceUser.php

namespace Ynote\UserBundle\Security\User;
use Symfony\Component\Security\Core\User\UserInterface;

class WebserviceUser implements UserInterface {

private $salt;
private $password;
private $email;
private $roles;

// Vous pouvez rajouter toutes les variables User souhaités , mais il faut au moins définir les 4 variables ci-dessus

/* Le constructeur est spécifique et lié au Custom User Provider que nous verrons ci-dessous */

public function __construct($username, $password, $salt, array $roles) {
——$this->username = $username;
——$this->password = $password;
——$this->salt = $salt;
——$this->roles = $roles;
}

/* Les méthodes ci-dessous sont a implémentés obligatoire vu les contraintes du UserInterface. En dehors de la méthode getSalt qui a été adapté pour les mots de passe en clair dans cette exemple , rien de très spéciale */

public function getRoles(){
——return $this->roles;
}

public function getPassword(){
——return $this->password;
}

public function getSalt(){
——return $this->salt;
}

public function getUsername(){
——return $this->username;
}

public function eraseCredentials(){
}

}

Configuration du User Provider

Pour utiliser notre propre Entité d’utilisateur dans le processus d’authentification de Symfony2 , nous allons utiliser le système de Service et le système d’authentification de Symfony.

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
<?php
// src/Acme/WebserviceUserBundle/Security/User/WebserviceUserProvider.php
namespace Ynote\UserBundle\Security\User;

use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
use Symfony\Component\Security\Core\Exception\UnsupportedUserException;

class WebserviceUserProvider implements UserProviderInterface{
protected $aws;

public function __construct(\Aws\Common\Aws $aws){
——$this->aws = $aws;
}

public function loadUserByUsername($username){
——$dynamoClient = $this->aws->get('DynamoDb');

——// On utiliser la table yn_user présente dans DynamoDB

——$dynamoClient->describeTable(array(
———'TableName' => 'yn_user'
——));
——$userData = "";
——$item=false;

——// On fait une requête dans DynamoDB pour retrouver l'email fournit dans notre formulaire de login

——$iterator = $dynamoClient->getIterator('Scan', array(
———'TableName' => 'yn_user',
———'ScanFilter' => array(
————'email' => array(
—————'AttributeValueList' => array(
——————array('S' => $username)
—————),
—————'ComparisonOperator' => 'EQ'
—————)
——)));

——foreach ($iterator as $item) {
———$userData = $item;
——}

——$userData = $item;

——if ($userData) {

———// On récupère les champ password / email / Salt / Roles présent dans le DynamoDB
———$password = $item['password']['S'];
———$email = $item['email']['S'];
———$salt = $item['salt']['S'];
———$roles = array($item['roles']['S']);
———return new WebserviceUser($username, $password, $salt, $roles);
——}else{
———throw new UsernameNotFoundException(sprintf('Username "%s" does not exist.', $username));
——}
}

public function refreshUser(UserInterface $user){
——if (!$user instanceof WebserviceUser) {
———throw new UnsupportedUserException(sprintf('Instances of "%s" are not supported.', get_class($user)));
——}
——return $this->loadUserByUsername($user->getUsername());
}

public function supportsClass($class){
——return $class === 'Ynote\UserBundle\Security\User\WebserviceUser';
}
}

Il faut noter que le User Provider, implémente  l’interface UserProviderInterface . Trois méthodes sont présentes ayant pour but de faire les choses suivantes :

  • loadUserByUsername() – Cette méthode a pour objectif de récupérer un user particulier dans DynamoDB en fonction du paramêtre login dans le formulaire associé.
  • refreshUser() – Cette fonction permet de rafraichir de le User chargé dans la sessions Symfony , pour s’assurer qu’il est toujours le même que celui chargé lors du login.
  • supportsClass() – Une méthode utilisé pour s’assurer que la classe utilisée est la même que celle de notre User Entity

Fichier Configuration

C’est bientôt fini ! A ce stade , tout ce qu’il reste a faire c’est de caller les fichiers de configuration. Il va falloir  rajouter le service de User Provider dans nos fichiers de configuration. Pour le faire , nous allons ajouter les paramêtres suivants dans le fichier  Ynote/UserBundle/Resources/Config/services.yml

1
2
3
4
5
6
7
—parameters:
——webservice_user_provider.class: Ynote\UserBundle\Security\User\WebserviceUserProvider

—services:
——webservice_user_provider:
———class: "%webservice_user_provider.class%"
———arguments: ["@platinum_pixs_aws.base"]

L’argument du service permet simplement d’ajouter le contexte AWS dans le contstructeur du UserProvider
Il faut ensuite ajuster le fichier security.yml

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
—security:
——encoders:
———Ynote\UserBundle\Security\User\WebserviceUser: plaintext

—role_hierarchy:
——ROLE_ADMIN: ROLE_USER
——ROLE_SUPER_ADMIN: [ ROLE_USER, ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH ]

—providers:
——webservice:
———id: webservice_user_provider

—firewalls:
——dev:
———pattern: ^/(_(profiler|wdt)|css|images|js)/
———security: false

—user_area:
——pattern: ^/
——anonymous: ~
——form_login:
———login_path: /login
———check_path: /login_check
———default_target_path: /user/index
——logout:
———path: /logout
———target: /

—access_control:
——- { path: ^/admin, roles: ROLE_ADMIN }
——- { path: ^/user/, roles: ROLE_USER }
——- { path: ^/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }

Et voila… Maintenant que tout ça est calé, il suffit maintenant de régler le routing dans l’application et de gérer un formulaire de login proprement.

Vous avez maintenant un système d’authentification basique utilisant DynamoDB en tant que liste des comptes utilisateurs.

Bien entendu, cet exemple est très basique et non sécurisé, mais permet de commencer a prendre la main sur le concept de Fournisseur d’Utilisateur personnalisé sur Symfony2.

Télécharger le Bundle Source