Ldap users have dynamic dn there must be a search of the user following his username to get his dn and make a bind with it
I only have one user source (sql), but I would like the password check to be done on the ldap or in the database
With this config, the user is authenticated but it is not loaded in the database and does not have the right roles
If I remove provider in form_login_ldap: my_chain_provider
I have this error : "Query execution is not possible without binding the connection first."
I can not find the right configuration
security.yaml
security:
providers:
my_chain_provider:
chain:
providers: [fos_userbundle, my_ldap]
my_ldap:
ldap:
service: Symfony\Component\Ldap\Ldap
base_dn: DC=domain,DC=be
search_dn: "CN=admin,OU=apps,DC=domain,DC=be"
search_password: mdp
default_roles: ROLE_USER
fos_userbundle:
id: fos_user.user_provider.username_email
encoders:
Symfony\Component\Security\Core\User\User: bcrypt
AcDomain\Travaux\Entity\Security\User: bcrypt # ou sha512
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
secured_area:
pattern: ^/
provider: fos_userbundle
form_login:
provider: my_chain_provider
use_referer: true
form_login_ldap:
provider: my_chain_provider
service: Symfony\Component\Ldap\Ldap
dn_string: 'DC=domain,DC=be'
query_string: 'sAMAccountName=*{username}*'
logout: true
anonymous: true
access_control:
- { path: ^/login$, role: IS_AUTHENTICATED_ANONYMOUSLY }
service.yaml
Symfony\Component\Ldap\Ldap:
arguments: ['@Symfony\Component\Ldap\Adapter\ExtLdap\Adapter']
Symfony\Component\Ldap\Adapter\ExtLdap\Adapter:
arguments:
- host: ldap.domain.be
port: 636
encryption: ssl
options:
protocol_version: 3
referrals: false
Thanks
I found the right configuration
service.yaml
Symfony\Component\Ldap\Ldap:
arguments: ['@Symfony\Component\Ldap\Adapter\ExtLdap\Adapter']
Symfony\Component\Ldap\Adapter\ExtLdap\Adapter:
arguments:
- host: '%env(ACLDAP_URL)%'
port: 636
encryption: ssl
options:
protocol_version: 3
referrals: false
security.yaml
security:
providers:
sql_provider:
id: AcMarche\Travaux\Security\UserProvider
ldap_provider:
ldap:
service: Symfony\Component\Ldap\Ldap
base_dn: '%env(ACLDAP_DN)%'
search_dn: '%env(ACLDAP_USER)%'
search_password: '%env(ACLDAP_PASSWORD)%'
default_roles: ROLE_USER
uid_key: sAMAccountName
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
secured_area:
pattern: ^/
provider: sql_provider
form_login:
use_referer: true
remember_me: true
form_login_ldap:
provider: ldap_provider
use_referer: true
remember_me: true
service: Symfony\Component\Ldap\Ldap
dn_string: '%env(ACLDAP_DN)%'
query_string: '(&(|(sAMAccountName=*{username}*))(objectClass=person))'
But with this config I have a other problem :
I the user is authenticate with password in datable, user are loaded with my entity : AcDomain\\Travaux\\Entity\\Security\\User
But if he 's log with ldap user are loaded with entity symfony core : Symfony\\Component\\Security\\Core\\User\\User
So I created my own authentication with guard FormLoginAuthenticator
form_login:
use_referer: true
remember_me: true
guard:
authenticators:
- AcDomain\Travaux\Security\FormLoginAuthenticator
FormLoginAuthenticator.php
class FormLoginAuthenticator extends AbstractFormLoginAuthenticator
{
use TargetPathTrait;
private $userRepository;
private $router;
private $encoder;
private $ldap;
/**
* @var CsrfTokenManagerInterface
*/
private $csrfTokenManager;
public function __construct(
UserRepository $userRepository,
UserPasswordEncoderInterface $encoder,
RouterInterface $router,
StaffLdap $ldap,
CsrfTokenManagerInterface $csrfTokenManager
) {
$this->router = $router;
$this->userRepository = $userRepository;
$this->encoder = $encoder;
$this->ldap = $ldap;
$this->csrfTokenManager = $csrfTokenManager;
}
/**
* Return the URL to the login page.
*
* @return string
*/
protected function getLoginUrl()
{
return $this->router->generate('travaux_login');
}
/**
* Does the authenticator support the given Request?
*
* If this returns false, the authenticator will be skipped.
*
* @param Request $request
*
* @return bool
*/
public function supports(Request $request)
{
if ($request->getPathInfo() == '/login_check' && $request->isMethod('POST')) {
return true;
}
return false;
}
/**
* Get the authentication credentials from the request and return them
* as any type (e.g. an associate array).
*
* Whatever value you return here will be passed to getUser() and checkCredentials()
*
* For example, for a form login, you might:
*
* return array(
* 'username' => $request->request->get('_username'),
* 'password' => $request->request->get('_password'),
* );
*
* Or for an API token that's on a header, you might use:
*
* return array('api_key' => $request->headers->get('X-API-TOKEN'));
*
* @param Request $request
*
* @return mixed Any non-null value
*
* @throws \UnexpectedValueException If null is returned
*/
public function getCredentials(Request $request)
{
return [
'username' => $request->request->get('_username'),
'password' => $request->request->get('_password'),
'token' => $request->request->get('_csrf_token'),
];
}
/**
* Return a UserInterface object based on the credentials.
*
* The *credentials* are the return value from getCredentials()
*
* You may throw an AuthenticationException if you wish. If you return
* null, then a UsernameNotFoundException is thrown for you.
*
* @param mixed $credentials
* @param UserProviderInterface $userProvider
*
* @throws AuthenticationException
*
* @return UserInterface|null
*/
public function getUser($credentials, UserProviderInterface $userProvider)
{
$username = $credentials['username'];
$user = null;
try {
return $this->userRepository->loadUserByUsername($username);
} catch (NonUniqueResultException $e) {
}
return null;
}
/**
* Returns true if the credentials are valid.
*
* If any value other than true is returned, authentication will
* fail. You may also throw an AuthenticationException if you wish
* to cause authentication to fail.
*
* The *credentials* are the return value from getCredentials()
*
* @param mixed $credentials
* @param UserInterface $user
*
* @return bool
*
* @throws AuthenticationException
*/
public function checkCredentials($credentials, UserInterface $user)
{
$token = $credentials['token'];
if (false === $this->csrfTokenManager->isTokenValid(new CsrfToken('authenticate', $token))) {
throw new InvalidCsrfTokenException('Invalid CSRF token.');
}
$entry = $this->ldap->getEntry($user->getUsername());
if ($entry instanceof Entry) {
$dn = $entry->getDn();
try {
$this->ldap->bind($dn, $credentials['password']);
return true;
} catch (\Exception $exception) {
//throw new BadCredentialsException($exception->getMessage());
}
}
//try check password in db
return $this->encoder->isPasswordValid($user, $credentials['password']);
}
/**
* Called when authentication executed and was successful!
*
* This should return the Response sent back to the user, like a
* RedirectResponse to the last page they visited.
*
* If you return null, the current request will continue, and the user
* will be authenticated. This makes sense, for example, with an API.
*
* @param Request $request
* @param TokenInterface $token
* @param string $providerKey The provider (i.e. firewall) key
*
* @return Response|null
*/
public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey)
{
$targetPath = $this->getTargetPath($request->getSession(), $providerKey);
return new RedirectResponse($targetPath);
// return new RedirectResponse($this->router->generate('intervention'));
}
}
StaffLdap.php
class StaffLdap
{
private $ldap;
private $dn;
public function __construct(string $host, $dn, string $user, string $password)
{
$this->ldap = Ldap::create(
'ext_ldap',
array(
'host' => $host,
'encryption' => 'ssl',
)
);
$this->dn = $dn;
$this->ldap->bind($user, $password);
}
/**
* @param $uid
* @return \Symfony\Component\Ldap\Entry|null
*
*/
public function getEntry($uid)
{
$filter = "(&(|(sAMAccountName=*$uid*))(objectClass=person))";
$query = $this->ldap->query($this->dn, $filter, ['maxItems' => 1]);
$results = $query->execute();
if ($results->count() > 0) {
return $results[0];
}
return null;
}
/**
* @param $user
* @param $password
* @throws LdapException
*/
public function bind($user, $password)
{
try {
$this->ldap->bind($user, $password);
} catch (\Exception $exception) {
throw new BadCredentialsException($exception->getMessage());
}
}
/**
* @return \Symfony\Component\Ldap\Adapter\EntryManagerInterface
*/
public function getEntryManager()
{
return $this->ldap->getEntryManager();
}
}
service.yaml
AcDomain\Travaux\Security\StaffLdap:
$host: '%env(ACLDAP_URL)%'
$dn: '%env(ACLDAP_DN)%'
$user: '%env(ACLDAP_USER)%'
$password: '%env(ACLDAP_PASSWORD)%'
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.