Completed
Push — master ( 3eb757...3bc7c8 )
by Oleg
03:46
created

UpdatePasswordAction::process()   C

Complexity

Conditions 12
Paths 39

Size

Total Lines 40

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 17
CRAP Score 20.7338

Importance

Changes 0
Metric Value
dl 0
loc 40
ccs 17
cts 28
cp 0.6071
rs 6.9666
c 0
b 0
f 0
cc 12
nc 39
nop 2
crap 20.7338

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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