AuthenticationProvider::getUser()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
eloc 3
nc 2
nop 0
dl 0
loc 7
ccs 4
cts 4
cp 1
crap 2
rs 10
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Uxmp\Core\Component\SubSonic;
6
7
use Usox\HyperSonic\Authentication\Exception\AuthenticationFailedException;
8
use Uxmp\Core\Component\Authentication\AccessKey\AccessKeyTypeEnum;
9
use Uxmp\Core\Orm\Model\UserInterface;
10
use Uxmp\Core\Orm\Repository\AccessKeyRepositoryInterface;
11
use Uxmp\Core\Orm\Repository\UserRepositoryInterface;
12
13
/**
14
 * Provides auth mechanism for subsonic api auth
15
 */
16
final class AuthenticationProvider implements HypersonicAuthenticationProviderInterface
17
{
18
    /**
19
     * @var string
20
     */
21
    public const CONFIG_KEY_TOKEN = 'accessToken';
22
23
    /**
24
     * @var int
25
     */
26
    public const SUBSONIC_KEY_LENGTH = 10;
27
28
    private ?UserInterface $user = null;
29
30 7
    public function __construct(
31
        private readonly UserRepositoryInterface $userRepository,
32
        private readonly AccessKeyRepositoryInterface $accessKeyRepository,
33
    ) {
34 7
    }
35
36
    /**
37
     * Preferred way of auth, using a hashed version of the access token
38
     *
39
     * @throws AuthenticationFailedException
40
     */
41 3
    public function authByToken(
42
        string $userName,
43
        string $token,
44
        string $salt,
45
    ): void {
46 3
        $user = $this->retrieveUser($userName);
47
48 2
        $accessKeyFromStorage = $this->retrieveToken($user);
49
50
        // use the provided salt to hash the token
51
        if (
52 2
            $accessKeyFromStorage === null ||
53 2
            $token !== md5($accessKeyFromStorage.$salt)
54
        ) {
55 1
            throw new AuthenticationFailedException();
56
        }
57
58 1
        $this->user = $user;
59
    }
60
61
    /**
62
     * Wah! Support for plaintext/hex encoded auth :(
63
     */
64 3
    public function authByPassword(
65
        string $userName,
66
        string $password,
67
    ): void {
68 3
        $user = $this->retrieveUser($userName);
69
70 2
        $accessKeyFromStorage = $this->retrieveToken($user);
71
72
        if (
73 2
            $accessKeyFromStorage === null ||
74 2
            $password !== $accessKeyFromStorage
75
        ) {
76 1
            throw new AuthenticationFailedException();
77
        }
78
79 1
        $this->user = $user;
80
    }
81
82 2
    public function getUser(): UserInterface
83
    {
84 2
        if ($this->user === null) {
85 1
            throw new AuthenticationFailedException();
86
        }
87
88 1
        return $this->user;
89
    }
90
91 4
    private function retrieveToken(
92
        UserInterface $user
93
    ): ?string {
94
        // search for a valid subsonic token
95 4
        $accessKey = $this->accessKeyRepository->findOneBy([
96 4
            'user' => $user,
97 4
            'type_id' => AccessKeyTypeEnum::SUBSONIC,
98 4
            'active' => true,
99 4
        ]);
100
101 4
        return $accessKey?->getConfig()[self::CONFIG_KEY_TOKEN] ?? null;
102
    }
103
104 6
    private function retrieveUser(string $userName): UserInterface
105
    {
106 6
        $user = $this->userRepository->findOneBy(['name' => $userName]);
107 6
        if ($user === null) {
108 2
            throw new AuthenticationFailedException();
109
        }
110
111 4
        return $user;
112
    }
113
}
114