SecurityProfileFactory   A
last analyzed

Complexity

Total Complexity 18

Size/Duplication

Total Lines 189
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 18
eloc 56
dl 0
loc 189
rs 10
c 0
b 0
f 0

9 Methods

Rating   Name   Duplication   Size   Complexity  
A createDisabledProfile() 0 23 4
A createProfile() 0 14 4
A profile() 0 3 1
A __construct() 0 2 1
A getProviderClass() 0 3 2
A createSecurityProfile() 0 13 1
A createEntryPoint() 0 8 2
A createAuthenticatorManager() 0 10 1
A createStatelessProfile() 0 13 2
1
<?php
2
3
/**
4
 * This file is part of web-stack
5
 *
6
 * For the full copyright and license information, please view the LICENSE
7
 * file that was distributed with this source code.
8
 */
9
10
declare(strict_types=1);
11
12
namespace Slick\WebStack\Domain\Security\Http;
13
14
use Psr\Container\ContainerExceptionInterface;
15
use Psr\Container\NotFoundExceptionInterface;
16
use Psr\Http\Message\ServerRequestInterface;
17
use Slick\Di\ContainerInterface;
18
use Slick\WebStack\Domain\Security\Authentication\Token\TokenValidator\UserIntegrityTokenValidator;
19
use Slick\WebStack\Domain\Security\Http\SecurityProfile\DisabledSecurityProfile;
20
use Slick\WebStack\Domain\Security\Http\SecurityProfile\SecurityProfile;
21
use Slick\WebStack\Domain\Security\Http\SecurityProfile\SecurityProfileTrait;
22
use Slick\WebStack\Domain\Security\Http\SecurityProfile\StatefulSecurityProfile\SessionSecurityProfile;
23
use Slick\WebStack\Domain\Security\User\UserProviderInterface;
24
use Slick\WebStack\Domain\Security\UserInterface;
25
use Slick\WebStack\Infrastructure\Http\Authenticator\Factory\AuthenticatorsListFactory;
26
27
/**
28
 * Class SecurityProfileFactory
29
 */
