8000 Fixing Security Entity Provider tutorial · symfony/symfony-docs@827dbbc · GitHub
[go: up one dir, main page]

Skip to content

Commit 827dbbc

Browse files
committed
Fixing Security Entity Provider tutorial
for more info see #2756
1 parent ebdcaba commit 827dbbc

File tree

3 files changed

+104
-86
lines changed

3 files changed

+104
-86
lines changed

book/doctrine.rst

Lines changed: 4 additions & 0 deletions
+
.. _book-doctrine-generating-getters-and-setters:
Original file line numberDiff line numberDiff line change
@@ -363,6 +363,8 @@ see the :ref:`book-doctrine-field-types` section.
363363
class Product
364364
// ...
365365

366
367+
366368
Generating Getters and Setters
367369
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
368370

@@ -425,6 +427,8 @@ mapping information) of a bundle or an entire namespace:
425427
The getters and setters are generated here only because you'll need them
426428
to interact with your PHP object.
427429

430+
.. _book-doctrine-creating-the-database-tables-schema:
431+
428432
Creating the Database Tables/Schema
429433
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
430434

contributing/community/releases.rst

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,18 +75,30 @@ Below is the schedule for the first few versions that use this release model:
7575

7676
This results in very predictable dates and maintenance periods:
7777

78+
79+
+---------+---------+---------------------+-------------+
7880
| Version | Release | End of Maintenance | End of Life |
79-
| ------- | ------- | ------------------- | ----------- |
81+
+=========+=========+=====================+=============+
8082
| 2.0 | 07/2011 | 03/2013 (20 months) | 09/2013 |
83+
+---------+---------+---------------------+-------------+
8184
| 2.1 | 09/2012 | 05/2013 (9 months) | 11/2013 |
85+
+---------+---------+---------------------+-------------+
8286
| 2.2 | 03/2013 | 11/2013 (8 months) | 05/2014 |
87+
+---------+---------+---------------------+-------------+
8388
| **2.3** | 05/2013 | 05/2016 (36 months) | 05/2017 |
89+
+---------+---------+---------------------+-------------+
8490
| 2.4 | 11/2013 | 07/2014 (8 months) | 01/2015 |
91+
+---------+---------+---------------------+-------------+
8592
| 2.5 | 05/2014 | 01/2015 (8 months) | 07/2016 |
93+
+---------+---------+---------------------+-------------+
8694
| 2.6 | 11/2014 | 07/2015 (8 months) | 01/2016 |
95+
+---------+---------+---------------------+-------------+
8796
| **2.7** | 05/2015 | 05/2018 (36 months) | 05/2019 |
97+
+---------+---------+---------------------+-------------+
8898
| 2.8 | 11/2015 | 07/2016 (8 months) | 01/2017 |
99+
+---------+---------+---------------------+-------------+
89100
| ... | ... | ... | ... |
101+
+---------+---------+---------------------+-------------+
90102

91103
.. tip::
92104

cookbook/security/entity_provider.rst

Lines changed: 87 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -167,21 +167,6 @@ interface forces the class to implement the five following methods:
167167

168168
For more details on each of these, see :class:`Symfony\\Component\\Security\\Core\\User\\UserInterface`.
169169

170-
.. code-block:: php
171-
172-
// src/Acme/UserBundle/Entity/User.php
173-
174-
namespace Acme\UserBundle\Entity;
175-
176-
use Symfony\Component\Security\Core\User\EquatableInterface;
177-
178-
// ...
179-
180-
public function isEqualTo(UserInterface $user)
181-
{
182-
return $this->id === $user->getId();
183-
}
184-
185170
.. note::
186171

