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

TokenManager::getToken()   A

Complexity

Conditions 3
Paths 16

Size

Total Lines 30

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 18
CRAP Score 3.009

Importance

Changes 0
Metric Value
dl 0
loc 30
ccs 18
cts 20
cp 0.9
rs 9.44
c 0
b 0
f 0
cc 3
nc 16
nop 3
crap 3.009
1
<?php
2
declare(strict_types=1);
3
4
namespace SlayerBirden\DataFlowServer\Authentication\Service;
5
6
use Doctrine\Common\Collections\ArrayCollection;
7
use Doctrine\Common\Collections\Criteria;
8
use Doctrine\Common\Collections\Selectable;
9
use Doctrine\Common\Persistence\ManagerRegistry;
10
use Doctrine\ORM\ORMException;
11
use Psr\Log\LoggerInterface;
12
use SlayerBirden\DataFlowServer\Authentication\Entities\Grant;
13
use SlayerBirden\DataFlowServer\Authentication\Entities\Token;
14
use SlayerBirden\DataFlowServer\Authentication\Exception\InvalidCredentialsException;
15
use SlayerBirden\DataFlowServer\Authentication\Exception\PermissionDeniedException;
16
use SlayerBirden\DataFlowServer\Authentication\PasswordManagerInterface;
17
use SlayerBirden\DataFlowServer\Authentication\TokenManagerInterface;
18
use SlayerBirden\DataFlowServer\Authorization\PermissionManagerInterface;
19
use SlayerBirden\DataFlowServer\Domain\Entities\User;
20
21
final class TokenManager implements TokenManagerInterface
22
{
23
    /**
24
     * @var PasswordManagerInterface
25
     */
26
    private $passwordManager;
27
    /**
28
     * @var LoggerInterface
29
     */
30
    private $logger;
31
    /**
32
     * @var PermissionManagerInterface
33
     */
34
    private $permissionManager;
35
    /**
36
     * @var ManagerRegistry
37
     */
38
    private $managerRegistry;
39
    /**
40
     * @var Selectable
41
     */
42
    private $userRepository;
43
44 12
    public function __construct(
45
        ManagerRegistry $managerRegistry,
46
        Selectable $userRepository,
47
        PasswordManagerInterface $passwordManager,
48
        LoggerInterface $logger,
49
        PermissionManagerInterface $permissionManager
50
    ) {
51 12
        $this->managerRegistry = $managerRegistry;
52 12
        $this->userRepository = $userRepository;
53 12
        $this->passwordManager = $passwordManager;
54 12
        $this->logger = $logger;
55 12
        $this->permissionManager = $permissionManager;
56 12
    }
57
58
    /**
59
     * @inheritdoc
60
     * @throws \Exception
61
     */
62 3
    public function getToken(string $user, string $password, array $resources): Token
63
    {
64
        try {
65 3
            $em = $this->managerRegistry->getManagerForClass(Token::class);
66 3
            $user = $this->getByUsername($user);
67 3
            if ($this->passwordManager->isValidForUser($password, $user)) {
68 2
                $token = new Token();
69 2
                $now = new \DateTime();
70
                // default token for 1 month
71 2
                $due = (new \DateTime())->add(new \DateInterval('P1M'));
72
73 2
                $token->setActive(true);
74 2
                $token->setCreatedAt($now);
75 2
                $token->setOwner($user);
76 2
                $token->setDue($due);
77 2
                $token->setGrants($this->getGrants($token, $resources, $user));
0 ignored issues
show
Bug introduced by
It seems like $this->getGrants($token, $resources, $user) targeting SlayerBirden\DataFlowSer...kenManager::getGrants() can also be of type array<integer,object<Sla...cation\Entities\Grant>>; however, SlayerBirden\DataFlowSer...ties\Token::setGrants() does only seem to accept object<Doctrine\Common\Collections\Collection>, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
78 1
                $token->setToken($this->generateToken());
79
80 1
                $em->persist($token);
81 1
                $em->flush();
82
83 1
                return $token;
84
            }
85 1
        } catch (ORMException $exception) {
86
            $this->logger->error((string)$exception);
87
            throw new InvalidCredentialsException('Unknown ORM error.', $exception);
88
        }
89
90 1
        throw new InvalidCredentialsException('Could not validate credentials.');
91
    }
92
93 3
    private function getByUsername(string $user): User
94
    {
95 3
        $criteria = Criteria::create();
96 3
        $criteria->where(Criteria::expr()->eq('email', $user));
97 3
        $users = $this->userRepository->matching($criteria);
98
99 3
        if ($users->count()) {
100 3
            return $users->first();
101
        } else {
102
            throw new InvalidCredentialsException('Could not find user by provided email.');
103
        }
104
    }
105
106
    /**
107
     * UUIDv4
108
     * https://stackoverflow.com/a/15875555/927404
109
     *
110
     * @return string
111
     */
112 6
    private function generateToken(): string
113
    {
114 6
        $data = random_bytes(16);
115
116 6
        $data[6] = chr(ord($data[6]) & 0x0f | 0x40);
117 6
        $data[8] = chr(ord($data[8]) & 0x3f | 0x80);
118
119 6
        return vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($data), 4));
120
    }
121
122
    /**
123
     * @param Token $token
124
     * @param array $resources
125
     * @param User $user
126
     * @return ArrayCollection|Grant[]
127
     * @throws PermissionDeniedException
128
     */
129 9
    private function getGrants(Token $token, array $resources, User $user): ArrayCollection
130
    {
131 9
        $grants = [];
132 9
        $em = $this->managerRegistry->getManagerForClass(Token::class);
133 9
        foreach ($resources as $resource) {
134 9
            if (!$this->permissionManager->isAllowed($resource, $user)) {
135 3
                throw new PermissionDeniedException(sprintf('%s is not allowed for provided user.', $resource));
136
            }
137 6
            $grant = new Grant();
138 6
            $grant->setToken($token);
139 6
            $grant->setResource($resource);
140 6
            $em->persist($grant);
141 6
            $grants[] = $grant;
142
        }
143
144 6
        return new ArrayCollection($grants);
145
    }
146
147
    /**
148
     * @param User $user
149
     * @param array $resources
150
     * @return Token
151
     * @throws \Exception
152
     */
153 7
    public function getTmpToken(User $user, array $resources): Token
154
    {
155 7
        $token = new Token();
156 7
        $now = new \DateTime();
157
        // tmp token for 1 hour
158 7
        $due = (new \DateTime())->add(new \DateInterval('PT1H'));
159
160 7
        $token->setActive(true);
161 7
        $token->setCreatedAt($now);
162 7
        $token->setOwner($user);
163 7
        $token->setDue($due);
164 7
        $token->setGrants($this->getGrants($token, $resources, $user));
0 ignored issues
show
Bug introduced by
It seems like $this->getGrants($token, $resources, $user) targeting SlayerBirden\DataFlowSer...kenManager::getGrants() can also be of type array<integer,object<Sla...cation\Entities\Grant>>; however, SlayerBirden\DataFlowSer...ties\Token::setGrants() does only seem to accept object<Doctrine\Common\Collections\Collection>, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
165 5
        $token->setToken($this->generateToken());
166
167 5
        $em = $this->managerRegistry->getManagerForClass(Token::class);
168 5
        $em->persist($token);
169 5
        $em->flush();
170
171 5
        return $token;
172
    }
173
}
174