30
class SecurityProfileFactory
31
{
32
    use SecurityProfileTrait;
33
34
    /**
35
     * @use ProfileFactoryTrait<UserInterface>
36
     */
37
    use ProfileFactoryTrait;
38
39
    protected ?SecurityProfileInterface $profile = null;
40
41
    private ?AuthenticationEntryPointInterface $entryPoint = null;
42
43
    /**
44
     * Creates a SecurityProfileFactory
45
     *
46
     * @param ContainerInterface $container
47
     */
48
    public function __construct(private readonly ContainerInterface $container)
49
    {
50
    }
51
52
    /**
53
     * Create a security profile based on a given profile configuration and request.
54
     *
55
     * @param array<string, mixed> $profileConfig The profile configuration.
56
     * @param ServerRequestInterface $request The server request.
57
     *
58
     * @return SecurityProfileInterface|null The created security profile, or null if no match found.
59
     * @throws ContainerExceptionInterface
60
     * @throws NotFoundExceptionInterface
61
     */
62
    public function createProfile(array $profileConfig, ServerRequestInterface $request): ?SecurityProfileInterface
63
    {
64
        foreach ($profileConfig['profiles'] as $profile) {
65
            if (!$this->matchExp = $profile['pattern'] ?? null) {
66
                continue;
67
            }
68
69
            if ($this->match($request)) {
70
                $this->profile = $this->createDisabledProfile($profile);
71
                return $this->profile;
72
            }
73
        }
74
75
        return null;
76
    }
77
78
    /**
79
     * Retrieves the security profile
80
     *
81
     * @return SecurityProfileInterface|null The security profile if available, null otherwise
82
     */
83
    public function profile(): ?SecurityProfileInterface
84
    {
85
        return $this->profile;
86
    }
87
88
    /**
89
     * Create a disabled security profile based on a given profile.
90
     *
91
     * @param array<string, mixed> $profile The profile configuration.
92
     * @return SecurityProfileInterface The created disabled security profile.
93
     *
94
     * @throws ContainerExceptionInterface
95
     * @throws NotFoundExceptionInterface
96
     */
97
    private function createDisabledProfile(array $profile): SecurityProfileInterface
98
    {
99
        $options = array_merge(self::$defaultOptions, $profile);
100
101
        if ($options['secured'] !== true) {
102
            if (isset($options['stateless']) && !$options['stateless']) {
103
                $tokenValidator = new UserIntegrityTokenValidator(
104
                    $this->createUserProvider(
105
                        $this->getProviderClass($profile)
106
                    )
107
                );
108
109
                return new DisabledSecurityProfile(
110
                    $options['pattern'],
111
                    $this->createTokenStorage($profile),
112
                    $this->createSessionDriver($profile),
113
                    $tokenValidator
114
                );
115
            }
116
            return new DisabledSecurityProfile($options['pattern']);
117
        }
118
119
        return $this->createStatelessProfile($options);
120
    }
121
122
    /**
123
     * Create a stateless security profile based on a given profile modules.
124
     *
125
     * @param array<string, mixed> $profile The profile configuration.
126
     * @return SecurityProfileInterface A stateless security profile, or null if the profile is not stateless.
127
     *
128
     * @throws ContainerExceptionInterface
129
     * @throws NotFoundExceptionInterface
130
     */
131
    private function createStatelessProfile(array $profile): SecurityProfileInterface
132
    {
133
        if ($profile['stateless'] === true) {
134
            return new SecurityProfile(
135
                $profile['pattern'],
136
                $this->createAuthenticatorManager($profile),
137
                $this->createTokenStorage($profile),
138
                $this->createEntryPoint($profile),
139
                $profile['accessControl']
140
            );
141
        }
142
143
        return $this->createSecurityProfile($profile);
144
    }
145
146
    /**
147
     * Create a security profile based on a given profile configuration.
148
     *
149
     * @param array<string, mixed> $profile The profile configuration.
150
     * @return SecurityProfileInterface The created security profile.
151
     *
152
     * @throws ContainerExceptionInterface
153
     * @throws NotFoundExceptionInterface
154
     */
155
    private function createSecurityProfile(array $profile): SecurityProfileInterface
156
    {
157
        $tokenValidator = new UserIntegrityTokenValidator(
158
            $this->createUserProvider($this->getProviderClass($profile))
159
        );
160
        return new SessionSecurityProfile(
161
            $profile['pattern'],
162
            $this->createAuthenticatorManager($profile),
163
            $this->createTokenStorage($profile),
164
            $this->createSessionDriver($profile),
165
            $this->createEntryPoint($profile),
166
            $tokenValidator,
167
            $profile['accessControl']
168
        );
169
    }
170
171
    /**
172
     * Creates an authenticator manager instance based on a given profile.
173
     *
174
     * @param array<string, mixed> $profile The profile configuration
175
     *
176
     * @return AuthenticatorManagerInterface The authenticator manager instance
177
     *
178
     * @throws ContainerExceptionInterface
179
     * @throws NotFoundExceptionInterface
180
     */
181
    private function createAuthenticatorManager(array $profile): AuthenticatorManagerInterface
182
    {
183
        $authenticators = new AuthenticatorsListFactory($this->container, $profile['authenticators']);
184
        $this->entryPoint = $authenticators->entryPoint();
185
        $args = [
186
            'authenticators' => $authenticators,
187
            'tokenStorage' => $this->createTokenStorage($profile),
188
            'hasher' => $this->createPasswordHasher($profile)
189
        ];
190
        return $this->container->make(AuthenticatorManager::class, ...array_values($args));
191
    }
192
193
    /**
194
     * Create an authentication entry point based on a given profile.
195
     *
196
     * @param array<string, mixed> $profile The profile configuration.
197
     * @return AuthenticationEntryPointInterface|null The created authentication entry point or null if
198
     *                                                no entry point is specified in the profile.
199
     * @throws ContainerExceptionInterface
200
     * @throws NotFoundExceptionInterface
201
     */
202
    private function createEntryPoint(array $profile): ?AuthenticationEntryPointInterface
203
    {
204
        $config = array_merge(self::$defaultOptions, $profile);
205
        if ($config['entryPoint'] !== null) {
206
            return $this->container->get($config['entryPoint']);
207
        }
208
209
        return $this->entryPoint;
210
    }
211
212
    /**
213
     * @param array<string, mixed> $profile
214
     * @return string
215
     */
216
    private function getProviderClass(array $profile): string
217
    {
218
        return isset($profile['userProvider']) ? $profile['userProvider'] : UserProviderInterface::class;
219
    }
220
}
221