Skip to content
This repository
Browse code

[Doctrine] fixed security user reloading when the user has been chang…

…ed via a form with validation errors (closes #2033)
  • Loading branch information...
commit 9d2ab9ca9c1762c529e053cefd39a5e626102133 1 parent 414d4be
Fabien Potencier authored November 08, 2011
10  src/Symfony/Bridge/Doctrine/Security/User/EntityUserProvider.php
@@ -30,13 +30,15 @@ class EntityUserProvider implements UserProviderInterface
30 30
     private $class;
31 31
     private $repository;
32 32
     private $property;
  33
+    private $metadata;
33 34
 
34 35
     public function __construct(EntityManager $em, $class, $property = null)
35 36
     {
36 37
         $this->class = $class;
  38
+        $this->metadata = $em->getClassMetadata($class);
37 39
 
38 40
         if (false !== strpos($this->class, ':')) {
39  
-            $this->class = $em->getClassMetadata($class)->name;
  41
+            $this->class = $this->metadata->name;
40 42
         }
41 43
 
42 44
         $this->repository = $em->getRepository($class);
@@ -74,7 +76,11 @@ public function refreshUser(UserInterface $user)
74 76
             throw new UnsupportedUserException(sprintf('Instances of "%s" are not supported.', get_class($user)));
75 77
         }
76 78
 
77  
-        return $this->loadUserByUsername($user->getUsername());
  79
+        // The user must be reloaded via the primary key as all other data
  80
+        // might have changed without proper persistence in the database.
  81
+        // That's the case when the user has been changed by a form with
  82
+        // validation errors.
  83
+        return $this->repository->find($this->metadata->getIdentifierValues($user));
78 84
     }
79 85
 
80 86
     /**
28  tests/Symfony/Tests/Bridge/Doctrine/Fixtures/CompositeIdentEntity.php
@@ -5,9 +5,10 @@
5 5
 use Doctrine\ORM\Mapping\Id;
6 6
 use Doctrine\ORM\Mapping\Column;
7 7
 use Doctrine\ORM\Mapping\Entity;
  8
+use Symfony\Component\Security\Core\User\UserInterface;
8 9
 
9 10
 /** @Entity */
10  
-class CompositeIdentEntity
  11
+class CompositeIdentEntity implements UserInterface
11 12
 {
12 13
     /** @Id @Column(type="integer") */
13 14
     protected $id1;
@@ -23,4 +24,29 @@ public function __construct($id1, $id2, $name) {
23 24
         $this->id2 = $id2;
24 25
         $this->name = $name;
25 26
     }
  27
+
  28
+    public function getRoles()
  29
+    {
  30
+    }
  31
+
  32
+    public function getPassword()
  33
+    {
  34
+    }
  35
+
  36
+    public function getSalt()
  37
+    {
  38
+    }
  39
+
  40
+    public function getUsername()
  41
+    {
  42
+        return $this->name;
  43
+    }
  44
+
  45
+    public function eraseCredentials()
  46
+    {
  47
+    }
  48
+
  49
+    public function equals(UserInterface $user)
  50
+    {
  51
+    }
26 52
 }
51  tests/Symfony/Tests/Bridge/Doctrine/Security/User/EntityUserProviderTest.php
... ...
@@ -0,0 +1,51 @@
  1
+<?php
  2
+
  3
+/*
  4
+ * This file is part of the Symfony package.
  5
+ *
  6
+ * (c) Fabien Potencier <fabien@symfony.com>
  7
+ *
  8
+ * For the full copyright and license information, please view the LICENSE
  9
+ * file that was distributed with this source code.
  10
+ */
  11
+
  12
+namespace Symfony\Tests\Bridge\Doctrine\Security\User;
  13
+
  14
+require_once __DIR__.'/../../DoctrineOrmTestCase.php';
  15
+require_once __DIR__.'/../../Fixtures/CompositeIdentEntity.php';
  16
+
  17
+use Symfony\Tests\Bridge\Doctrine\DoctrineOrmTestCase;
  18
+use Symfony\Tests\Bridge\Doctrine\Fixtures\CompositeIdentEntity;
  19
+use Symfony\Bridge\Doctrine\Security\User\EntityUserProvider;
  20
+use Doctrine\ORM\Tools\SchemaTool;
  21
+
  22
+class EntityUserProviderTest extends DoctrineOrmTestCase
  23
+{
  24
+    public function testRefreshUserGetsUserByPrimaryKey()
  25
+    {
  26
+        $em = $this->createTestEntityManager();
  27
+        $this->createSchema($em);
  28
+
  29
+        $user1 = new CompositeIdentEntity(1, 1, 'user1');
  30
+        $user2 = new CompositeIdentEntity(1, 2, 'user2');
  31
+
  32
+        $em->persist($user1);
  33
+        $em->persist($user2);
  34
+        $em->flush();
  35
+
  36
+        $provider = new EntityUserProvider($em, 'Symfony\Tests\Bridge\Doctrine\Fixtures\CompositeIdentEntity', 'name');
  37
+
  38
+        // try to change the user identity
  39
+        $user1->name = 'user2';
  40
+
  41
+        $this->assertSame($user1, $provider->refreshUser($user1));
  42
+    }
  43
+
  44
+    private function createSchema($em)
  45
+    {
  46
+        $schemaTool = new SchemaTool($em);
  47
+        $schemaTool->createSchema(array(
  48
+            $em->getClassMetadata('Symfony\Tests\Bridge\Doctrine\Fixtures\CompositeIdentEntity'),
  49
+        ));
  50
+    }
  51
+}

0 notes on commit 9d2ab9c

Benjamin Eberlei

Just to understand, this will still be a security problem if someone adds the "id" as hidden field through a form instead of going through a get parameter in the Url or?

Fabien Potencier

Correct. If you allow the user to change its primary key, then the security issue stil exist. But frankly, I don't think this happens a lot, does it?

Benjamin Eberlei

How about additionally saving the username into the token and making an assertion on username == user->getUsername()?

Benjamin Eberlei

other than that you are probably right, that this doesnt happen often. The PR looks good to me.

Pierre Durand

Since this patch, my authentication is broken.
I use a form authentication, with an email and a password.

With the previous version of this file "$user->getUsername()" returns my user's email.
Currently "$this->metadata->getIdentifierValues($user)" returns an empty array, because the user's id is not saved in the "remember me" cookie. (The user's email is saved).

Is something wrong?

Christophe Coevoet

keep the id when serializing your user.

Please sign in to comment.
Something went wrong with that request. Please try again.