8

I need to modify my user object on logout. To do this, I have a security.yml that contains the following (amongst other things) -

#...
    logout:
        success_handler: my.logout_success_handler
        target: /
#...

...this defines a logout success handler, which is defined in services.yml like this -

   my.security.logout_success_handler:
       class: My\Security\LogoutSuccessHandler
       arguments: ["@security.context", "@doctrine.orm.default_entity_manager"]

...finally, the business-end of my handler is like this -

// ...
public function onLogoutSuccess(Request $request)
{

    $user = $this->securityContext->getToken()->getUser();

    // ... do stuff with the user object....
    $this->em->flush();

    // now what?

}
// ...

So, where it says "now what?" I understand that I need to return a Response object. Ideally I want that response object to redirect the user to whatever is defined in logout.target in the security.yml.

Is there an easy way I can query that? Or, even better, is there another way of doing this kind of thing that doesn't require me to get involved with the request/response objects at all?

Thanks

Jez
  • 2,384
  • 1
  • 17
  • 31

3 Answers3

8

You could define your target as a parameter in your parameters.yml or config.yml:

parameters:
    logout.target: /

And then reference this value in your security.yml:

    logout:
        success_handler: my.logout_success_handler
        target: %logout.target%   

And/or inject it into your logout handler:

    my.security.logout_success_handler:
        class: My\Security\LogoutSuccessHandler
        arguments: ["@security.context", "@doctrine.orm.default_entity_manager", %logout.target%]

And return a RedirectResponse with this value:

// Assign the 3. constructor parameter to the instance variable $logoutTarget

public function onLogoutSuccess(Request $request)
{
    // ...

    return new RedirectResponse($this->logoutTarget);
}
Leif
  • 1,076
  • 10
  • 16
8

So, I think I've figured out the right answer -

Rather than implementing LogoutSuccessHandlerInterface and configuring a logout.success_handler, my security.yml now looks like this -

# ...
logout:
    handlers: [my.bundle.security.logout_handler]
# ...

...and I'm implementing Symfony\Component\Security\Http\Logout\LogoutHandlerInterface. Confusing naming, but this seems to be the preferred way of doing post-logout operations without having to get involved with the response object. My implementation looks like this -

namespace My\Bundle\Security;

use Symfony\Component\Security\Http\Logout\LogoutHandlerInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Request;
use Doctrine\ORM\EntityManager;

/**
 * Do post logout stuff
 */
class LogoutHandler implements LogoutHandlerInterface
{

    /**
     * @var EntityManager
     */
    protected $em;

    /**
     * Constructor
     * @param EntityManager $em
     */
    public function __construct(EntityManager $em)
    {

        $this->em = $em;
    }

    /**
     * Do post logout stuff
     */
    public function logout(Request $request, Response $response, TokenInterface $authToken)
    {
        $user = $authToken->getUser();

        // do stuff with the user object...
        $this->em->flush();

        return $response;
    }
}

...as you can see, the LogoutHandlerInterface provides a pre-made $response object that I can just return when I'm finished.

Jez
  • 2,384
  • 1
  • 17
  • 31
  • The difference here is that you are always manipulating your user, even if logout isnt successful. – Henrik Bjørnskov Feb 18 '14 at 12:28
  • Actually, it looks like the `LogoutHandler`(s) are called *after* the `LogoutSuccessHandler`, which would imply that logout success is guaranteed to happen. See https://github.com/symfony/symfony/blob/f828aee7f749009a574eee0a1ad5bfd092b0d488/src/Symfony/Component/Security/Http/Firewall/LogoutListener.php#L108-L118 – Jez Feb 18 '14 at 13:44
  • Only if it is bailing if its not successful. – Henrik Bjørnskov Feb 18 '14 at 14:16
7

You could use composition and inject the default LogoutSuccessHandler into your object and call the onLogoutSucces method on it.

The following pseudu code shows the idea of doing it.

class MyLogoutSuccessHandler implements \LogoutSuccessHandler
{
    protected $original;

    public function __construct(OriginalLogoutSuccesHandler $original)
    {
        $this->original = $original;
    }

    public function onLogoutSuccess(Request $request)
    {
        // do stuf your want and delegate to the original
        return $this->original->onLogoutSuccess($request);
    }
}

This is also the way HttpKernelInterface works in StackPHP and when you use HttpCache in your application.

Hopefully this helps, happy coding :)