UpdatePasswordAction   A
last analyzed

Complexity

Total Complexity 9

Size/Duplication

Total Lines 115
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 13

Test Coverage

Coverage 91.3%

Importance

Changes 0
Metric Value
wmc 9
lcom 1
cbo 13
dl 0
loc 115
ccs 42
cts 46
cp 0.913
rs 10
c 0
b 0
f 0

3 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 15 1
A process() 0 26 4
A updatePassword() 0 33 4
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\ORM\ORMException;
9
use Psr\Http\Message\ResponseInterface;
10
use Psr\Http\Message\ServerRequestInterface;
11
use Psr\Http\Server\MiddlewareInterface;
12
use Psr\Http\Server\RequestHandlerInterface;
13
use Psr\Log\LoggerInterface;
14
use SlayerBirden\DataFlowServer\Authentication\Entities\Password;
15
use SlayerBirden\DataFlowServer\Authentication\Exception\PasswordRestrictionsException;
16
use SlayerBirden\DataFlowServer\Authentication\PasswordManagerInterface;
17
use SlayerBirden\DataFlowServer\Doctrine\Persistence\EntityManagerRegistry;
18
use SlayerBirden\DataFlowServer\Domain\Entities\ClaimedResourceInterface;
19
use SlayerBirden\DataFlowServer\Stdlib\Request\Parser;
20
use SlayerBirden\DataFlowServer\Stdlib\ResponseFactory;
21
use SlayerBirden\DataFlowServer\Stdlib\Validation\ValidationResponseFactory;
22
use Zend\Hydrator\HydratorInterface;
23
use Zend\InputFilter\InputFilterInterface;
24
25
final class UpdatePasswordAction implements MiddlewareInterface
26
{
27
    /**
28
     * @var LoggerInterface
29
     */
30
    private $logger;
31
    /**
32
     * @var PasswordManagerInterface
33
     */
34
    private $passwordManager;
35
    /**
36
     * @var InputFilterInterface
37
     */
38
    private $inputFilter;
39
    /**
40
     * @var HydratorInterface
41
     */
42
    private $hydrator;
43
    /**
44
     * @var EntityManagerRegistry
45
     */
46
    private $managerRegistry;
47
    /**
48
     * @var Selectable
49
     */
50
    private $passwordRepository;
51
52 6
    public function __construct(
53
        EntityManagerRegistry $managerRegistry,
54
        Selectable $passwordRepository,
55
        InputFilterInterface $inputFilter,
56
        LoggerInterface $logger,
57
        PasswordManagerInterface $passwordManager,
58
        HydratorInterface $hydrator
59
    ) {
60 6
        $this->managerRegistry = $managerRegistry;
61 6
        $this->passwordRepository = $passwordRepository;
62 6
        $this->logger = $logger;
63 6
        $this->passwordManager = $passwordManager;
64 6
        $this->inputFilter = $inputFilter;
65 6
        $this->hydrator = $hydrator;
66 6
    }
67
68
    /**
69
     * @inheritdoc
70
     * @throws ORMException
71
     * @throws \Exception
72
     */
73 6
    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
74
    {
75 6
        $data = Parser::getRequestBody($request);
76 6
        $this->inputFilter->setData($data);
77
78 6
        if (!$this->inputFilter->isValid()) {
79 2
            return (new ValidationResponseFactory())('password', $this->inputFilter);
80
        }
81 4
        $em = $this->managerRegistry->getManagerForClass(Password::class);
82 4
        $em->beginTransaction();
83
        try {
84 4
            $pw = $this->updatePassword($data);
85 2
            $em->flush();
86 2
            $em->commit();
87
88 2
            $msg = 'Password successfully updated.';
89 2
            return (new ResponseFactory())($msg, 200, 'password', $this->hydrator->extract($pw));
90 2
        } catch (PasswordRestrictionsException | \InvalidArgumentException $exception) {
91 2
            $em->rollback();
92 2
            return (new ResponseFactory())($exception->getMessage(), 400, 'password');
93
        } catch (ORMException $exception) {
94
            $this->logger->error((string)$exception);
95
            $em->rollback();
96
            return (new ResponseFactory())('There was an error while updating password.', 400, 'password');
97
        }
98
    }
99
100
    /**
101
     * @param array $data
102
     * @return Password
103
     * @throws PasswordRestrictionsException
104
     * @throws \Exception
105
     */
106 4
    private function updatePassword(array $data): Password
107
    {
108 4
        $userPasswords = $this->passwordRepository->matching(
109 4
            Criteria::create()->where(
110 4
                Criteria::expr()->eq('owner', $data[ClaimedResourceInterface::OWNER_PARAM])
111
            )
112
        );
113
        # check if mentioned
114 4
        $em = $this->managerRegistry->getManagerForClass(Password::class);
115
        /** @var Password $item */
116 4
        foreach ($userPasswords as $item) {
117 4
            if (password_verify($data['new_password'], $item->getHash())) {
118 2
                throw new PasswordRestrictionsException(
119 2
                    'You have already used this password before. Please use a new one.'
120
                );
121
            }
122 4
            if ($item->isActive()) {
123 4
                $item->setActive(false);
124 4
                $em->persist($item);
125
            }
126
        }
127
128
        /** @var Password $password */
129 2
        $data['password'] = $data['new_password'];
130 2
        $data['created_at'] = (new \DateTime())->format(\DateTime::RFC3339);
131 2
        $data['due'] = (new \DateTime())->add(new \DateInterval('P1Y'))->format(\DateTime::RFC3339);
132 2
        $data['active'] = $data['active'] ?? true;
133
134 2
        $password = $this->hydrator->hydrate($data, new Password());
135 2
        $em->persist($password);
136
137 2
        return $password;
138
    }
139
}
140