silexphp / Silex

[DEPRECATED -- Use Symfony instead] The PHP micro-framework based on the Symfony Components
https://silex.symfony.com
MIT License
3.58k stars 718 forks source link

Login form works with default user entity class but not with a custom entity class #718

Closed ke20 closed 11 years ago

ke20 commented 11 years ago

Hi,

I suppose have find a bug because i've posted my problem on three differents forum and i've no answers since about four days...

I developped a login form with the security provider like explain in the documentation but when I want to use a custom user entity class, I'm always redirected on my login form. This bug doesn't occur when I use the default user entity class provides by Symfony.

My custom user entity class is as below:

    <?php

    namespace App\Models;

    use \Symfony\Component\Security\Core\User\AdvancedUserInterface;

    final class User implements AdvancedUserInterface, \Serializable 
    {
        private $id;
        private $username;
        private $password;
        private $salt;
        private $email;
        private $roles;
        private $enabled;

        private $accountNonExpired;
        private $credentialsNonExpired;
        private $accountNonLocked;

        public function __construct($id, $username, $password, array $roles = array(), $enabled = true, $userNonExpired = true, $credentialsNonExpired = true, $userNonLocked = true)
        {
            if (empty($username)) {
                throw new \InvalidArgumentException('The username cannot be empty.');
            }

            $this->id = $id;
            $this->username = $username;
            $this->password = $password;
            $this->enabled = $enabled;
            $this->accountNonExpired = $userNonExpired;
            $this->credentialsNonExpired = $credentialsNonExpired;
            $this->accountNonLocked = $userNonLocked;
            $this->roles = $roles;
        }

        public function getId() {
            return $this->id;
        }

        public function setId($id) {
            $this->id = $id;
        }

        // [...]
        // others getters and setters
        // [...]

        public function eraseCredentials() {}

        public function serialize() {
            return serialize(array(
                $this->id,
            ));
        }

        public function unserialize($serialized) {
            list (
                $this->id,
            ) = unserialize($serialized);
        }
    }

The user provider using my custom entity class and which always redirects me on my login form is the following :

    <?php

    /*
     *
     * With my "App\Models\User" entity class, there is nothing going on...
     *
     */

    namespace App\Models;

    use \Symfony\Component\Security\Core\User\UserProviderInterface;
    use \Symfony\Component\Security\Core\User\UserInterface;
    use \Doctrine\DBAL\Connection;

    use \Symfony\Component\Security\Core\Exception\UnsupportedUserException;
    use \Symfony\Component\Security\Core\Exception\UsernameNotFoundException;

    use App\Models\User; // <-------- HERE

    class UserProvider implements UserProviderInterface 
    {
        private $conn;

        public function __construct(Connection $conn) {
            $this->conn = $conn;
        }

        public function loadUserByUsername($username) {
            $stmt = $this->conn->executeQuery('SELECT * FROM users WHERE username = ?', array(strtolower($username)));

            if (!$user = $stmt->fetch()) {
                throw new UsernameNotFoundException(sprintf('Username "%s" does not exist.', $username));
            }

            // -------- HERE
            return new User($user['id'], $user['username'], $user['password'], explode(',', $user['roles']), true, true, true, true);
        }

        public function refreshUser(UserInterface $user) {
            if (!$user instanceof User) {
                throw new UnsupportedUserException(sprintf('Instances of "%s" are not supported.', get_class($user)));
            }

            return $this->loadUserByUsername($user->getUsername());
        }

        public function supportsClass($class) {
            return $class === 'App\Models\User'; // <-------- HERE
        }
    }

On the other side, when in my user prodiver I use the default user entity class (like below) the login form works fine :

    <?php

    /*
     *
     * With the default entity "Symfony\Component\Security\Core\User\User",  the login form is sent successfully
     *
     */

    namespace App\Models;

    use \Symfony\Component\Security\Core\User\UserProviderInterface;
    use \Symfony\Component\Security\Core\User\UserInterface;
    use \Doctrine\DBAL\Connection;

    use \Symfony\Component\Security\Core\Exception\UnsupportedUserException;
    use \Symfony\Component\Security\Core\Exception\UsernameNotFoundException;

    use Symfony\Component\Security\Core\User\User; // <-------- HERE

    class UserProvider implements UserProviderInterface 
    {
        private $conn;

        public function __construct(Connection $conn) {
            $this->conn = $conn;
        }

        public function loadUserByUsername($username) {
            $stmt = $this->conn->executeQuery('SELECT * FROM users WHERE username = ?', array(strtolower($username)));

            if (!$user = $stmt->fetch()) {
                throw new UsernameNotFoundException(sprintf('Username "%s" does not exist.', $username));
            }

            // --------- HERE
            return new User($user['username'], $user['password'], explode(',', $user['roles']), true, true, true, true);
        }

        public function refreshUser(UserInterface $user) {
            if (!$user instanceof User) {
                throw new UnsupportedUserException(sprintf('Instances of "%s" are not supported.', get_class($user)));
            }

            return $this->loadUserByUsername($user->getUsername());
        }

        public function supportsClass($class) {
            return $class === 'Symfony\Component\Security\Core\User\User'; // <--------- HERE
        }
    }

The security part in my configuration file is:

    <?php

    // [Others configurations]

    // Manage Security and differents access
    $app->register(new Silex\Provider\SecurityServiceProvider(), array(
        'security.firewalls' => array( 
            'admin' => array(
                'pattern' => '^/admin',
                'form' => array(
                    'login_path' => '/user/login',
                    'check_path' => '/admin/login_check'
                ),
                'logout' => array('logout_path' => '/admin/logout'),
                'users' => $app->share(function () use ($app) {
                    return new App\Models\UserProvider($app['db']);
                }),
            )
        )
    ));

    return $app

I don't know if it's really a bug or if it's my code, but someone can help me please?

Thanks in advance and sorry for my english I'm french....

ke20 commented 11 years ago

I've removed the Serializable class implementation for the User class and it worked