Browse Source

User model, SaltGenerator, UserProvider, UserCreateCommand

tags/v1
Simon Vieille 3 years ago
parent
commit
2f7864be0f

+ 41
- 0
app/bootstrap.php.d/70-user.php View File

@@ -0,0 +1,41 @@
1
+<?php
2
+
3
+use Gist\Service\UserProvider;
4
+use Silex\Provider\SecurityServiceProvider;
5
+use Gist\Service\SaltGenerator;
6
+
7
+$app['salt_generator'] = function ($app) {
8
+    return new SaltGenerator();
9
+};
10
+
11
+$app['user.provider'] = function ($app) {
12
+    return new UserProvider(
13
+        $app['security.encoder.digest'], 
14
+        $app['salt_generator']
15
+    );
16
+};
17
+
18
+$app->register(
19
+    new SecurityServiceProvider(), 
20
+    [
21
+        'security.firewalls' => [
22
+            'default' => [
23
+                'pattern' => '^/user.*$',
24
+                'anonymous' => false,
25
+                'form' => [
26
+                    'login_path' => '/login', 
27
+                    'check_path' => 'login_check',
28
+                ],
29
+                'logout' => [
30
+                    'logout_path' => '/logout'
31
+                ],
32
+                'users' => $app->share(function() use ($app) {
33
+                    return $app['user.provider'];
34
+                }),
35
+            ],
36
+        ],
37
+        'security.access_rules' => [
38
+            ['^/user.*$', 'ROLE_USER'],
39
+        ]
40
+    ]
41
+);

+ 2
- 0
app/console View File

@@ -4,11 +4,13 @@
4 4
 use Gist\Command\CreateCommand;
5 5
 use Gist\Command\UpdateCommand;
6 6
 use Gist\Command\StatsCommand;
7
+use Gist\Command\UserCreateCommand;
7 8
 
8 9
 $app = require __DIR__.'/bootstrap.php';
9 10
 
10 11
 $app['console']->add(new CreateCommand());
11 12
 $app['console']->add(new UpdateCommand());
12 13
 $app['console']->add(new StatsCommand());
14
+$app['console']->add(new UserCreateCommand());
13 15
 
14 16
 $app['console']->run();

+ 2
- 1
composer.json View File

@@ -10,7 +10,8 @@
10 10
         "symfony/security-csrf": "~2.6",
11 11
         "knplabs/console-service-provider": "~1.0",
12 12
         "propel/propel": "~2.0@dev",
13
-        "guzzlehttp/guzzle": "~6.0"
13
+        "guzzlehttp/guzzle": "~6.0",
14
+        "symfony/security": "^2.7"
14 15
     },
