A little FOSUserBundle service to spoof a user identity (English)

2013-08-20

  symfony    english 

I have written a little service to temporarily spoof a user identity when using FOSUserBundle in your Symfony projects.

Thus, you can temporarily appears as another user identity and when you decide, go back to your original identity.

The service

namespace Eko\\MyBundle\\Identity;

use Symfony\\Component\\Security\\Core\\Authentication\\Token\\UsernamePasswordToken;
use Symfony\\Component\\Security\\Core\\SecurityContextInterface;
use Symfony\\Component\\HttpFoundation\\Session\\SessionInterface;

use FOS\\UserBundle\\Model\\UserInterface;

/**
 * Class Spoofer
 *
 * This service is used to use the identity of another user
 */
class Spoofer
{
    /**
     * @var SessionInterface
     */
    protected $session;

    /**
     * @var SecurityContextInterface
     */
    protected $securityContext;

    /**
     * Constructor
     *
     * @param SessionInterface         $session         Symfony session service
     * @param SecurityContextInterface $securityContext Symfony security context service
     */
    public function __construct(SessionInterface $session, SecurityContextInterface $securityContext)
    {
        $this->session         = $session;
        $this->securityContext = $securityContext;
    }

    /**
     * Spoof the identity of a given user by setting the user in the security context and storing the
     * current administrator user in the session
     *
     * @param UserInterface $user
     */
    public function spoof(UserInterface $user)
    {
        $spoofer = $this->securityContext->getToken()->getUser();
        $this->session->set('spoofer', $spoofer);

        $this->setToken($user);
    }

    /**
     * Stops spoofing an identity and rewinds original administrator identity
     *
     * @throws \\Exception
     */
    public function stop()
    {
        if (!$this->isSpoofing()) {
            throw new \\Exception('There is currently no spoofing');
        }

        $spoofer = $this->session->get('spoofer');
        $this->setToken($spoofer);

        $this->session->set('spoofer', null);
    }

    /**
     * Returns if current user is spoofing an identity
     *
     * @return bool
     */
    public function isSpoofing()
    {
        return $this->session->get('spoofer') ? true : false;
    }

    /**
     * Sets a new token to Symfony security context
     *
     * @param UserInterface $user
     */
    protected function setToken(UserInterface $user)
    {
        $token = new UsernamePasswordToken($user, $user->getPassword(), 'main', $user->getRoles());

        $this->securityContext->setToken($token);
    }
}

Now, let's register this service in the Symfony dependency injection container:


<?xml version="1.0" ?>

<container xmlns="http://symfony.com/schema/dic/services"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">

    <services>

        <service id="eko.mybundle.identity.spoofer" class="Eko\\MyBundle\\Identity\\Spoofer" public="true">
            <argument type="service" id="session" />
            <argument type="service" id="security.context" />
        </service>

    </services>
</container>

This service constructor is taking the Symfony session and security context.

The method spoof($user) will permit you to temporarily spoof a user identity while your original identity will be stored in session.

When you want to go back to your original identity, simply call the stop() method.

A simple use case

Here is a really simple use case with a user identity identifier 23, from a controller:


$user = $this->getUserManager()->findUserBy(array('id' => 23));
$this->get('eko.mybundle.identity.spoofer')->spoof($user);

Then, to go back to your original identity:


$this->get('eko.mybundle.identity.spoofer')->stop();

Have fun!

Comments