Thomas K
Thomas K

Reputation: 1117

Silex framework : Create an UserProvider/PasswordEncoder/User

I use the framework Silex, especially SecurityServiceProvider.

I have to create my own User class (because my salt is the username => with the default class the salt is null) :

<?php
namespace Adh\Security;

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

class User implements AdvancedUserInterface {

  private $username;
  private $password;

  public function __construct($username, $password)
  {
    $this->username = $username;
    $this->password = $password;
  }

  public function getRoles()
  {
    return array();
  }

  public function getPassword()
  {
    return $this->password;
  }

  public function getSalt()
  {
    return $this->username;
  }
...
}

Until this, no problem. Now, I have to create a custom UserProvider to retrieve my user from MySQL :

<?php
namespace Adh\Security;

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

class UserProvider implements UserProviderInterface
{
  private $conn;

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

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

    if (!$user = $stmt->fetch()) {
      throw new UsernameNotFoundException(sprintf('Le nom d\'utilisateur "%s" n\'existe pas', $username));
    }

    return new \Adh\Security\User($user['username'], $user['sha_pass_hash']);
  }
  ...
}

And to register the security provider :

$app->register(new Silex\Provider\SecurityServiceProvider(), array(
  'security.firewalls' => array(
    'user' => array(
      'pattern' => '^/user',
      'form' => array('login_path' => '/connexion', 'check_path' => '/user'),
      'users' => $app->share(function () use ($app) {
        return new Adh\Security\UserProvider($app['db']);
      })
    )
  )
));

$app['security.encoder_factory'] = $app->share(function ($app) {
  return new EncoderFactory(
    array('Adh\Security\User' => new Adh\Security\PasswordEncoder())
    );
});

It works, except when the authentification is positive (the username and password match) I've this exception :

RuntimeException: There is no user provider for user "Adh\Security\User".

How to set my UserProvider for my User class ?

Thank's

Upvotes: 2

Views: 4364

Answers (2)

Thomas K
Thomas K

Reputation: 1117

I found the solution. To create my provider I followed this example : http://silex.sensiolabs.org/doc/providers/security.html#defining-a-custom-user-provider

In the refreshUser method:

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

This is correct for the default User class: I have my own User class so the exception is raised.

The condition becomes :

if (!$user instanceof \Adh\Security\User) {
   throw new UnsupportedUserException(sprintf('Instances of "%s" are not supported.', get_class($user)));
}

Upvotes: 3

Ralf Hertsch
Ralf Hertsch

Reputation: 1287

Your function

loadUserByUsername()

does not return any role. By default a Symfony\Component\Security\Core\User\User record is returned with the roles of the user as third parameter. At least any user must have one role.

Sample:

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

public function loadUserByUsername($username)
{
    $frameworkUser = new FrameworkUser($this->app);
    if (false === ($user = $frameworkUser->selectUser($username))) {
      throw new UsernameNotFoundException(sprintf('Username "%s" does not exist.', $username));
    }
    return new User($user['username'], $user['password'], $user['roles'], true, true, true, true);
}

Upvotes: 1

Related Questions