8000 Merge branch '4.4' into 5.0 · symfony/symfony-docs@97baf67 · GitHub
[go: up one dir, main page]

Skip to content

Commit 97baf67

Browse files
committed
Merge branch '4.4' into 5.0
* 4.4: Update parent_services.rst add versionadded directive. refs #12741 Added article on password migrations [DependencyInjection] Bind tagged services Fix documentation for PhpUnitBridge regex configuration
2 parents 27d3268 + b61e9c1 commit 97baf67

File tree

5 files changed

+226
-5
lines changed

5 files changed

+226
-5
lines changed

components/phpunit_bridge.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,7 @@ message, enclosed with ``/``. For example, with:
213213
214214
<php>
215215
<server name="KERNEL_CLASS" value="App\Kernel"/>
216-
<env name="SYMFONY_DEPRECATIONS_HELPER" value="regex=/foobar/"/>
216+
<env name="SYMFONY_DEPRECATIONS_HELPER" value="/foobar/"/>
217217
</php>
218218
</phpunit>
219219

security.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1000,6 +1000,7 @@ Authentication (Identifying/Logging in the User)
10001000
security/form_login_setup
10011001
security/json_login_setup
10021002
security/guard_authentication
1003+
security/password_migration
10031004
security/auth_providers
10041005
security/user_provider
10051006
security/ldap

security/password_migration.rst

