Search code examples
symfonyfosuserbundlefosrestbundlefosoauthserverbundle

Symfony3 FosUser and FosAuthServer, login user with LDAP on AuthenticationFailureHandler


I'm working in symfony with FosUser and FosAuthServer. I have a front web site who access to an API by Oauth2. On the front web site, if the user is not logon, redirect him to the oauth serveur and after the user is login, redirect him to the font web site with an oauth2 token.

It's work well, but I need this : if user is find in the database (my user provider), longin him if the user is not find in the database, I search him in LDAP and I login him manually.

To do this, I use the AuthenticationFailureHandler to find the user in the LDAP. After find him, I create a User Entity and dispatch envent FOSUserEvents::SECURITY_IMPLICIT_LOGIN, but I don't know who can I create a token and redirect to the front web site.

security.yml

security:
    encoders:
        AppBundle\Entity\User: sha512
        Symfony\Component\Security\Core\User\User: plaintext

    providers:
        api:
            id: fos_user.user_provider.username_email
        admin:
            memory:
                users:
                    admin:  { password: password}

    firewalls:
        doc:
            pattern:  ^/api/doc
            anonymous: true

        oauth_token:
            pattern:  ^/oauth/v2/token
            security: false

        oauth_authorize:
            pattern:  ^/oauth/v2/auth
            guard:
                authenticators:
                    - app.security.login_form_authenticator
            form_login:
                provider: api
                csrf_token_generator: security.csrf.token_manager
                login_path: fos_user_security_login
                check_path: fos_user_security_check
                failure_handler: app.authentification_failure_handler
            logout:
                path: fos_user_security_logout
                target: fos_user_security_login
            anonymous: true

        admin:
            pattern:  ^/admin
            http_basic:
                realm:    'Secured Area'
                provider: admin
            anonymous:  false

        api:
            pattern:  ^/api
            fos_oauth:  true
            stateless:  true
            anonymous:  false



access_control:
    - { path: ^/oauth/v2/auth, roles: [ IS_AUTHENTICATED_ANONYMOUSLY ] }
    - { path: ^/api/doc, roles: [ IS_AUTHENTICATED_ANONYMOUSLY ] }
    - { path: ^/admin, roles: [ IS_AUTHENTICATED_FULLY ] }
    - { path: ^/, roles: [ ROLE_USER ] }

Service.yml

 app.authentification_failure_handler:
    public: false
    class: AppBundle\Handler\AuthenticationFailureHandler
    arguments:
        - '@utilities.active_directory.ageo'
        - '@doctrine.orm.entity_manager'
        - '@fos_user.user_manager'
        - '@security.token_storage'
        - '@debug.event_dispatcher'
        - '@router'
        - '@http_kernel'
        - '@security.http_utils'

AuthentificationFaillureHandler.php

<?php

namespace AppBundle\Handler;


use AppBundle\Entity\Assure;
use AppBundle\Entity\BrokerStructur;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\EntityManagerInterface;
use FOS\OAuthServerBundle\Model\AuthCodeManager;
use FOS\UserBundle\Event\FilterUserResponseEvent;
use FOS\UserBundle\Event\GetResponseUserEvent;
use FOS\UserBundle\Event\UserEvent;
use FOS\UserBundle\FOSUserEvents;
use FOS\UserBundle\Model\UserManager;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Session\Session;
use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\Routing\RouterInterface;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage;
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Http\Authentication\DefaultAuthenticationFailureHandler;
use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;
use Symfony\Component\Security\Http\HttpUtils;


class AuthenticationFailureHandler extends DefaultAuthenticationFailureHandler
{
   // private $cafeteriaEntityManager;
    private $router;

    /**
     * @var \Ageo\UtilitiesBundle\ActiveDirectory\Ageo      Connection au LDAP Ageo
     */
    private $ldap;

    /**
     * @var $entityManagerSql           Manager des entitées de la base mysql
     */
    private $entityManagerSql;

    /**
     * @var UserManager                 Manager des utilisateurs (FosUserBundle)
     */
    private $userManager;

    /**
     * @var EventDispatcher
     */
    private $eventDispatcher;

    /**
     * @var TokenStorage
     */
    private $tokenStorage;


    public function __construct(\Ageo\UtilitiesBundle\ActiveDirectory\Ageo $ldap, EntityManager $entityManagerSql, UserManager $userManager,
                                TokenStorage $tokenStorage, \Symfony\Component\HttpKernel\Debug\TraceableEventDispatcher  $eventDispatcher,
                                RouterInterface $router, HttpKernelInterface $httpKernel, HttpUtils $httpUtils)
    {
        $this->ldap = $ldap;
        $this->router = $router;
        $this->entityManagerSql = $entityManagerSql;
        $this->userManager = $userManager;
        $this->tokenStorage = $tokenStorage;
        $this->eventDispatcher = $eventDispatcher;
        parent::__construct($httpKernel, $httpUtils, array(), null);

    }

    /**
     * Si l'utilisateur n'est pas présent dans la base de données local, on le cherche dans l'Active directory pour le logguer
     * On vérifie ensuite s'il existe dans la base de donné local. S'il existe, on le charge et on le met à jour, sinon, on le crée.
     * @param Request $request
     * @param AuthenticationException $exception
     * @return RedirectResponse|\Symfony\Component\HttpFoundation\Response
     */
    public function onAuthenticationFailure(Request $request, AuthenticationException $exception)
    {
        $username = trim($request->request->get('_username'));
        $password = trim($request->request->get('_password'));

        /*
         * Manupulation to find the user in LDAP => $userAd
         */
        $loaclUser = $this->userManager->createUser();
        $loaclUser
            ->setEnabled(true)
            ->setUsername($username)
            ->setPlainPassword($password)
            ->setEmail($userAd->hasAttribute('mail') ? $userAd->getAttribute('mail')[0] : null )

        $this->userManager->updateUser($loaclUser);

        $this->eventDispatcher->dispatch(FOSUserEvents::SECURITY_IMPLICIT_LOGIN, new UserEvent($loaclUser, $request));

        //$this->eventDispatcher->dispatch(FOSUserEvents::REGISTRATION_COMPLETED, new FilterUserResponseEvent($loaclUser, $request, $response));

        // I need to login my user on authServeur


        // I have test this, but it's not working
        if ($this->session->has('_security.oauth_authorize.target_path'))
        {
            parse_str(parse_url($this->session->get('_security.oauth_authorize.target_path'), PHP_URL_QUERY), $target_path);
            $url = $this->session->get('_security.oauth_authorize.target_path');

        }
        $response = new RedirectResponse($url);



        return $response;
    }

}

Anyone say who can I do the same proccess of the login_check ?


Solution

  • I never done it but this seems to be the use case of the chain provider

    security:
        providers:
            chain_provider:
                chain:
                    providers: [api, admin, ldap_provider]