Completed
Push — master ( 298ac7...0024da )
by Oleg
12:58
created

CreatePasswordAction   A

Complexity

Total Complexity 10

Size/Duplication

Total Lines 122
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 13

Test Coverage

Coverage 84%

Importance

Changes 0
Metric Value
wmc 10
lcom 1
cbo 13
dl 0
loc 122
ccs 42
cts 50
cp 0.84
rs 10
c 0
b 0
f 0

5 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 13 1
A process() 0 21 3
A validateUser() 0 11 2
B createPassword() 0 30 3
A hasActivePassword() 0 12 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\ORM\EntityManager;
8
use Doctrine\ORM\ORMException;
9
use Doctrine\ORM\ORMInvalidArgumentException;
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\Middleware\TokenMiddleware;
17
use SlayerBirden\DataFlowServer\Domain\Entities\User;
18
use SlayerBirden\DataFlowServer\Notification\DangerMessage;
19
use SlayerBirden\DataFlowServer\Notification\SuccessMessage;
20
use SlayerBirden\DataFlowServer\Stdlib\Validation\ValidationResponseFactory;
21
use Zend\Diactoros\Response\JsonResponse;
22
use Zend\Hydrator\ExtractionInterface;
23
use Zend\Hydrator\HydrationInterface;
24
use Zend\InputFilter\InputFilterInterface;
25
26
class CreatePasswordAction implements MiddlewareInterface
27
{
28
    /**
29
     * @var EntityManager
30
     */
31
    private $entityManager;
32
    /**
33
     * @var InputFilterInterface
34
     */
35
    private $inputFilter;
36
    /**
37
     * @var LoggerInterface
38
     */
39
    private $logger;
40
    /**
41
     * @var ExtractionInterface
42
     */
43
    private $extraction;
44
    /**
45
     * @var HydrationInterface
46
     */
47
    private $hydration;
48
49 4
    public function __construct(
50
        EntityManager $entityManager,
0 ignored issues
show
Bug introduced by
You have injected the EntityManager via parameter $entityManager. This is generally not recommended as it might get closed and become unusable. Instead, it is recommended to inject the ManagerRegistry and retrieve the EntityManager via getManager() each time you need it.

The EntityManager might become unusable for example if a transaction is rolled back and it gets closed. Let’s assume that somewhere in your application, or in a third-party library, there is code such as the following:

function someFunction(ManagerRegistry $registry) {
    $em = $registry->getManager();
    $em->getConnection()->beginTransaction();
    try {
        // Do something.
        $em->getConnection()->commit();
    } catch (\Exception $ex) {
        $em->getConnection()->rollback();
        $em->close();

        throw $ex;
    }
}

If that code throws an exception and the EntityManager is closed. Any other code which depends on the same instance of the EntityManager during this request will fail.

On the other hand, if you instead inject the ManagerRegistry, the getManager() method guarantees that you will always get a usable manager instance.

Loading history...
51
        InputFilterInterface $inputFilter,
52
        LoggerInterface $logger,
53
        ExtractionInterface $extraction,
54
        HydrationInterface $hydration
55
    ) {
56 4
        $this->entityManager = $entityManager;
57 4
        $this->inputFilter = $inputFilter;
58 4
        $this->logger = $logger;
59 4
        $this->extraction = $extraction;
60 4
        $this->hydration = $hydration;
61 4
    }
62
63
    /**
64
     * @inheritdoc
65
     */
66 4
    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
67
    {
68
        try {
69 4
            $this->validateUser($request);
70 1
        } catch (\DomainException $exception) {
71 1
            return new JsonResponse([
72 1
                'data' => [],
73
                'success' => false,
74 1
                'msg' => new DangerMessage($exception->getMessage()),
75 1
            ], $exception->getCode());
76
        }
77
78 3
        $data = $request->getParsedBody();
79 3
        $this->inputFilter->setData($data);
0 ignored issues
show
Bug introduced by
It seems like $data defined by $request->getParsedBody() on line 78 can also be of type null or object; however, Zend\InputFilter\InputFilterInterface::setData() does only seem to accept array|object<Traversable>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
80
81 3
        if ($this->inputFilter->isValid()) {
82 1
            return $this->createPassword($data);
0 ignored issues
show
Bug introduced by
It seems like $data defined by $request->getParsedBody() on line 78 can also be of type null or object; however, SlayerBirden\DataFlowSer...ction::createPassword() does only seem to accept array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
83
        } else {
84 2
            return (new ValidationResponseFactory())('password', $this->inputFilter);
85
        }
86
    }
87
88
    /**
89
     * @param ServerRequestInterface $request
90
     * @throws \DomainException
91
     */
92 4
    private function validateUser(ServerRequestInterface $request): void
93
    {
94 4
        $user = $request->getAttribute(TokenMiddleware::USER_PARAM);
95
96 4
        if ($this->hasActivePassword($user)) {
97 1
            throw new \DomainException(
98 1
                'You already have active password. Please use "update" routine.',
99 1
                412
100
            );
101
        }
102 3
    }
103
104 1
    private function createPassword(array $data): ResponseInterface
105
    {
106
        try {
107 1
            $password = $this->hydration->hydrate($data, new Password());
108 1
            $this->entityManager->persist($password);
109 1
            $this->entityManager->flush();
110 1
            return new JsonResponse([
111 1
                'msg' => new SuccessMessage('Password has been successfully created!'),
112
                'success' => true,
113
                'data' => [
114
                    'validation' => [],
115 1
                    'password' => $this->extraction->extract($password),
116
                ]
117 1
            ], 200);
118
        } catch (ORMInvalidArgumentException | \InvalidArgumentException $exception) {
119
            $message = new DangerMessage($exception->getMessage());
120
        } catch (ORMException $exception) {
121
            $this->logger->error((string)$exception);
122
            $message = new DangerMessage('There was an error creating password.');
123
        }
124
125
        return new JsonResponse([
126
            'msg' => $message,
127
            'success' => false,
128
            'data' => [
129
                'validation' => [],
130
                'password' => null,
131
            ]
132
        ], 400);
133
    }
134
135 4
    private function hasActivePassword(User $user): bool
136
    {
137 4
        $collection = $this->entityManager
138 4
            ->getRepository(Password::class)
139 4
            ->matching(
140 4
                Criteria::create()
141 4
                    ->where(Criteria::expr()->eq('owner', $user))
142 4
                    ->andWhere(Criteria::expr()->eq('active', true))
143
            );
144
145 4
        return !$collection->isEmpty();
146
    }
147
}
148