简体   繁体   中英

Symfony ldap authentification with crediantials

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM