Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Symfony2 FOSUserBundle – Validate against "user active" flag on login

I have a flag on my users for 'active' and if set to zero or null, I will not allow login.

I have tried a couple of approaches and come up short.

If I do the logout route the flash message is not preserved, so the user sees nothing.

I looked into adding a validation on the login form so that it would throw a normal form error if the flag was not set to true, but in that folder (vendor/Bundles/FOS/UserBundle/Form/Type) I find nothing for login form, only registration and such, so I wouldn't know where to put it or where to inherit from in order to override.

I also tried as suggested here to manually log out, but that left me with a white screen of death...

Any suggestions how to easily accomplish this?

************** UPDATE ************

I realized that I probably want to go about it adding a validator on the login form. I currently have it coded into the controller of the first route a user gets sent to, but that won't provide much security if a user types a route before logging in, because on a successful login attempt, my default "landing page" after login will not be the route that the user is taken to, but he will be landing on the route of his choice...

***UPDATE AGAIN ****

So the service config file has this...

    <service id="security.user_checker" class="%security.user_checker.class%" public="false" />

And that parameter is defined here...

    <parameter key="security.user_checker.class">Symfony\Component\Security\Core\User\UserChecker</parameter>

So in order to modify the login logics I need to override

Symfony\Component\Security\Core\User\UserChecker

Now I have done that by overriding that parameter above in my own parameters.ini in the symfony app/config like this

security.user_checker.class  = BizTV\UserBundle\Controller\UserChecker

.. and added this check to my userChecker overrider...

    //Test for companylock...
    if ( !$user->getCompany()->getActive() ) {
        throw new LockedException('The company of this user is locked.', $user);
    }

Here's the entire file:

<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <[email protected]>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

//Override by Mattias

namespace BizTV\UserBundle\Controller;
//namespace Symfony\Component\Security\Core\User;

use Symfony\Component\Security\Core\Exception\CredentialsExpiredException;
use Symfony\Component\Security\Core\Exception\LockedException;
use Symfony\Component\Security\Core\Exception\DisabledException;
use Symfony\Component\Security\Core\Exception\AccountExpiredException;

use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\User\UserChecker as OriginalUserChecker;

/**
 * UserChecker checks the user account flags.
 *
 * @author Fabien Potencier <[email protected]>
 */
class UserChecker extends OriginalUserChecker
{
    /**
     * {@inheritdoc}
     */
    public function checkPreAuth(UserInterface $user)
    {

    //Test for companylock...
    if ( !$user->getCompany()->getActive() ) {
        throw new LockedException('The company of this user is locked.', $user);
    }

        if (!$user instanceof AdvancedUserInterface) {
            return;
        }

        if (!$user->isCredentialsNonExpired()) {
            throw new CredentialsExpiredException('User credentials have expired.', $user);
        }


    }

    /**
     * {@inheritdoc}
     */
    public function checkPostAuth(UserInterface $user)
    {

    //Test for companylock...
    if ( !$user->getCompany()->getActive() ) {
        throw new LockedException('The company of this user is locked.', $user);
    }  

        if (!$user instanceof AdvancedUserInterface) {
            return;
        }



        if (!$user->isAccountNonLocked()) {
            throw new LockedException('User account is locked.', $user);
        }

        if (!$user->isEnabled()) {
            throw new DisabledException('User account is disabled.', $user);
        }

        if (!$user->isAccountNonExpired()) {
            throw new AccountExpiredException('User account has expired.', $user);
        }
    }
}

* Update nb 3 ******** Now I only have left to make it actually check for the standard user lock which surprisingly it doesn't do out of the box. (Thanks nifr for getting me this far!)

My user entity starts off like this, and like Nifr said, I need to implement the AdvancedUserInterface, but this is probably not the way to do it since it still doesn't check for this lock... but it throws me no error message either (if I change them up and put implememts AdvancedUserInterface and then EXTENDs baseUser it throws an error so...)

<?php
// src/BizTV/UserBundle/Entity/User.php

namespace BizTV\UserBundle\Entity;

use BizTV\UserBundle\Validator\Constraints as BizTVAssert;
use Symfony\Component\Security\Core\User\AdvancedUserInterface;

use FOS\UserBundle\Entity\User as BaseUser;
use Doctrine\ORM\Mapping as ORM;

use BizTV\BackendBundle\Entity\company as company;

/**
 * @ORM\Entity
 * @ORM\Table(name="fos_user")
 */
class User extends BaseUser implements AdvancedUserInterface
{

Not sure if that's how you do it when you both extend base user and try and implement AdvancedUserInterface, when done as above I still can't use the features it's supposed to add (but it throws me no error message either), but If I switch places of the EXTENDS and IMPLEMENTS like this (line 18)...

class User implements AdvancedUserInterface extends BaseUser 

...I get this error:

Parse error: syntax error, unexpected T_EXTENDS, expecting '{' in /var/www/cloudsign/src/BizTV/UserBundle/Entity/User.php on line 18
like image 639
Matt Welander Avatar asked Aug 29 '13 22:08

Matt Welander


1 Answers

FOSUserBundle / Symfony already has some kind of "active" flag integrated.

FOS\UserBundle\Model\User already provides the properties "locked" and "enabled" which are intended basically for this purpose. The difference between those two properties is the following ( quoting @stof's comment here)

From the Security component point of view, there is no real difference: both are forbidden to log in. The difference is a semantic one: disabled users are generally users that need to activate their account (for instance, when you activate the need to confirm the email in FOSUserBundle, the user is disabled on creation and enabled on confirmation). On the other hand, locking a user is generally an action done by the admin of the site to ban a user. Using the same field in the database does not make sense as it would allow banned user to have access again by simply going through the confirmation process.

The check for locked/disabled users is being performed by a UserChecker ( symfony provides this one as @security.user_checker ) in FOSUserBundle's AuthenticationListener which implements Symfony\Component\Security\Core\User\UserCheckerInterface.

Now in order to redirect inactive user's to a different route you would:

  1. Catch the Symfony\Component\Security\Core\Exception\DisabledException in the try/catch block in an extended AuthenticationListener
  2. Redirect the user to a certain route if the caught exception is of type InactiveUserException

Optionally move the redirect to a newly created EventListener/-Subscriber which is being dispatched in the extended AuthenticationListener. This way you could later create additional Listeners i.e. for logging purposes and just subscribe them to the inactive-user login-attempt event.

like image 54
Nicolai Fröhlich Avatar answered Sep 28 '22 03:09

Nicolai Fröhlich