Lines changed: 210 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
1+
.. index::
2+
single: Security; How to Migrate a Password Hash
3+
4+
How to Migrate a Password Hash
5+
==============================
6+
7+
.. versionadded:: 4.4
8+
9+
Password migration was introduced in Symfony 4.4.
10+
11+
In order to protect passwords, it is recommended to store them using the latest
12+
hash algorithms. This means that if a better hash algorithm is supported on the
13+
system, the user's password should be rehashed and stored. Symfony provides this
14+
functionality when a user is successfully authenticated.
15+
16+
To enable this, make sure you apply the following steps to your application:
17+
18+
#. `Configure a new Encoder Using "migrate_from"`_
19+
#. `Upgrade the Password`_
20+
#. Optionally, `Trigger Password Migration From a Custom Encoder`_
21+
22+
Configure a new Encoder Using "migrate_from"
23+
--------------------------------------------
24+
25+
When configuring a new encoder, you can specify a list of legacy encoders by
26+
using the ``migrate_from`` option:
27+
28+
.. configuration-block::
29+
30+
.. code-block:: yaml
31+
32+
# config/packages/security.yaml
33+
security:
34+
# ...
35+
36+
encoders:
37+
legacy:
38+
algorithm: sha256
39+
encode_as_base64: false
40+
iterations: 1
41+
42+
App\Entity\User:
43+
# the new encoder, along with its options
44+
algorithm: sodium
45+
migrate_from:
46+
- bcrypt # uses the "bcrypt" encoder with the default options
47+
- legacy # uses the "legacy" encoder configured above
48+
49+
.. code-block:: xml
50+
51+
<!-- config/packages/security.xml -->
52+
<?xml version="1.0" encoding="UTF-8"?>
53+
<container xmlns="http://symfony.com/schema/dic/services"
54+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
55+
xmlns:security="http://symfony.com/schema/dic/security"
56+
xsi:schemaLocation="http://symfony.com/schema/dic/security
57+
https://symfony.com/schema/dic/security/security-1.0.xsd">
58+
59+
<security:config>
60+
<!-- ... -->
61+
62+
<security:encoder class="legacy"
63+
algorithm="sha256"
64+
encode-as-base64="false"
65+
iterations="1"
66+
/>
67+
68+
<!-- algorithm: the new encoder, along with its options -->
69+
<security:encoder class="App\Entity\User"
70+
algorithm="sodium"
71+
>
72+
<!-- uses the bcrypt encoder with the default options -->
73+
<security:migrate-from>bcrypt</security:migrate-from>
74+
75+
<!-- uses the legacy encoder configured above -->
76+
<security:migrate-from>legacy</security:migrate-from>
77+
</security:encoder>
78+
</security:config>
79+
</container>
80+
81+
.. code-block:: php
82+
83+
// config/packages/security.php
84+
$container->loadFromExtension('security', [
85+
// ...
86+
87+
'encoders' => [
88+
'legacy' => [
89+
'algorithm' => 'sha256',
90+
'encode_as_base64' => false,
91+
'iterations' => 1,
92+
],
93+
94+
'App\Entity\User' => [
95+
// the new encoder, along with its options
96+
'algorithm' => 'sodium',
97+
'migrate_from' => [
98+
'bcrypt', // uses the "bcrypt" encoder with the default options
99+
'legacy', // uses the "legacy" encoder configured above
100+
],
101+
],
102+
],
103+
]);
104+
105+
.. tip::
106+
107+
The *auto*, *native*, *bcrypt* and *argon* encoders automatically enable
108+
password migration using the following list of ``migrate_from`` algorithms:
109+
110+
#. :ref:`PBKDF2 <reference-security-pbkdf2>` (which uses :phpfunction:`hash_pbkdf2`);
111+
#. Message digest (which uses :phpfunction:`hash`)
112+
113+
Both use the ``hash_algorithm`` setting as algorithm. It is recommended to
114+
use ``migrate_from`` instead of ``hash_algorithm``, unless the *auto*
115+
encoder is used.
116+
117+
Upgrade the Password
118+
--------------------
119+
120+
Upon successful login, the Security system checks whether a better algorithm
121+
is available to hash the user's password. If it is, it'll hash the correct
122+
password using the new hash. You can enable this behavior by implementing how
123+
this newly hashed password should be stored:
124+
125+
* `When using Doctrine's entity user provider <Upgrade the Password when using Doctrine>`_
126+
* `When using a custom user provider <Upgrade the Password when using a custom User Provider>`_
127+
128+
After this, you're done and passwords are always hashed as secure as possible!
129+
130+
Upgrade the Password when using Doctrine
131+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
132+
133+
When using the :ref:`entity user provider <security-entity-user-provider>`, implement
134+
:class:`Symfony\\Component\\Security\\Core\\User\\PasswordUpgraderInterface` in
135+
the ``UserRepository`` (see `the Doctrine docs for information`_ on how to
136+
create this class if it's not already created). This interface implements
137+
storing the newly created password hash::
138+
139+
// src/Repository/UserRepository.php
140+
namespace App\Repository;
141+
142+
// ...
143+
use Symfony\Component\Security\Core\User\PasswordUpgraderInterface;
144+
145+
class UserRepository extends EntityRepository implements PasswordUpgraderInterface
146+
{
147+
// ...
148+
149+
public function upgradePassword(UserInterface $user, string $newEncodedPassword): void
150+
{
151+
// set the new encoded password on the User object
152+
$user->setPassword($newEncodedPassword);
153+
154+
// execute the queries on the database
155+
$this->getEntityManager()->flush($user);
156+
}
157+
}
158+
159+
Upgrade the Password when using a Custom User Provider
160+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
161+
162+
If you're using a :ref:`custom user provider <custom-user-provider>`, implement the
163+
:class:`Symfony\\Component\\Security\\Core\\User\\PasswordUpgraderInterface` in
164+
the user provider::
165+
166+
// src/Security/UserProvider.php
167+
namespace App\Security;
168+
169+
// ...
170+
use Symfony\Component\Security\Core\User\PasswordUpgraderInterface;
171+
172+
class UserProvider implements UserProviderInterface, PasswordUpgraderInterface
173+
{
174+
// ...
175+
176+
public function upgradePassword(UserInterface $user, string $newEncodedPassword): void
177+
{
178+
// set the new encoded password on the User object
179+
$user->setPassword($newEncodedPassword);
180+
181+
// ... store the new password
182+
}
183+
}
184+
185+
Trigger Password Migration From a Custom Encoder
186+
------------------------------------------------
187+
188+
If you're using a custom password encoder, you can trigger the password
189+
migration by returning ``true`` in the ``needsRehash()`` method::
190+
191+
// src/Security/UserProvider.php
192+
namespace App\Security;
193+
194+
// ...
195+
use Symfony\Component\Security\Core\Encoder\PasswordEncoderInterface;
196+
197+
class CustomPasswordEncoder implements PasswordEncoderInterface
198+
{
199+
// ...
200+
201+
public function needsRehash(string $encoded): bool
202+
{
203+
// check whether the current password is hash using an outdated encoder
204+
$hashIsOutdated = ...;
205+
206+
return $hashIsOutdated;
207+
}
208+
}
209+
210+
.. _`the Doctrine docs for information`: https://www.doctrine-project.org/projects/doctrine-orm/en/current/reference/working-with-objects.html#custom-repositories

service_container.rst

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -190,8 +190,8 @@ each time you ask for it.
190190
->defaults()
191191
->autowire() // Automatically injects dependencies in your services.
192192
->autoconfigure() // Automatically registers your services as commands, event subscribers, etc.
193-
;
194-
193+
;
194+
195195
// makes classes in src/ available to be used as services
196196
// this creates a service per class whose id is the fully-qualified class name
197197
$services->load('App\\', '../src/*')
@@ -665,6 +665,7 @@ You can also use the ``bind`` keyword to bind specific arguments by name or type
665665
# optionally you can define both the name and type of the argument to match
666666
string $adminEmail: 'manager@example.com'
667667
Psr\Log\LoggerInterface $requestLogger: '@monolog.logger.request'
668+
iterable $rules: !tagged_iterator app.foo.rule
668669
669670
# ...
670671
@@ -695,6 +696,10 @@ You can also use the ``bind`` keyword to bind specific arguments by name or type
695696
type="service"
696697
id="monolog.logger.request"
697698
/>
699+
<bind key="iterable $rules"
700+
type="tagged_iterator"
701+
tag="app.foo.rule"
702+
/>
698703
</defaults>
699704
700705
<!-- ... -->
@@ -728,11 +733,16 @@ You can also use the ``bind`` keyword to bind specific arguments by name or type
728733
// optionally you can define both the name and type of the argument to match
729734
->bind('string $adminEmail', 'manager@example.com')
730735
->bind(LoggerInterface::class.' $requestLogger', ref('monolog.logger.request'))
736+
->bind('iterable $rules', tagged_iterator('app.foo.rule'))
731737
;
732738
733739
// ...
734740
};
735741
742+
.. versionadded:: 4.4
743+
744+
The feature to bind tagged services was introduced in Symfony 4.4.
745+
736746
By putting the ``bind`` key under ``_defaults``, you can specify the value of *any*
737747
argument for *any* service defined in this file! You can bind arguments by name
738748
(e.g. ``$adminEmail``), by type (e.g. ``Psr\Log\LoggerInterface``) or both

service_container/parent_services.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ Your child service classes may look like this::
3939
// src/Repository/DoctrineUserRepository.php
4040
namespace App\Repository;
4141

42-
use App\Repository\BaseDoctrineRepository
42+
use App\Repository\BaseDoctrineRepository;
4343

4444
// ...
4545
class DoctrineUserRepository extends BaseDoctrineRepository
@@ -50,7 +50,7 @@ Your child service classes may look like this::
5050
// src/Repository/DoctrinePostRepository.php
5151
namespace App\Repository;
5252

53-
use App\Repository\BaseDoctrineRepository
53+
use App\Repository\BaseDoctrineRepository;
5454

5555
// ...
5656
class DoctrinePostRepository extends BaseDoctrineRepository

0 commit comments

Comments
 (0)
0