-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathUserManager.php
More file actions
108 lines (95 loc) · 3.89 KB
/
Copy pathUserManager.php
File metadata and controls
108 lines (95 loc) · 3.89 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
<?php
declare(strict_types=1);
namespace App\Security;
use App\Entity\User;
use App\Enum\UserStatus;
use App\Repository\UserRepository;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
/**
* Create users and rotate their passwords.
*
* Hides the Doctrine + password-hasher wiring from callers
* (controllers, console commands, fixtures) so they work with plain
* strings and get a persisted {@see User} back.
*/
final class UserManager
{
/**
* @param EntityManagerInterface $entityManager Doctrine entity manager
* @param UserRepository $userRepository read-side lookup of users by email
* @param UserPasswordHasherInterface $passwordHasher Symfony Security hasher
*/
public function __construct(
private readonly EntityManagerInterface $entityManager,
private readonly UserRepository $userRepository,
private readonly UserPasswordHasherInterface $passwordHasher,
) {
}
/**
* Create a new persisted user with a hashed password.
*
* Defaults to `UserStatus::Approved` so the console / fixture paths
* land a usable account immediately. The registration flow (#62)
* passes `UserStatus::Pending` explicitly so a domain manager has
* to approve the user before they can sign in.
*
* @param string $email user e-mail; must be unique
* @param string $name display name; required, may be any non-null string
* @param string $plainPassword clear-text password, hashed before persistence
* @param list<string> $roles additional roles beyond the implicit `ROLE_USER`
* @param UserStatus $status identity-lifecycle status; defaults to {@see UserStatus::Approved}
*
* @return User the persisted user with an assigned id
*
* @throws \DomainException when a user with the same e-mail already exists
* @throws \InvalidArgumentException when `$plainPassword` is empty
*/
public function createUser(
string $email,
string $name,
string $plainPassword,
array $roles = [],
UserStatus $status = UserStatus::Approved,
): User {
if ('' === $plainPassword) {
throw new \InvalidArgumentException('Password must not be empty.');
}
if (null !== $this->userRepository->findOneBy(['email' => $email])) {
throw new \DomainException(\sprintf('A user with the e-mail "%s" already exists.', $email));
}
$user = (new User())
->setEmail($email)
->setName($name)
->setRoles($roles)
->setStatus($status);
$user->setPassword($this->passwordHasher->hashPassword($user, $plainPassword));
$this->entityManager->persist($user);
$this->entityManager->flush();
return $user;
}
/**
* Replace a user's password with a freshly hashed copy.
*
* @param string $email e-mail of the user to update
* @param string $newPlainPassword new clear-text password, hashed before persistence
*
* @return User the updated user
*
* @throws \DomainException when no user with that e-mail exists
* @throws \InvalidArgumentException when `$newPlainPassword` is empty
*/
public function changePassword(string $email, string $newPlainPassword): User
{
if ('' === $newPlainPassword) {
throw new \InvalidArgumentException('Password must not be empty.');
}
$user = $this->userRepository->findOneBy(['email' => $email]);
if (null === $user) {
throw new \DomainException(\sprintf('No user with the e-mail "%s" was found.', $email));
}
$user->setPassword($this->passwordHasher->hashPassword($user, $newPlainPassword));
$this->entityManager->flush();
return $user;
}
}