1
votes

With Symfony 3 I'm using a custom guard authenticator that is operating correctly to authenticate a user directly. I now want to support user impersonation but when attempting to impersonate a user using the _switch_user query parameter, the getUser() object is always the original user.

My security config is

security:

    encoders:
        Symfony\Component\Security\Core\User\User: plaintext

    providers:
        app:
            entity:
                class: AppBundle:Authentication
                property: username

    firewalls:
        dev:
            pattern: ^/(_(profiler|wdt)|css|images|js)/
            security: false

        main:
            anonymous: ~
            switch_user: true
            guard:
                authenticators:
                    - app.authenticator

    role_hierarchy:
        ROLE_ADMIN:       ROLE_USER
        ROLE_SUPER_ADMIN: [ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH]

    access_control:
        - { path: ^/admin/, role: ROLE_ADMIN }
        - { path: ^/, role: ROLE_USER }
​

My authenticator is

class AppAuthenticator extends AbstractGuardAuthenticator
{
    public function getCredentials(Request $request)
    {
        $username = $request->query->get('username');
        if (empty($username)) {
            return;
        }

        return array(
		    'username' => $username,
        );
    }

    public function getUser($credentials, UserProviderInterface $userProvider)
    {
        $username = $credentials['username'];
       	return $userProvider->loadUserByUsername($username);
     }

    public function checkCredentials($credentials, UserInterface $user)
    {
        return true;
    }

    public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey)
    {
        return null;
    }

    public function onAuthenticationFailure(Request $request, AuthenticationException $exception)
    {
		$data = array(
				'message' => strtr($exception->getMessageKey(), $exception->getMessageData())

				// or to translate this message
				// $this->translator->trans($exception->getMessageKey(), $exception->getMessageData())
		);

		return new JsonResponse($data, 403);
    }

    public function start(Request $request, AuthenticationException $authException = null)
    {
        return new RedirectResponse(
            ...
            )
    }

    public function supportsRememberMe()
    {
        return false;
    }

}

It seems that the getCredentials() method of authenticator is always called and I can see no way to detect impersonation in this. So, my question is can users be impersonated when using guard authentication and if so, what am I missing?

1

1 Answers

1
votes

I found a way to do that, but it doesn't rely on native switch_user system and has dependency with your application logic.

The principle is basic, you implements your own switching system based on two properties on your User class:

  • impersonate : a string value that stores the username of the user to be impersonated
  • impersonatedBy : a string value that contains (no db storage) the username of the user doing the impersonation

NB: You can also store the User in those properties (e.g. mapped with Doctrine)

So, when you tell your guard authenticator to search for a user (getUser), you ask the UserProvider to return the user's impersonated one ($user->getImpersonate), adding to it the user that makes impersonation ($impersonatedUser->setImpersonatedBy).