187172
The :phpclass:`Serializable` interface and its ``serialize`` and ``unserialize``
@@ -191,24 +176,32 @@ For more details on each of these, see :class:`Symfony\\Component\\Security\\Cor
191176
because the :method:`Symfony\\Bridge\\Doctrine\\Security\\User\\EntityUserProvider::refreshUser`
192177
method reloads the user on each request by using the ``id``.
193178

194-
Below is an export of my ``User`` table from MySQL. For details on how to
195-
create user records and encode their password, see :ref:`book-security-encoding-user-password`.
179+
.. tip::
180+
181+
To generate missing setters and getters for your ``User`` entity, you
182+
can use ``php app/console doctrine:generate:entities Acme/UserBundle/Entity/User``.
183+
For more details, see Doctrine's :ref:`book-doctrine-generating-getters-and-setters`.
184+
185+
Below is an export of my ``User`` table from MySQL with user `admin`
F438 186+
and password `admin`. For details on how to create user records and
187+
encode their password, see :ref:`book-security-encoding-user-password`.
196188

197189
.. code-block:: bash
198190
199-
$ mysql> select * from user;
200-
+----+----------+----------------------------------+------------------------------------------+--------------------+-----------+
201-
| id | username | salt | password | email | is_active |
202-
+----+----------+----------------------------------+------------------------------------------+--------------------+-----------+
203-
| 1 | hhamon | 7308e59b97f6957fb42d66f894793079 | 09610f61637408828a35d7debee5b38a8350eebe | hhamon@example.com | 1 |
204-
| 2 | jsmith | ce617a6cca9126bf4036ca0c02e82dee | 8390105917f3a3d533815250ed7c64b4594d7ebf | jsmith@example.com | 1 |
205-
| 3 | maxime | cd01749bb995dc658fa56ed45458d807 | 9764731e5f7fb944de5fd8efad4949b995b72a3c | maxime@example.com | 0 |
206-
| 4 | donald | 6683c2bfd90c0426088402930cadd0f8 | 5c3bcec385f59edcc04490d1db95fdb8673bf612 | donald@example.com | 1 |
207-
+----+----------+----------------------------------+------------------------------------------+--------------------+-----------+
208-
4 rows in set (0.00 sec)
209-
210-
The database now contains four users with different usernames, emails and
211-
statuses. The next part will focus on how to authenticate one of these users
191+
$ mysql> select * from acme_users;
192+
+----+----------+------+------------------------------------------+--------------------+-----------+
193+
| id | username | salt | password | email | is_active |
194+
+----+----------+------+------------------------------------------+--------------------+-----------+
195+
| 1 | admin | | d033e22ae348aeb5660fc2140aec35850c4da997 | admin@example.com | 1 |
196+
+----+----------+------+------------------------------------------+--------------------+-----------+
197+
198+
.. tip::
199+
200+
To generate database table from your ``User`` entity, you can run
201+
``php app/console doctrine:schema:update --force``.
202+
For mor details, see Doctrine's :ref:`book-doctrine-creating-the-database-tables-schema`.
203+
204+
The next part will focus on how to authenticate one of these users
212205
thanks to the Doctrine entity user provider and a couple of lines of
213206
configuration.
214207
@@ -323,9 +316,8 @@ entity user provider to load User entity objects from the database by using
323316
the ``username`` unique field. In other words, this tells Symfony how to
324317
fetch the user from the database before checking the password validity.
325318
326-
This code and configuration works but it's not enough to secure the application
327-
for **active** users. As of now, you can still authenticate with ``maxime``. The
328-
next section explains how to forbid non active users.
319+
This code is not enough to secure the application for **active** users.
320+
The next section explains how to forbid non active users.
329321
330322
Forbid non Active Users
331323
-----------------------
@@ -355,10 +347,10 @@ For this example, the first three methods will return ``true`` whereas the
355347
// src/Acme/UserBundle/Entity/User.php
356348
namespace Acme\UserBundle\Entity;
357349
358-
// ...
350+
use Doctrine\ORM\Mapping as ORM;
359351
use Symfony\Component\Security\Core\User\AdvancedUserInterface;
360352
361-
class User implements AdvancedUserInterface
353+
class User implements AdvancedUserInterface, \Serializable
362354
{
363355
// ...
364356
@@ -383,10 +375,8 @@ For this example, the first three methods will return ``true`` whereas the
383375
}
384376
}
385377
386-
If you try to authenticate as ``maxime``, the access is now forbidden as this
387-
user does not have an enabled account. The next session will focus on how
388-
to write a custom entity provider to authenticate a user with his username
389-
or his email address.
378+
The next session will focus on how to write a custom entity provider
379+
to authenticate a user with his username or his email address.
390380
391381
Authenticating Someone with a Custom Entity Provider
392382
----------------------------------------------------
@@ -428,8 +418,7 @@ The code below shows the implementation of the
428418
->where('u.username = :username OR u.email = :email')
429419
->setParameter('username', $username)
430420
->setParameter('email', $username)
431-
->getQuery()
432-
;
421+
->getQuery();
433422
434423
try {
435424
// The Query::getSingleResult() method throws an exception
@@ -537,10 +526,11 @@ about in this section.
5375 10000 26
authenticated at all.
538527
539528
In this example, the ``AcmeUserBundle:User`` entity class defines a
540-
many-to-many relationship with a ``AcmeUserBundle:Group`` entity class. A user
541-
can be related to several groups and a group can be composed of one or
542-
more users. As a group is also a role, the previous ``getRoles()`` method now
543-
returns the list of related groups::
529+
many-to-many relationship with a ``AcmeUserBundle:Role`` entity class.
530+
A user can be related to several roles and a role can be composed of
531+
one or more users. The previous ``getRoles()`` method now returns
532+
the list of related roles.
533+
Notice that methods ``__construcotor()`` and ``getRoles()`` had changed::
544534
545535
// src/Acme/UserBundle/Entity/User.php
546536
namespace Acme\UserBundle\Entity;
@@ -550,63 +540,46 @@ returns the list of related groups::
550540
551541
class User implements AdvancedUserInterface, \Serializable
552542
{
543+
//...
544+
553545
/**
554-
* @ORM\ManyToMany(targetEntity="Group", inversedBy="users")
546+
* @ORM\ManyToMany(targetEntity="Role", inversedBy="users")
555547
*
556548
*/
557-
private $groups;
549+
private $roles;
558550
559551
public function __construct()
560552
{
561-
$this->groups = new ArrayCollection();
553+
$this->roles = new ArrayCollection();
562554
}
563555
564-
// ...
565-
566556
public function getRoles()
567557
{
568-
return $this->groups->toArray();
569-
}
570-
571-
/**
572-
* @see \Serializable::serialize()
573-
*/
574 17AE -
public function serialize()
575-
{
576-
return serialize(array(
577-
$this->id,
578-
));
558+
return $this->roles->toArray();
579559
}
560+
561+
// ...
580562
581-
/**
582-
* @see \Serializable::unserialize()
583-
*/
584-
public function unserialize($serialized)
585-
{
586-
list (
587-
$this->id,
588-
) = unserialize($serialized);
589-
}
590563
}
591564
592-
The ``AcmeUserBundle:Group`` entity class defines three table fields (``id``,
565+
The ``AcmeUserBundle:Role`` entity class defines three table fields (``id``,
593566
``name`` and ``role``). The unique ``role`` field contains the role name used by
594567
the Symfony security layer to secure parts of the application. The most
595-
important thing to notice is that the ``AcmeUserBundle:Group`` entity class
568+
important thing to notice is that the ``AcmeUserBundle:Role`` entity class
596569
extends the :class:`Symfony\\Component\\Security\\Core\\Role\\Role`::
597570
598-
// src/Acme/Bundle/UserBundle/Entity/Group.php
571+
// src/Acme/Bundle/UserBundle/Entity/Role.php
599572
namespace Acme\UserBundle\Entity;
600573
601-
use Symfony\Component\Security\Core\Role\Role;
574+
use Symfony\Component\Security\Core\Role\RoleInterface;
602575
use Doctrine\Common\Collections\ArrayCollection;
603576
use Doctrine\ORM\Mapping as ORM;
604577
605578
/**
606-
* @ORM\Table(name="acme_groups")
579+
* @ORM\Table(name="acme_roles")
607580
* @ORM\Entity()
608581
*/
609-
class Group extends Role
582+
class Role implements RoleInterface
610583
{
611584
/**
612585
* @ORM\Column(name="id", type="integer")
@@ -626,7 +599,7 @@ extends the :class:`Symfony\\Component\\Security\\Core\\Role\\Role`::
626599
private $role;
627600
628601
/**
629-
* @ORM\ManyToMany(targetEntity="User", mappedBy="groups")
602+
* @ORM\ManyToMany(targetEntity="User", mappedBy="roles")
630603
*/
631604
private $users;
632605
@@ -635,21 +608,27 @@ extends the :class:`Symfony\\Component\\Security\\Core\\Role\\Role`::
635608
$this->users = new ArrayCollection();
636609
}
637610
638-
// ... getters and setters for each property
639-
640611
/**
641612
* @see RoleInterface
642613
*/
643614
public function getRole()
644615
{
645616
return $this->role;
646617
}
618+
619+
// ... getters and setters for each property
647620
}
648621
649-
To improve performances and avoid lazy loading of groups when retrieving a user
650-
from the custom entity provider, the best solution is to join the groups
622+
.. tip::
623+
624+
To generate missing setters and getters for your ``Role`` entity, you
625+
can use ``php app/console doctrine:generate:entities Acme/UserBundle/Entity/User``.
626+
For more details, see Doctrine's :ref:`book-doctrine-generating-getters-and-setters`.
627+
628+
To improve performances and avoid lazy loading of roles when retrieving a user
629+
from the custom entity provider, the best solution is to join the roles
651630
relationship in the ``UserRepository::loadUserByUsername()`` method. This will
652-
fetch the user and his associated roles / groups with a single query::
631+
fetch the user and his associated roles with a single query::
653632
654633
// src/Acme/UserBundle/Entity/UserRepository.php
655634
namespace Acme\UserBundle\Entity;
@@ -662,8 +641,8 @@ fetch the user and his associated roles / groups with a single query::
662641
{
663642
$q = $this
664643
->createQueryBuilder('u')
665-
->select('u, g')
666-
->leftJoin('u.groups', 'g')
644+
->select('u, r')
645+
->leftJoin('u.roles', 'r')
667646
->where('u.username = :username OR u.email = :email')
668647
->setParameter('username', $username)
669648
->setParameter('email', $username)
@@ -675,6 +654,29 @@ fetch the user and his associated roles / groups with a single query::
675654
// ...
676655
}
677656
678-
The ``QueryBuilder::leftJoin()`` method joins and fetches related groups from
657+
The ``QueryBuilder::leftJoin()`` method joins and fetches related roles from
679658
the ``AcmeUserBundle:User`` model class when a user is retrieved with his email
680659
address or username.
660+
661+
To re-generate all database tables, you can run ``php app/console doctrine:schema:update --force``.
662+
This will also create additional table ``user_role`` what holds
663+
relations between users and roles.
664+
For mor details, see Doctrine's :ref:`book-doctrine-creating-the-database-tables-schema`.
665+
666+
Below is an export of my ``Roles`` and ``user_role`` tables from MySQL:
667+
668+
.. code-block:: bash
669+
670+
$ mysql> select * from acme_users;
671+
+----+-------+------------+
672+
| id | name | role |
673+
+----+-------+------------+
674+
| 1 | admin | ROLE_ADMIN |
675+
+----+-------+------------+
676+
677+
mysql> select * from user_role;
678+
+---------+---------+
679+
| user_id | role_id |
680+
+---------+---------+
681+
| 1 | 1 |
682+
+---------+---------+

0 commit comments

Comments
 (0)
0