Completed
Push — master ( 5c0b6f...5c71f2 )
by Oleg
10:16
created

UpdatePasswordAction::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 15

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 15
ccs 8
cts 8
cp 1
rs 9.7666
c 0
b 0
f 0
cc 1
nc 1
nop 6
crap 1
1
<?php
2
declare(strict_types=1);
3
4
namespace SlayerBirden\DataFlowServer\Authentication\Controller;
5
6
use Doctrine\Common\Collections\Criteria;
7
use Doctrine\Common\Collections\Selectable;
8
use Doctrine\Common\Persistence\ManagerRegistry;
9
use Doctrine\ORM\EntityManagerInterface;
10
use Psr\Http\Message\ResponseInterface;
11
use Psr\Http\Message\ServerRequestInterface;
12
use Psr\Http\Server\MiddlewareInterface;
13
use Psr\Http\Server\RequestHandlerInterface;
14
use Psr\Log\LoggerInterface;
15
use SlayerBirden\DataFlowServer\Authentication\Entities\Password;
16
use SlayerBirden\DataFlowServer\Authentication\Exception\PasswordRestrictionsException;
17
use SlayerBirden\DataFlowServer\Authentication\PasswordManagerInterface;
18
use SlayerBirden\DataFlowServer\Domain\Entities\ClaimedResourceInterface;
19
use SlayerBirden\DataFlowServer\Notification\DangerMessage;
20
use SlayerBirden\DataFlowServer\Notification\SuccessMessage;
21
use SlayerBirden\DataFlowServer\Stdlib\Validation\DataValidationResponseFactory;
22
use SlayerBirden\DataFlowServer\Stdlib\Validation\ValidationResponseFactory;
23
use Zend\Diactoros\Response\JsonResponse;
24
use Zend\Hydrator\HydratorInterface;
25
use Zend\InputFilter\InputFilterInterface;
26
27
final class UpdatePasswordAction implements MiddlewareInterface
28
{
29
    /**
30
     * @var LoggerInterface
31
     */
32
    private $logger;
33
    /**
34
     * @var PasswordManagerInterface
35
     */
36
    private $passwordManager;
37
    /**
38
     * @var InputFilterInterface
39
     */
40
    private $inputFilter;
41
    /**
42
     * @var HydratorInterface
43
     */
44
    private $hydrator;
45
    /**
46
     * @var ManagerRegistry
47
     */
48
    private $managerRegistry;
49
    /**
50
     * @var Selectable
51
     */
52
    private $passwordRepository;
53
54 3
    public function __construct(
55
        ManagerRegistry $managerRegistry,
56
        Selectable $passwordRepository,
57
        InputFilterInterface $inputFilter,
58
        LoggerInterface $logger,
59
        PasswordManagerInterface $passwordManager,
60
        HydratorInterface $hydrator
61
    ) {
62 3
        $this->managerRegistry = $managerRegistry;
63 3
        $this->passwordRepository = $passwordRepository;
64 3
        $this->logger = $logger;
65 3
        $this->passwordManager = $passwordManager;
66 3
        $this->inputFilter = $inputFilter;
67 3
        $this->hydrator = $hydrator;
68 3
    }
69
70
    /**
71
     * @inheritdoc
72
     */
73 3
    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
74
    {
75 3
        $data = $request->getParsedBody();
76 3
        if (!is_array($data)) {
77
            return (new DataValidationResponseFactory())('password');
78
        }
79 3
        $this->inputFilter->setData($data);
80
81 3
        if (!$this->inputFilter->isValid()) {
82 1
            return (new ValidationResponseFactory())('password', $this->inputFilter);
83
        }
84 2
        $em = $this->managerRegistry->getManagerForClass(Password::class);
85 2
        if ($em === null) {
86
            return new JsonResponse([
87
                'msg' => new DangerMessage('Could not retrieve ObjectManager'),
88
                'success' => false,
89
                'data' => [
90
                    'token' => null,
91
                ]
92
            ], 500);
93
        }
94 2
        if (!($em instanceof EntityManagerInterface)) {
95
            return new JsonResponse([
96
                'msg' => new DangerMessage('Can not use current ObjectManager'),
97
                'success' => false,
98
                'data' => [
99
                    'token' => null,
100
                ]
101
            ], 500);
102
        }
103 2
        $em->beginTransaction();
104
        try {
105 2
            $pw = $this->updatePassword($data);
106 1
            $em->flush();
107 1
            $em->commit();
108
109 1
            return new JsonResponse([
110 1
                'data' => [
111 1
                    'password' => $this->hydrator->extract($pw),
112
                    'validation' => [],
113
                ],
114
                'success' => true,
115 1
                'msg' => new SuccessMessage('Password successfully updated.'),
116 1
            ], 200);
117 1
        } catch (PasswordRestrictionsException | \InvalidArgumentException $exception) {
118 1
            $em->rollback();
119 1
            return new JsonResponse([
120 1
                'data' => [
121
                    'password' => null,
122
                    'validation' => [],
123
                ],
124
                'success' => false,
125 1
                'msg' => new DangerMessage($exception->getMessage()),
126 1
            ], 400);
127
        } catch (\Exception $exception) {
128
            $this->logger->error((string)$exception);
129
            $em->rollback();
130
            return new JsonResponse([
131
                'data' => [
132
                    'password' => null,
133
                    'validation' => [],
134
                ],
135
                'success' => false,
136
                'msg' => new DangerMessage('There was an error while updating password.'),
137
            ], 500);
138
        }
139
    }
140
141
    /**
142
     * @param array $data
143
     * @return Password
144
     * @throws PasswordRestrictionsException
145
     * @throws \Exception
146
     */
147 2
    private function updatePassword(array $data): Password
148
    {
149 2
        $userPasswords = $this->passwordRepository->matching(
150 2
            Criteria::create()->where(
151 2
                Criteria::expr()->eq('owner', $data[ClaimedResourceInterface::OWNER_PARAM])
152
            )
153
        );
154
        # check if mentioned
155 2
        $em = $this->managerRegistry->getManagerForClass(Password::class);
156
        /** @var Password $item */
157 2
        foreach ($userPasswords as $item) {
158 2
            if (password_verify($data['new_password'], $item->getHash())) {
159 1
                throw new PasswordRestrictionsException(
160 1
                    'You have already used this password before. Please use a new one.'
161
                );
162
            }
163 2
            if ($item->isActive()) {
164 2
                $item->setActive(false);
165 2
                $em->persist($item);
166
            }
167
        }
168
169
        /** @var Password $password */
170 1
        $data['password'] = $data['new_password'];
171 1
        unset($data['new_password']);
172 1
        $data['created_at'] = (new \DateTime())->format(\DateTime::RFC3339);
173 1
        $data['due'] = (new \DateTime())->add(new \DateInterval('P1Y'))->format(\DateTime::RFC3339);
174 1
        $data['active'] = $data['active'] ?? true;
175
176 1
        $password = $this->hydrator->hydrate($data, new Password());
177 1
        $em->persist($password);
178
179 1
        return $password;
180
    }
181
}
182