diff --git a/doc/providers/security.rst b/doc/providers/security.rst index 610b4ccab..69e3a3f30 100644 --- a/doc/providers/security.rst +++ b/doc/providers/security.rst @@ -10,6 +10,8 @@ Parameters * **security.hide_user_not_found** (optional): Defines whether to hide user not found exception or not. Defaults to ``true``. +* **security.encoder.bcrypt.cost** (optional): Defines BCrypt password encoder cost. Defaults to 13. + Services -------- @@ -36,9 +38,15 @@ Services Request object. * **security.encoder_factory**: Defines the encoding strategies for user - passwords (default to use a digest algorithm for all users). + passwords (uses ``security.default_encoder``). + +* **security.default_encoder**: The encoder to use by default for all users (BCrypt). + +* **security.encoder.digest**: Digest password encoder. + +* **security.encoder.bcrypt**: BCrypt password encoder. -* **security.encoder.digest**: The encoder to use by default for all users. +* **security.encoder.pbkdf2**: Pbkdf2 password encoder. * **user**: Returns the current user @@ -552,20 +560,36 @@ sample users:: Defining a custom Encoder ~~~~~~~~~~~~~~~~~~~~~~~~~ -By default, Silex uses the ``sha512`` algorithm to encode passwords. -Additionally, the password is encoded multiple times and converted to base64. -You can change these defaults by overriding the ``security.encoder.digest`` -service:: +By default, Silex uses the ``BCrypt`` algorithm to encode passwords. +Additionally, the password is encoded multiple times. +You can change these defaults by overriding ``security.default_encoder`` +service to return one of the predefined encoders: - use Symfony\Component\Security\Core\Encoder\MessageDigestPasswordEncoder; +* **security.encoder.digest**: Digest password encoder. + +* **security.encoder.bcrypt**: BCrypt password encoder. + +* **security.encoder.pbkdf2**: Pbkdf2 password encoder. + +.. code-block:: php - $app['security.encoder.digest'] = function ($app) { - // use the sha1 algorithm - // don't base64 encode the password - // use only 1 iteration - return new MessageDigestPasswordEncoder('sha1', false, 1); + $app['security.default_encoder'] = function ($app) { + return $app['security.encoder.pbkdf2']; }; +Or you can define you own, fully customizable encoder:: + + use Symfony\Component\Security\Core\Encoder\PlaintextPasswordEncoder; + + $app['security.default_encoder'] = function ($app) { + // Plain text (e.g. for debugging) + return new PlaintextPasswordEncoder(); + }; + +.. tip:: + + You can change the default BCrypt encoding cost by overriding ``security.encoder.bcrypt.cost`` + Defining a custom Authentication Provider ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/src/Silex/Provider/SecurityServiceProvider.php b/src/Silex/Provider/SecurityServiceProvider.php index 19180e49a..57e93f6ae 100644 --- a/src/Silex/Provider/SecurityServiceProvider.php +++ b/src/Silex/Provider/SecurityServiceProvider.php @@ -26,6 +26,8 @@ use Symfony\Component\Security\Core\User\InMemoryUserProvider; use Symfony\Component\Security\Core\Encoder\EncoderFactory; use Symfony\Component\Security\Core\Encoder\MessageDigestPasswordEncoder; +use Symfony\Component\Security\Core\Encoder\BCryptPasswordEncoder; +use Symfony\Component\Security\Core\Encoder\Pbkdf2PasswordEncoder; use Symfony\Component\Security\Core\Authentication\Provider\DaoAuthenticationProvider; use Symfony\Component\Security\Core\Authentication\Provider\AnonymousAuthenticationProvider; use Symfony\Component\Security\Core\Authentication\AuthenticationProviderManager; @@ -78,6 +80,7 @@ public function register(Container $app) $app['security.role_hierarchy'] = array(); $app['security.access_rules'] = array(); $app['security.hide_user_not_found'] = true; + $app['security.encoder.bcrypt.cost'] = 13; $app['security.authorization_checker'] = function ($app) { return new AuthorizationChecker($app['security.token_storage'], $app['security.authentication_manager'], $app['security.access_manager']); @@ -109,14 +112,27 @@ public function register(Container $app) // by default, all users use the digest encoder $app['security.encoder_factory'] = function ($app) { return new EncoderFactory(array( - 'Symfony\Component\Security\Core\User\UserInterface' => $app['security.encoder.digest'], + 'Symfony\Component\Security\Core\User\UserInterface' => $app['security.default_encoder'], )); }; + // by default, all users use the BCrypt encoder + $app['security.default_encoder'] = function ($app) { + return $app['security.encoder.bcrypt']; + }; + $app['security.encoder.digest'] = function ($app) { return new MessageDigestPasswordEncoder(); }; + $app['security.encoder.bcrypt'] = function ($app) { + return new BCryptPasswordEncoder($app['security.encoder.bcrypt.cost']); + }; + + $app['security.encoder.pbkdf2'] = function ($app) { + return new Pbkdf2PasswordEncoder(); + }; + $app['security.user_checker'] = function ($app) { return new UserChecker(); }; diff --git a/tests/Silex/Tests/Application/SecurityTraitTest.php b/tests/Silex/Tests/Application/SecurityTraitTest.php index 340f2d347..e91eda730 100644 --- a/tests/Silex/Tests/Application/SecurityTraitTest.php +++ b/tests/Silex/Tests/Application/SecurityTraitTest.php @@ -25,11 +25,16 @@ class SecurityTraitTest extends \PHPUnit_Framework_TestCase public function testEncodePassword() { $app = $this->createApplication(array( - 'fabien' => array('ROLE_ADMIN', '5FZ2Z8QIkA7UTZ4BYkoC+GsReLf569mSKDsfods6LYQ8t+a8EW9oaircfMpmaLbPBh4FOBiiFyLfuZmTSUwzZg=='), + 'fabien' => array('ROLE_ADMIN', '$2y$15$lzUNsTegNXvZW3qtfucV0erYBcEqWVeyOmjolB7R1uodsAVJ95vvu'), )); $user = new User('foo', 'bar'); - $this->assertEquals('5FZ2Z8QIkA7UTZ4BYkoC+GsReLf569mSKDsfods6LYQ8t+a8EW9oaircfMpmaLbPBh4FOBiiFyLfuZmTSUwzZg==', $app->encodePassword($user, 'foo')); + $password = 'foo'; + $encoded = $app->encodePassword($user, $password); + + $this->assertTrue( + $app['security.encoder_factory']->getEncoder($user)->isPasswordValid($encoded, $password, $user->getSalt()) + ); } /** @@ -48,8 +53,8 @@ public function testIsGranted() $request = Request::create('/'); $app = $this->createApplication(array( - 'fabien' => array('ROLE_ADMIN', '5FZ2Z8QIkA7UTZ4BYkoC+GsReLf569mSKDsfods6LYQ8t+a8EW9oaircfMpmaLbPBh4FOBiiFyLfuZmTSUwzZg=='), - 'monique' => array('ROLE_USER', '5FZ2Z8QIkA7UTZ4BYkoC+GsReLf569mSKDsfods6LYQ8t+a8EW9oaircfMpmaLbPBh4FOBiiFyLfuZmTSUwzZg=='), + 'fabien' => array('ROLE_ADMIN', '$2y$15$lzUNsTegNXvZW3qtfucV0erYBcEqWVeyOmjolB7R1uodsAVJ95vvu'), + 'monique' => array('ROLE_USER', '$2y$15$lzUNsTegNXvZW3qtfucV0erYBcEqWVeyOmjolB7R1uodsAVJ95vvu'), )); $app->get('/', function () { return 'foo'; }); diff --git a/tests/Silex/Tests/Provider/RememberMeServiceProviderTest.php b/tests/Silex/Tests/Provider/RememberMeServiceProviderTest.php index 04223770d..b027497cd 100644 --- a/tests/Silex/Tests/Provider/RememberMeServiceProviderTest.php +++ b/tests/Silex/Tests/Provider/RememberMeServiceProviderTest.php @@ -77,7 +77,7 @@ public function createApplication($authenticationMethod = 'form') 'remember_me' => array(), 'logout' => true, 'users' => array( - 'fabien' => array('ROLE_USER', '5FZ2Z8QIkA7UTZ4BYkoC+GsReLf569mSKDsfods6LYQ8t+a8EW9oaircfMpmaLbPBh4FOBiiFyLfuZmTSUwzZg=='), + 'fabien' => array('ROLE_USER', '$2y$15$lzUNsTegNXvZW3qtfucV0erYBcEqWVeyOmjolB7R1uodsAVJ95vvu'), ), ), ); diff --git a/tests/Silex/Tests/Provider/SecurityServiceProviderTest.php b/tests/Silex/Tests/Provider/SecurityServiceProviderTest.php index fee637dc9..f50ec986a 100644 --- a/tests/Silex/Tests/Provider/SecurityServiceProviderTest.php +++ b/tests/Silex/Tests/Provider/SecurityServiceProviderTest.php @@ -187,7 +187,7 @@ public function testUser() 'default' => array( 'http' => true, 'users' => array( - 'fabien' => array('ROLE_ADMIN', '5FZ2Z8QIkA7UTZ4BYkoC+GsReLf569mSKDsfods6LYQ8t+a8EW9oaircfMpmaLbPBh4FOBiiFyLfuZmTSUwzZg=='), + 'fabien' => array('ROLE_ADMIN', '$2y$15$lzUNsTegNXvZW3qtfucV0erYBcEqWVeyOmjolB7R1uodsAVJ95vvu'), ), ), ), @@ -271,8 +271,8 @@ private function addFormAuthentication($app) 'logout' => true, 'users' => array( // password is foo - 'fabien' => array('ROLE_USER', '5FZ2Z8QIkA7UTZ4BYkoC+GsReLf569mSKDsfods6LYQ8t+a8EW9oaircfMpmaLbPBh4FOBiiFyLfuZmTSUwzZg=='), - 'admin' => array('ROLE_ADMIN', '5FZ2Z8QIkA7UTZ4BYkoC+GsReLf569mSKDsfods6LYQ8t+a8EW9oaircfMpmaLbPBh4FOBiiFyLfuZmTSUwzZg=='), + 'fabien' => array('ROLE_USER', '$2y$15$lzUNsTegNXvZW3qtfucV0erYBcEqWVeyOmjolB7R1uodsAVJ95vvu'), + 'admin' => array('ROLE_ADMIN', '$2y$15$lzUNsTegNXvZW3qtfucV0erYBcEqWVeyOmjolB7R1uodsAVJ95vvu'), ), ), ), @@ -322,8 +322,8 @@ private function addHttpAuthentication($app) 'http' => true, 'users' => array( // password is foo - 'dennis' => array('ROLE_USER', '5FZ2Z8QIkA7UTZ4BYkoC+GsReLf569mSKDsfods6LYQ8t+a8EW9oaircfMpmaLbPBh4FOBiiFyLfuZmTSUwzZg=='), - 'admin' => array('ROLE_ADMIN', '5FZ2Z8QIkA7UTZ4BYkoC+GsReLf569mSKDsfods6LYQ8t+a8EW9oaircfMpmaLbPBh4FOBiiFyLfuZmTSUwzZg=='), + 'dennis' => array('ROLE_USER', '$2y$15$lzUNsTegNXvZW3qtfucV0erYBcEqWVeyOmjolB7R1uodsAVJ95vvu'), + 'admin' => array('ROLE_ADMIN', '$2y$15$lzUNsTegNXvZW3qtfucV0erYBcEqWVeyOmjolB7R1uodsAVJ95vvu'), ), ), ), diff --git a/tests/Silex/Tests/Route/SecurityTraitTest.php b/tests/Silex/Tests/Route/SecurityTraitTest.php index bf85f5911..352a77bd6 100644 --- a/tests/Silex/Tests/Route/SecurityTraitTest.php +++ b/tests/Silex/Tests/Route/SecurityTraitTest.php @@ -74,7 +74,7 @@ private function createApplication() 'default' => array( 'http' => true, 'users' => array( - 'fabien' => array('ROLE_ADMIN', '5FZ2Z8QIkA7UTZ4BYkoC+GsReLf569mSKDsfods6LYQ8t+a8EW9oaircfMpmaLbPBh4FOBiiFyLfuZmTSUwzZg=='), + 'fabien' => array('ROLE_ADMIN', '$2y$15$lzUNsTegNXvZW3qtfucV0erYBcEqWVeyOmjolB7R1uodsAVJ95vvu'), ), ), ),