15 16
     "autoload": {
16 17
         "psr-0": {

+ 45
- 0
src/Gist/Command/UserCreateCommand.php View File

@@ -0,0 +1,45 @@
1
+<?php
2
+
3
+namespace Gist\Command;
4
+
5
+use Symfony\Component\Console\Input\InputInterface;
6
+use Symfony\Component\Console\Output\OutputInterface;
7
+use Knp\Command\Command;
8
+use Symfony\Component\Console\Question\Question;
9
+
10
+class UserCreateCommand extends Command
11
+{
12
+    protected function configure()
13
+    {
14
+        $this
15
+            ->setName('user:create')
16
+            ->setDescription('Create a user')
17
+            ->setHelp("");
18
+    }
19
+
20
+    protected function execute(InputInterface $input, OutputInterface $output)
21
+    {
22
+        $helper = $this->getHelper('question');
23
+        $userProvider = $this->getSilexApplication()['user.provider'];
24
+
25
+        $username = '';
26
+        $password = '';
27
+
28
+        while (trim($username) === '') {
29
+            $question = new Question('Username: ', '');
30
+            $username = $helper->ask($input, $output, $question);
31
+        
32
+            if ($userProvider->userExists($username)) {
33
+                $output->writeln('<error>This username is already used.</error>');
34
+                $username = '';
35
+            }
36
+        }
37
+
38
+        while (trim($password) === '') {
39
+            $question = new Question('Password: ', '');
40
+            $password = $helper->ask($input, $output, $question);
41
+        }
42
+
43
+        $userProvider->registerUser($username, $password);
44
+    }
45
+}

+ 14
- 0
src/Gist/Model/User.php View File

@@ -0,0 +1,14 @@
1
+<?php
2
+
3
+namespace Gist\Model;
4
+
5
+use Gist\Model\Base\User as BaseUser;
6
+use Symfony\Component\Security\Core\User\UserInterface;
7
+
8
+class User extends BaseUser implements UserInterface
9
+{
10
+    public function eraseCredentials()
11
+    {
12
+        $this->setPassword(null);
13
+    }
14
+}

+ 20
- 0
src/Gist/Model/UserQuery.php View File

@@ -0,0 +1,20 @@
1
+<?php
2
+
3
+namespace Gist\Model;
4
+
5
+use Gist\Model\Base\UserQuery as BaseUserQuery;
6
+
7
+/**
8
+ * Skeleton subclass for performing query and update operations on the 'user' table.
9
+ *
10
+ *
11
+ *
12
+ * You should add additional methods to this class to meet the
13
+ * application requirements.  This class will only be generated as
14
+ * long as it does not already exist in the output directory.
15
+ *
16
+ */
17
+class UserQuery extends BaseUserQuery
18
+{
19
+
20
+}

+ 15
- 1
src/Gist/Resources/config/propel/schema.xml View File

@@ -2,12 +2,26 @@
2 2
 <database defaultIdMethod="native" name="default" namespace="Gist\Model">
3 3
    <table name="gist">
4 4
 		<column name="id" type="INTEGER" primaryKey="true" required="true" autoIncrement="true"/>
5
-		
6 5
 		<column name="title" type="VARCHAR" size="255" required="false" />
7 6
 		<column name="cipher" type="BOOLEAN" required="true" defaultValue="false" />
8 7
 		<column name="type" type="VARCHAR" size="30" required="true" />
9 8
 		<column name="file" type="VARCHAR" size="30" required="true" />
9
+        <column name="user_id" type="INTEGER" required="false" />
10
+        
11
+        <foreign-key foreignTable="user" onDelete="setnull" onUpdate="cascade">
12
+            <reference local="user_id" foreign="id"/>
13
+        </foreign-key>
10 14
 
11 15
 		<behavior name="timestampable"/>
12 16
 	</table>
17
+
18
+    <table name="user">
19
+        <column name="id" type="INTEGER" primaryKey="true" required="true" autoIncrement="true"/>
20
+        <column name="username" type="VARCHAR" size="255" required="true" />
21
+        <column name="password" type="VARCHAR" size="255" required="true" />
22
+        <column name="roles" type="VARCHAR" size="255" required="true" />
23
+        <column name="salt" type="VARCHAR" size="64" required="true" />
24
+
25
+        <behavior name="timestampable"/>
26
+    </table>
13 27
 </database>

+ 29
- 0
src/Gist/Service/SaltGenerator.php View File

@@ -0,0 +1,29 @@
1
+<?php
2
+
3
+namespace Gist\Service;
4
+
5
+use InvalidArgumentException;
6
+
7
+/**
8
+ * Class SaltGenerator
9
+ * @author Simon Vieille <simon@deblan.fr>
10
+ */
11
+class SaltGenerator
12
+{
13
+    public function generate($length = 64)
14
+    {
15
+        if (!is_numeric($length)) {
16
+            throw new InvalidArgumentException('Paramter length must be a valid integer.');
17
+        }
18
+
19
+        if (function_exists('openssl_random_pseudo_bytes')) {
20
+            return substr(base64_encode(openssl_random_pseudo_bytes($length)), 0, $length);
21
+        }
22
+
23
+        if (function_exists('mcrypt_create_iv')) {
24
+            return substr(base64_encode(mcrypt_create_iv($length, MCRYPT_DEV_URANDOM)), 0, $length);
25
+        }
26
+
27
+        throw new RuntimeException('You must enable openssl or mcrypt modules.');
28
+    }
29
+}

+ 112
- 0
src/Gist/Service/UserProvider.php View File

@@ -0,0 +1,112 @@
1
+<?php
2
+
3
+namespace Gist\Service;
4
+
5
+use Gist\Model\UserQuery;
6
+use Symfony\Component\Security\Core\Encoder\MessageDigestPasswordEncoder;
7
+use Gist\Model\User;
8
+use Symfony\Component\Security\Core\User\UserProviderInterface;
9
+use Symfony\Component\Security\Core\User\UserInterface;
10
+use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
11
+use Symfony\Component\Security\Core\Exception\UnsupportedUserException;
12
+use Gist\Service\SaltGenerator;
13
+
14
+/**
15
+ * Class UserProvider
16
+ * @author Simon Vieille <simon@deblan.fr>
17
+ */
18
+class UserProvider implements UserProviderInterface
19
+{
20
+    protected $encoder;
21
+    
22
+    protected $saltGenerator;
23
+
24
+    public function __construct(MessageDigestPasswordEncoder $encoder, SaltGenerator $saltGenerator)
25
+    {
26
+        $this->encoder = $encoder;
27
+        $this->saltGenerator = $saltGenerator;
28
+    }
29
+
30
+    public function setEncoder(MessageDigestPasswordEncoder $encoder)
31
+    {
32
+        $this->encoder = $encoder;
33
+
34
+        return $this;
35
+    }
36
+
37
+    public function getEncoder()
38
+    {
39
+        return $this->encoder;
40
+    }
41
+
42
+    public function setSaltGenerator(SaltGenerator $saltGenerator)
43
+    {
44
+        $this->saltGenerator = $saltGenerator;
45
+
46
+        return $this;
47
+    }
48
+
49
+    public function getSaltGenerator()
50
+    {
51
+        return $this->saltGenerator;
52
+    }
53
+
54
+    public function userExists($username)
55
+    {
56
+        return UserQuery::create()
57
+            ->filterByUsername($username)
58
+            ->count() > 0;
59
+    }
60
+
61
+    public function registerUser($username, $password)
62
+    {
63
+        $user = new User();
64
+
65
+        $salt = $this->saltGenerator->generate(64);
66
+
67
+        $user
68
+            ->setUsername($username)
69
+            ->setRoles('ROLE_USER')
70
+            ->setSalt($salt);
71
+
72
+        $user
73
+            ->setPassword($this->encoder->encodePassword($user, $password))
74
+            ->save();
75
+
76
+        return $user;
77
+    }
78
+
79
+    public function updateUserPassword(User $user, $password)
80
+    {
81
+        $user
82
+            ->setPassword($this->encoder->encodePassword($password))
83
+            ->save();
84
+
85
+        return $user;
86
+    }
87
+
88
+    public function loadUserByUsername($username)
89
+    {
90
+        $user = UserQuery::create()->findOneByUsername($username);
91
+
92
+        if (null === $user) {
93
+            throw new UsernameNotFoundException(sprintf('Username "%s" does not exist.', $username));
94
+        }
95
+
96
+        return $user;
97
+    }
98
+
99
+    public function refreshUser(UserInterface $user)
100
+    {
101
+        if (!$user instanceof User) {
102
+            throw new UnsupportedUserException(sprintf('Instances of "%s" are not supported.', get_class($user)));
103
+        }
104
+
105
+        return $this->loadUserByUsername($user->getUsername());
106
+    }
107
+
108
+    public function supportsClass($class)
109
+    {
110
+        return $class === 'Gist\\Model\\User';
111
+    }
112
+}

Loading…
Cancel
Save