1

I've followed the guide for implementing authentication/authorization and I can login. I have one main difference though from what's in the guide. Instead of an isActive property I have a status table in my database.

I'm at a loss as to how I would deny/accept logins based on the values in the status table rather than the isActive property referenced in the guide.

I'm not sure what code to post because it works as it does in the guide and I'm pretty sure the Symfony security system handles all the authentication stuff where I can't see it.

Even if you just point me in the right direction I would be grateful.

Edit:

Using ChadSikorra's advice I came up with this code to implement the AdvancedUserInterface functions:

public function isAccountNonExpired()
    {
        $status = $this->getTblStatus()->getStatustext();

        switch ($status){
            case "expired":
                return false;
            default:
                return true;
        }
    }

    public function isAccountNonLocked()
    {
        $status = $this->getTblStatus()->getStatustext();

        switch ($status){
            case "locked":
                return false;
            case "suspended":
                return false;
            case "registered":
                return false;
            default:
                return true;
        }
    }

    public function isCredentialsNonExpired()
    {
        return $this->pwdexpired;
    }

    public function isEnabled()
    {
        $status = $this->getTblStatus()->getStatustext();

        if($status != 'active')
            return false
        else
            return true;
    }

The next question I have then is how do I handle the exceptions that are thrown when a user has one of the statuses?

Based on what I have so far I think this is doable by catching the errors in the loginAction. What I don't know how to do is identify the errors, but I'll keep digging.

/**
     * @Route("/Login", name="wx_exchange_login")
     * @Template("WXExchangeBundle:User:login.html.twig")
     * User login - Open to public
     * Authenticates users to the system
     */
    public function loginAction(Request $request)
    {
        $session = $request->getSession();

        if ($this->get('security.context')->isGranted('IS_AUTHENTICATED_REMEMBERED'))
        {
            // redirect authenticated users to homepage 
            return $this->redirect($this->generateUrl('wx_exchange_default_index')); 
        }

        // get the login error if there is one
        if ($request->attributes->has(SecurityContext::AUTHENTICATION_ERROR)) {
            $error = $request->attributes->get(
                SecurityContext::AUTHENTICATION_ERROR
            );
        } else {
            $error = $session->get(SecurityContext::AUTHENTICATION_ERROR);
            $session->remove(SecurityContext::AUTHENTICATION_ERROR);
        }

        if($error instanceof LockedException)
    {
    }

        return $this->render(
            'WXExchangeBundle:User:login.html.twig',
            array(
                // last username entered by the user
                'last_username' => $session->get(SecurityContext::LAST_USERNAME),
                'error'         => $error,
            )
        );
    }

I am now able to check for the type of Exception, but I'm at a loss as to how to get the specific status so that I can redirect to the correct place. This is the last piece of the puzzle.

John the Ripper
  • 2,389
  • 4
  • 35
  • 61

1 Answers1

3

You could add mapping to your custom status table on the user entity, like so:

  /**
   * @ORM\OneToOne(targetEntity="AccountStatus")
   * @ORM\JoinColumn(name="status_id", referencedColumnName="id", nullable=true)
   */
  private $accountStatus;

This would also require creating an entity describing the custom status table. Then you could use this mapping in your user entity by implementing Symfony\Component\Security\Core\User\AdvancedUserInterface as referenced in the guide you linked. Then implement the isEnabled function something like this...

public function isEnabled()
{
    return $this->getAccountStatus()->getIsActive(); /* Or whatever you named it */
}

EDIT:

Based on the API Doc for AdvancedUserInterface, if you want to do custom logic for handling the different statuses you'll need to register an exception listener...

If you need to perform custom logic for any of these situations, then you will need to register an exception listener and watch for the specific exception instances thrown in each case. All exceptions are a subclass of AccountStatusException

There's a pretty good Cookbook article for creating something like this here. The basic process in this instance would be to create the class for the listener...

src/Acme/DemoBundle/EventListener/AcmeExceptionListener.php

namespace Acme\DemoBundle\EventListener;

use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Security\Core\Exception\DisabledException;
use Symfony\Component\Security\Core\Exception\LockedException;

class AcmeExceptionListener
{
    public function onKernelException(GetResponseForExceptionEvent $event)
    {
        $exception = $event->getException();

        if ($exception instanceof DisabledException) {
            // Customize your response object to display the exception details
            $response = new Response();
            $response->setContent('<html><body><h1>Custom disabled page!</h1></body></html>');

            // Send the modified response object to the event
            $event->setResponse($response);
        }
        elseif ($exception instanceof LockedException) {
            // Or render a custom template as a subrequest instead...
            $kernel = $event->getKernel();
            $response  = $kernel->forward('AcmeDemoBundle:AccountStatus:locked', array(
                'exception' => $exception,
            ));

            $event->setResponse($response);
        }
        // ... and so on
    }
}

The above are just basic examples, but it gives you the gist anyway. Technically I guess you could also make custom exceptions by extending AccountStatusException and then throw them in your logic for your AdvancedUserInterface implementation. Then you would know exactly which status you are catching. Anyway, then make sure to register the listener as a service.

app/config/config.yml

services:
    kernel.listener.your_listener_name:
        class: Acme\DemoBundle\EventListener\AcmeExceptionListener
        tags:
            - { name: kernel.event_listener, event: kernel.exception, method: onKernelException }

Another way to go about this would be to implement some sort of a custom User Checker. See this question: Symfony2 custom user checker based on accepted eula

Community
  • 1
  • 1
ChadSikorra
  • 2,829
  • 2
  • 21
  • 27
  • I think this is almost what I want, however in my status table I have statuses such as active, unconfirmed, banned, etc. I want deny login to the user and redirect them to a different page based on the status. Example if the user is not confirmed after registration a page with that message plus a button to resend the registration email. Banned user would get the banned message, etc. – John the Ripper Apr 25 '14 at 10:58
  • BTW I do have the mapping done alredy. I just don't have the isEnabled function done yet. – John the Ripper Apr 25 '14 at 10:59
  • I added some additional examples and information that should help you in doing some custom actions based on the account status. – ChadSikorra Apr 30 '14 at 00:42