Symfony: Authentifiez les users contre le server LDAP, mais autorisez la connection uniquement si leur nom d'user est dans la table db personnalisée

D'abord une brève explication de ma tâche. J'utilise Symfony 2.8 et j'ai une application avec REST API et SonataAdminBundle. Les visiteurs du site Web peuvent afficher certaines données sur l'API REST qui est persistée dans la database. Un certain groupe d'employés devrait gérer ces données dans la zone d'administration.

L'access à la zone d'administration doit être protégé avec le nom d'user et le mot de passe. Il existe un Employee entité avec un username propriété, mais pas de mot de passe. L'authentification doit être effectuée contre le server LDAP, mais l'access à la zone d'administration doit être limité uniquement aux employés présents dans l' Employee l'entité, c'est-à-dire le tableau de la base de reference.

Pour l'authentification LDAP, j'utilise le nouveau composant LDAP dans Symfony 2.8.

Au-delà, il devrait y avoir un count admin comme in_memory user.

C'est ce que j'ai maintenant:

app/config/services.yml

 services: app.ldap: class: Symfony\Component\Ldap\LdapClient arguments: ["ldaps://ldap.uni-rostock.de"] app.db_user_provider: class: AppBundle\Security\DbUserProvider arguments: ["@docsortingne.orm.entity_manager"] 

app/config/security.yml

 security: role_hierarchy: ROLE_ADMIN: ROLE_USER ROLE_SUPER_ADMIN: [ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH] providers: chain_provider: chain: providers: [db_user, app_users] in_memory: memory: users: admin: { password: adminpass, roles: 'ROLE_ADMIN' } app_users: ldap: service: app.ldap base_dn: ou=people,o=uni-rostock,c=de search_dn: uid=testuser,ou=people,o=uni-rostock,c=de search_password: testpass filter: (uid={username}) default_roles: ROLE_USER db_user: id: app.db_user_provider firewalls: dev: pattern: ^/(_(profiler|wdt)|css|images|js)/ security: false admin: anonymous: true pattern: ^/ form_login_ldap: provider: chain_provider service: app.ldap dn_ssortingng: "uid={username},ou=people,o=uni-rostock,c=de" check_path: /login_check login_path: /login form_login: provider: in_memory check_path: /login_check login_path: /login logout: path: /logout target: / access_control: - { path: ^/admin, roles: ROLE_USER } encoders: Symfony\Component\Security\Core\User\User: plaintext AppBundle\Entity\Employee: bcrypt 

src/AppBundle/Entity/Employee.php

 namespace AppBundle\Entity; use Symfony\Component\Security\Core\User\UserInterface; use Symfony\Component\Security\Core\User\EquatableInterface; use Docsortingne\ORM\Mapping as ORM; class Employee implements UserInterface, EquatableInterface { // other properties private $username; // getters and setters for the other properties public function getUsername() { return $this->username; } public function getRoles() { return array('ROLE_USER'); } public function getPassword() { return null; } public function getSalt() { return null; } public function eraseCredentials() { } public function isEqualTo(UserInterface $user) { if (!$user instanceof Employee) { return false; } if ($this->username !== $user->getUsername()) { return false; } return true; } } 

src/AppBundle/Security/DbUserProvider.php

 <?php namespace AppBundle\Security; 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; use Docsortingne\ORM\EntityManager; use AppBundle\Entity\Employee; class DbUserProvider implements UserProviderInterface { private $em; public function __construct(EntityManager $em) { $this->em = $em; } public function loadUserByUsername($username) { $repository = $this->em->getRepository('AppBundle:Employee'); $user = $repository->findOneByUsername($username); if ($user) { return new Employee(); } throw new UsernameNotFoundException( sprintf('Username "%s" does not exist.', $username) ); } public function refreshUser(UserInterface $user) { if (!$user instanceof Employee) { throw new UnsupportedUserException( sprintf('Instances of "%s" are not supported.', get_class($user)) ); } return $this->loadUserByUsername($user->getUsername()); } public function supportsClass($class) { return $class === 'AppBundle\Entity\Employee'; } } 

L'authentification contre LDAP fonctionne comme un charme. Lorsqu'un employé de la database essaie de se connecter, il / elle est redirigé vers la page d'accueil ('/') et la connection est échouée. Tous les autres users, qui ne sont pas dans la database, peuvent se connecter sans problème.

C'est exactement le contraire de ce que je veux!

Si je string des fournisseurs comme ceci:

 chain_provider: chain: providers: [app_users, db_user] 

alors la méthode loadUserByUsername n'est même pas appelée et tous les users peuvent se connecter, ceux qui sont dans la database et ceux qui ne le sont pas.

L' in_memory user in_memory peut se connecter sans problème en tout cas.

J'apprécie toute aide. Si quelqu'un pense que toute mon approche est mauvaise et connaît une meilleure façon, n'épargnez pas avec les critiques.

Je sais qu'il y a FOSUserBundle et SonataUserBundle, mais je préférerais un fournisseur d'users personnalisé car je ne veux pas bloquer l'Employé de l'entité, car je n'ai vraiment pas besoin de toutes ces propriétés, comme mot de passe, sel, isLocked, etc. Je pense que la configuration de SonataUserBundle dans mon cas particulier serait beaucoup plus simple. Si vous pensez toujours qu'il existe une façon plus élégante de remplir ma tâche avec ces deux packages, je serai reconnaissant d'avoir un bon conseil.

vous pouvez configurer un vérificateur d'user par pare-feu, votre fournisseur d'access publicitaire db n'est pas vraiment un fournisseur d'access, car il ne dispose pas de toutes les informations nécessaires pour authentifier un user (par exemple, un mot de passe), alors, ce que je ferais, je supprimerais le fournisseur d'access publicitaire db et append un controller user à la place, l'idée principale du vérificateur user est d'append des controls supplémentaires au cours du process d'authentification. Dans votre cas, nous devons vérifier si l'user se trouve dans la table des employés ou non

vous devez faire trois choses pour mettre en œuvre cette

implémentez UserCheckerInterface Symfony\Component\Security\Core\User\UserCheckerInterface

vérifiez si l'user est dans la table des employés ou non dans la méthode checkPostAuth()

exposez votre nouveau vérificateur user en tant que service

 services: app.employee_user_checker: class: Path\To\Class\EmployeeUserChecker 

modifiez votre configuration de pare-feu pour utiliser le nouveau vérificateur d'user

 security: firewalls: admin: pattern: ^/admin user_checker: app.employee_user_checker #... 

Lire la suite