Christopher Francisco
Christopher Francisco

Reputation: 16288

ZF2 - Assuming other user's identity

Before I dive into reinventing the wheel, I'd first like to check if ZF2 supports, either out-of-the-box or with a 3rd party library, this particular use case where admins log in as other user, or assume their identity.

If not, as I'm not familiar with ZF2 internal design, how would I go into implementing this, with the only constraint being that the system is already built, so I can't change components (controllers, auth services, etc) into supporting it.

My first thought would be to make a mechanism to switch the logged user information stored in the session storage, with the one whose identity I want to assume. Then, write to the session, under a different namespace, the original user information (admin) so that it can be reverted.

Going by this approach, I am expecting components like Zend\Authentication\AuthenticationService return the user whose identity I'm assuming. So, in every call I make to $this->identity()->getId() (identity being a controller plugin for AuthenticationService, that returns the User) in other controllers, the business logic will work normally.

Having said this, the questions would be:

  1. Is there a solution already for this?
  2. Is my approach correct in assuming that by overwriting the session storage I can assume other user ID and expect ZF2 components to work accordingly, or is there any considerations regarding ZF2 internal design/infrastructure I haven't taken in consideration that I should?
  3. Maybe there's a better way to do this?

Upvotes: 2

Views: 403

Answers (1)

thomasstuttard
thomasstuttard

Reputation: 357

I think you would need to create your own AuthenticationAdaptor.

class AdminUserLoginAsUser implements \Zend\Authentication\Adapter\AdapterInterface
{

    /**
     * @var User
     */
    private $userToLoginAs;

    /**
     * @var AdminUser
     */
    private $adminUser;

    public function __construct(User $userToLoginAs, AdminUser $adminUser)
    {

        $this->userToLoginAs = $userToLoginAs;
        $this->adminUser = $adminUser;
    }

    /**
     * Performs an authentication attempt
     *
     * @return \Zend\Authentication\Result
     * @throws \Zend\Authentication\Adapter\Exception\ExceptionInterface If authentication cannot be performed
     */
    public function authenticate()
    {
        return new \Zend\Authentication\Result(
            Result::SUCCESS, $this->user, [
                'You have assumed control of user.',
            ]
        );
    }
}

The above class will allow you to login as another user when used with Zend's AuthenticationService class.

You will need some way of using Zend's AuthenticationService class and I would recommend using an AuthManager that wraps around the AuthenticationService.

/**
 * The AuthManager service is responsible for user's login/logout and simple access
 * filtering. The access filtering feature checks whether the current visitor
 * is allowed to see the given page or not.
 */
class AuthManager
{
    /**
     * Authentication service.
     * @var \Zend\Authentication\AuthenticationService
     */
    private $authService;

    /**
     * Session manager.
     * @var Zend\Session\SessionManager
     */
    private $sessionManager;

    /**
     * Contents of the 'access_filter' config key.
     * @var array
     */
    private $config;

    /**
     * Constructs the service.
     */
    public function __construct($authService, $sessionManager, $config)
    {
        $this->authService = $authService;
        $this->sessionManager = $sessionManager;
        $this->config = $config;
    }

    /**
     * Performs a login attempt. If $rememberMe argument is true, it forces the session
     * to last for one month (otherwise the session expires on one hour).
     */
    public function login($email, $password, $rememberMe)
    {
        // Check if user has already logged in. If so, do not allow to log in
        // twice.
        if ($this->authService->getIdentity()!=null) {
            throw new \Exception('Already logged in');
        }

        // Authenticate with login/password.
        $authAdapter = $this->authService->getAdapter();
        $authAdapter->setEmail($email);
        $authAdapter->setPassword($password);
        $result = $this->authService->authenticate();

        // If user wants to "remember him", we will make session to expire in
        // one month. By default session expires in 1 hour (as specified in our
        // config/global.php file).
        if ($result->getCode()==Result::SUCCESS && $rememberMe) {
            // Session cookie will expire in 1 month (30 days).
            $this->sessionManager->rememberMe(60*60*24*30);
        }

        return $result;
    }

    public function loginAsUser($user)
    {
        // Check if user has already logged in. If so, do not allow to log in
        // twice.
        if ($this->authService->getIdentity() !== null) {
            throw new \Exception('Not logged in.');
        }

        // First need to logout of current user
        $this->authService->clearIdentity();

        $authAdapter = $this->authService->setAdapter(new AdminUserLoginAsUser($user, $this->authService->getIdentity()));
        return $this->authService->authenticate();

    }

    /**
     * Performs user logout.
     */
    public function logout()
    {
        // Allow to log out only when user is logged in.
        if ($this->authService->getIdentity()==null) {
            throw new \Exception('The user is not logged in');
        }

        // Remove identity from session.
        $this->authService->clearIdentity();
    }

}

To see how to plug it all together I would recommend looking at the following resources:

The resources are for zf3 but I think the authenticating of users and managing authentication is very similar to zf2.

Upvotes: 1

Related Questions