Symfony ldap аутентификация с использованием кредитов - PullRequest
0 голосов
/ 06 июня 2018

У пользователей Ldap есть динамический dn, должен быть поиск пользователя по его имени, чтобы получить свой dn и связать его с ним

У меня есть только один пользовательский источник (sql), но мне бы хотелось, чтобыпроверка пароля должна быть сделана в ldap или в базе данных

С помощью этой конфигурации пользователь проходит проверку подлинности, но он не загружается в базу данных и не имеет необходимых ролей

Если я удаляюпровайдер в форме form_login_ldap: my_chain_provider

У меня есть эта ошибка: «Выполнение запроса невозможно без привязки соединения в первую очередь.»

canНе могу найти нужную конфигурацию

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

Спасибо

1 Ответ

0 голосов
/ 11 июня 2018

Я нашел правильную конфигурацию

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))'

Но с этой конфигурацией у меня есть другая проблема:

Если пользователь проходит аутентификацию с паролем в datable, пользователь загружается с моей сущностью: AcDomain \ Travaux \ Entity \ Security \ User

Но если в его логе с пользователем ldap загружаются сущности symfonycore: Symfony \ Component \ Security \ Core \ User \ User

Поэтому я создал свою собственную аутентификацию с защитным средством 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)%'
...