src/Security/SecurityLdapFormAuthenticator.php line 41

  1. <?php
  2. namespace App\Security;
  3. use Symfony\Component\HttpFoundation\RedirectResponse;
  4. use Symfony\Component\HttpFoundation\Request;
  5. use Symfony\Component\HttpFoundation\Response;
  6. use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
  7. use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
  8. use Symfony\Component\Security\Core\Exception\CustomUserMessageAuthenticationException;
  9. use Symfony\Component\Security\Core\Exception\InvalidCsrfTokenException;
  10. use Symfony\Component\Security\Core\Security;
  11. use Symfony\Component\Security\Core\User\UserInterface;
  12. use Symfony\Component\Security\Core\User\UserProviderInterface;
  13. use Symfony\Component\Security\Csrf\CsrfToken;
  14. use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface;
  15. use Symfony\Component\Security\Http\Authenticator\AbstractLoginFormAuthenticator;
  16. use Symfony\Component\Security\Http\Authenticator\Passport\Badge\CsrfTokenBadge;
  17. use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
  18. use Symfony\Component\Security\Http\Authenticator\Passport\Credentials\PasswordCredentials;
  19. use Symfony\Component\Security\Http\Authenticator\Passport\Credentials\CustomCredentials;
  20. use Symfony\Component\Security\Http\Authenticator\Passport\Passport;
  21. use Symfony\Component\Security\Http\Util\TargetPathTrait;
  22. use Symfony\Component\Security\Http\SecurityRequestAttributes;
  23. use Symfony\Component\Ldap\Ldap;
  24. use Symfony\Component\Ldap\LdapInterface;
  25. use Symfony\Component\Ldap\Adapter\QueryInterface;
  26. use Symfony\Component\Security\Core\Exception\UnsupportedUserException;
  27. use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
  28. use Symfony\Component\Security\Core\Exception\LockedException;
  29. use Symfony\Component\Security\Core\Exception\AuthenticationException;
  30. use App\Repository\UserRepository;
  31. use App\Entity\User;
  32. use Symfony\Component\Ldap\Exception\InvalidCredentialsException;
  33. use Symfony\Component\Ldap\Exception\InvalidSearchCredentialsException;
  34. class SecurityLdapFormAuthenticator extends AbstractLoginFormAuthenticator
  35. {
  36.     use TargetPathTrait;
  37.     private UrlGeneratorInterface $urlGenerator;
  38.     private CsrfTokenManagerInterface $csrfTokenManager;
  39.     public function __construct(UrlGeneratorInterface $urlGeneratorCsrfTokenManagerInterface $csrfTokenManager)
  40.     {
  41.         $this->urlGenerator $urlGenerator;
  42.         $this->csrfTokenManager $csrfTokenManager;
  43.     }
  44.     protected function getLoginUrl(Request $request): string
  45.     {
  46.         return $this->urlGenerator->generate('security_ldap_login');
  47.     }
  48.     public function supports(Request $request): bool
  49.     {
  50.         return 'security_ldap_login' === $request->attributes->get('_route') && $request->isMethod('POST');
  51.     }
  52.     public function getCredentials(Request $request): array
  53.     {
  54.         $credentials = [
  55.             'username' => $request->request->get('_username'),
  56.             'password' => $request->request->get('_password'),
  57.             'csrf_token' => $request->request->get('_csrf_token'),
  58.         ];
  59.         $credentials['username'] = trim($credentials['username']);
  60.         $request->getSession()->set(
  61.             SecurityRequestAttributes::LAST_USERNAME,
  62.             $credentials['username']
  63.         );
  64.         return $credentials;
  65.     }
  66.     public function checkCredentials($credentialsUserInterface $user): bool
  67.     {
  68.         // In this scenario, this method is by-passed since user authentication need to be managed before in getUser method.
  69.         return true;
  70.     }
  71.     public function onAuthenticationSuccess(
  72.         Request $request,
  73.         TokenInterface $token,
  74.         string $firewallName
  75.     ): ?Response {
  76.         // $providerKey = $firewallName
  77.         $request->getSession()->getFlashBag()->add('info''connected!');
  78.         if ($targetPath $this->getTargetPath($request->getSession(), $firewallName)) {
  79.             return new RedirectResponse($targetPath);
  80.         }
  81.         return new RedirectResponse($this->urlGenerator->generate('home'));
  82.     }
  83.     public function authenticate(Request $request): Passport
  84.     {
  85.         $credentials $this->getCredentials($request);
  86.         $userBadge = new UserBadge($credentials["username"]);
  87.         $customCredentials = new CustomCredentials(
  88.             function($credentialsUserInterface $user) {
  89.                 if ($credentials["username"] && $credentials["password"
  90.                     && strtoupper($user->getUserIdentifier()) === strtoupper($credentials["username"])
  91.                     && $this->getUserEntityCheckedFromLdap($credentials["username"], $credentials["password"], $user) !== NULL) {
  92.                     return true;
  93.                 }
  94.                 return false;
  95.             },
  96.             $credentials
  97.         );
  98.         $passport = new Passport($userBadge$customCredentials, [new CsrfTokenBadge('authenticate'$request->request->get('_csrf_token'))]);
  99.         return ($passport);
  100.     }
  101.     /**
  102.      * search user against ldap and returns the matching App\Entity\User. The $user entity will be created if not exists.
  103.      * @param string $username
  104.      * @param string $password
  105.      * @return User|object|null
  106.      */
  107.     public function getUserEntityCheckedFromLdap(string $usernamestring $passwordUser $user)
  108.     {
  109.         $ldapSearchDn $_ENV["LDAP_USER"];
  110.         $ldapSearchPassword $_ENV["LDAP_PASS"];
  111.         $ldapSearchDnString $_ENV["LDAP_DN"];
  112.         $server $_ENV["LDAP_SERVER"];
  113.         $ldap Ldap::create('ext_ldap',[
  114.             'host' => $server,
  115.             'encryption' => 'tls',
  116.             'version' => '3',
  117.             'referrals' => false,
  118.             'options' => ['debug_level' => 7'x_tls_require_cert' => 'never'],
  119.         ]);
  120.         try {
  121.             $ldap->bind($ldapSearchDn$ldapSearchPassword);
  122.         } catch (InvalidCredentialsException) {
  123.             throw new InvalidSearchCredentialsException();
  124.         }
  125.         $username $ldap->escape($username''LdapInterface::ESCAPE_FILTER);
  126.         $search $ldap->query($ldapSearchDnString"(sAMAccountName=" $username ")", ['scope' => QueryInterface::SCOPE_SUB]);
  127.         $entries $search->execute()->toArray();
  128.         $count count($entries);
  129.         if (!$count) {
  130.             throw new AuthenticationException(sprintf('User "%s" not found.'$username));
  131.         }
  132.         if ($count 1) {
  133.             throw new AuthenticationException('More than one user found');
  134.         }
  135.         $ldapEntry $entries[0];
  136.         $dn $ldapEntry->getDn();
  137.         try {
  138.             $ldap->bind($dn$password);
  139.         } catch (InvalidCredentialsException) {
  140.             throw new AuthenticationException('Credentials Invalid');
  141.         }
  142.         //Si l'utilisateur n'existe pas en DB
  143.         if (!$user)
  144.             throw new AuthenticationException("User doesn't exist on ENSO");
  145.         //S'il existe, on récupère l'employee
  146.         $employee $user->getEmployee();
  147.         if (!$employee)
  148.             throw new LockedException();
  149.         return $user;
  150.     }
  151. }