-
Notifications
You must be signed in to change notification settings - Fork 1
/
CreatePasswordResetToken.php
105 lines (90 loc) · 3.75 KB
/
CreatePasswordResetToken.php
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
<?php
namespace BigGive\Identity\Application\Actions;
use BigGive\Identity\Application\Settings\SettingsInterface;
use BigGive\Identity\Client\Mailer;
use BigGive\Identity\Domain\PasswordResetToken;
use BigGive\Identity\Repository\PasswordResetTokenRepository;
use BigGive\Identity\Repository\PersonRepository;
use Laminas\Diactoros\Response\JsonResponse;
use OpenApi\Annotations as OA;
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Log\LoggerInterface;
use Slim\Exception\HttpBadRequestException;
use Symfony\Component\Validator\Constraints\Email;
use Symfony\Component\Validator\Validator\ValidatorInterface;
/**
* @OA\Post(
* path="/v1/password-reset-token",
* @OA\RequestBody(
* description="",
* required=true,
* @OA\JsonContent(
* @OA\Property(property="email_address", type="string", example="[email protected]"),
* )
* ),
* @OA\Response(
* response=200,
* description="Token created and emailed to user, if they exist, which the server does
* not confirm or deny via http",
* @OA\JsonContent(),
* ),
* @OA\Response(
* response=400,
* description="Email address is invalid, e.g. doesn't create @ sign. The absence of this
* error doesn't mean the email is registered with us or exists, just that the format looks OK..",
* @OA\JsonContent(),
* )
* )
* )
* )
*/ class CreatePasswordResetToken extends Action
{
public function __construct(
LoggerInterface $logger,
private readonly PersonRepository $personRepository,
private readonly PasswordResetTokenRepository $tokenRepository,
private readonly ValidatorInterface $validator,
private readonly SettingsInterface $settings,
private readonly Mailer $mailer,
) {
parent::__construct($logger);
}
protected function action(Request $request, array $args): Response
{
// I'd prefer to just inject the baseUri instead of the entire settings, but this seems easier for now.
/** @var array $settings */
$settings = $this->settings->get('accountManagement');
$accountManagementBaseUrl = ($settings['baseUri']);
\assert(is_string($accountManagementBaseUrl));
/** @var array $decoded */
$decoded = json_decode($request->getBody()->__toString(), true, 512, \JSON_THROW_ON_ERROR);
$email = (string) $decoded['email_address'];
$violations = $this->validator->validate($email, constraints: new Email(mode: Email::VALIDATION_MODE_HTML5));
if (count($violations) > 0) {
throw new HttpBadRequestException($request, 'Invalid email address');
}
$person = $this->personRepository->findPasswordEnabledPersonByEmailAddress($email);
if ($person === null) {
// do not let the client know that we didn't find the person.
return new JsonResponse([]);
}
$token = PasswordResetToken::random($person);
$resetLink = $accountManagementBaseUrl . '/reset-password' . "?token=" . urlencode($token->toBase58Secret());
$email = $person->email_address;
if ($email === null) {
throw new \Exception('Missing email address for person ' . ($person->getId() ?? 'null'));
}
$this->mailer->sendEmail([
'templateKey' => 'password-reset-requested',
'recipientEmailAddress' => $email,
'params' => [
'firstName' => $person->getFirstName(),
'lastName' => $person->getLastName(),
'resetLink' => $resetLink,
],
]);
$this->tokenRepository->persist($token);
return new JsonResponse([]);
}
}