getFlowPreferencesFromSamlAttributes()   A
last analyzed

Complexity

Conditions 5
Paths 6

Size

Total Lines 22
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 13
nc 6
nop 0
dl 0
loc 22
rs 9.5222
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types = 1);
4
5
/**
6
 * Copyright 2022 SURFnet bv
7
 *
8
 * Licensed under the Apache License, Version 2.0 (the "License");
9
 * you may not use this file except in compliance with the License.
10
 * You may obtain a copy of the License at
11
 *
12
 *     http://www.apache.org/licenses/LICENSE-2.0
13
 *
14
 * Unless required by applicable law or agreed to in writing, software
15
 * distributed under the License is distributed on an "AS IS" BASIS,
16
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17
 * See the License for the specific language governing permissions and
18
 * limitations under the License.
19
 */
0 ignored issues
show
Coding Style introduced by
PHP version not specified
Loading history...
Coding Style introduced by
Missing @category tag in file comment
Loading history...
Coding Style introduced by
Missing @package tag in file comment
Loading history...
Coding Style introduced by
Missing @author tag in file comment
Loading history...
Coding Style introduced by
Missing @license tag in file comment
Loading history...
Coding Style introduced by
Missing @link tag in file comment
Loading history...
20
21
namespace Surfnet\StepupSelfService\SelfServiceBundle\Service;
22
23
use Psr\Log\LoggerInterface;
24
use Surfnet\StepupSelfService\SelfServiceBundle\Exception\InvalidArgumentException;
25
use Surfnet\StepupSelfService\SelfServiceBundle\Exception\LogicException;
26
use Surfnet\StepupSelfService\SelfServiceBundle\Security\Authentication\AuthenticatedSessionStateHandler;
27
use Surfnet\StepupSelfService\SelfServiceBundle\Value\ActivationFlowPreference;
28
use Surfnet\StepupSelfService\SelfServiceBundle\Value\ActivationFlowPreferenceInterface;
29
use Surfnet\StepupSelfService\SelfServiceBundle\Value\ActivationFlowPreferenceNotExpressed;
30
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
31
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
32
33
class ActivationFlowService
0 ignored issues
show
Coding Style introduced by
Missing doc comment for class ActivationFlowService
Loading history...
34
{
35
    /**
0 ignored issues
show
Coding Style introduced by
Parameter $logger should have a doc-comment as per coding-style.
Loading history...
Coding Style introduced by
Parameter $fieldName should have a doc-comment as per coding-style.
Loading history...
Coding Style introduced by
Parameter $sessionState should have a doc-comment as per coding-style.
Loading history...
Coding Style introduced by
Parameter $tokenStorage should have a doc-comment as per coding-style.
Loading history...
Coding Style introduced by
Parameter $attributeName should have a doc-comment as per coding-style.
Loading history...
36
     * Handle preferred activation flow logic
37
     *
38
     * 1. On the / path, the preferred activation flow can be set using the configured
39
     *    query string field. This field can have an option indicating the preferred flow.
40
     *    This is stored in session.
41
     * 2. If this preference was set, the list of available options is limited to just that
42
     *    option.
43
     * 3. The option can be reset by the Identity, removing it from session.
44
     *
45
     * Note that:
46
     * - fieldName and options are configured in the SelfServiceExtension
47
     *
48
     * @param string[] $options
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Expected 14 spaces after parameter type; 1 found
Loading history...
Coding Style introduced by
Doc comment for parameter $options does not match actual variable name $sessionState
Loading history...
49
     * @param array<string, string> $attributes
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Doc comment for parameter $attributes does not match actual variable name $tokenStorage
Loading history...
50
     */
51
    public function __construct(
52
        private readonly AuthenticatedSessionStateHandler $sessionState,
53
        private readonly TokenStorageInterface $tokenStorage,
54
        private readonly LoggerInterface $logger,
55
        private readonly string $fieldName,
56
        private readonly array $options,
57
        private readonly string $attributeName,
58
        private readonly array $attributes
59
    ) {
60
    }
61
62
    public function processPreferenceFromUri(string $uri): void
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function processPreferenceFromUri()
Loading history...
63
    {
64
        $requestedActivationPreference = $this->getFlowPreferenceFromUri($uri);
65
        if ($requestedActivationPreference instanceof ActivationFlowPreferenceNotExpressed) {
66
            return;
67
        }
68
69
        $this->logger->info('Storing the preference in session');
70
        $this->sessionState->setRequestedActivationFlowPreference($requestedActivationPreference);
71
    }
72
73
    public function getPreference(): ActivationFlowPreferenceInterface
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function getPreference()
Loading history...
74
    {
75
        $requestedActivationPreference = $this->sessionState->getRequestedActivationFlowPreference();
76
        $availableActivationPreferences = $this->getFlowPreferencesFromSamlAttributes();
77
78
        if (count($availableActivationPreferences) == 0) {
79
            $this->logger->info('No entitlement attributes found to determine the allowed flow, allowing all flows');
80
            $availableActivationPreferences = [
81
                ActivationFlowPreference::createSelf(),
82
                ActivationFlowPreference::createRa(),
83
            ];
84
        }
85
86
        if ($requestedActivationPreference instanceof ActivationFlowPreferenceNotExpressed && count($availableActivationPreferences) === 1) {
87
            $this->logger->info('Only one activation flow allowed');
88
            return $availableActivationPreferences[0];
89
        }
90
91
        if (in_array($requestedActivationPreference, $availableActivationPreferences)) {
92
            $this->logger->info('Found allowed activation flow');
93
            return $requestedActivationPreference;
94
        }
95
96
        $this->logger->info('Not found allowed activation flow');
97
98
        return new ActivationFlowPreferenceNotExpressed();
99
    }
100
101
    private function getFlowPreferenceFromUri(string $uri): ActivationFlowPreferenceInterface
0 ignored issues
show
Coding Style introduced by
Private method name "ActivationFlowService::getFlowPreferenceFromUri" must be prefixed with an underscore
Loading history...
Coding Style introduced by
Missing doc comment for function getFlowPreferenceFromUri()
Loading history...
102
    {
103
        $this->logger->info(sprintf('Analysing uri "%s" for activation flow query parameter', $uri));
104
105
        $parts = parse_url($uri);
106
107
        $parameters = [];
108
        if (array_key_exists('query', $parts)) {
109
            $this->logger->debug('Found a query string in the uri');
110
            parse_str($parts['query'], $parameters);
111
        }
112
113
        // Is the configured field name in the querystring?
114
        if (!array_key_exists($this->fieldName, $parameters)) {
115
            $this->logger->notice(
116
                sprintf(
117
                    'The configured query string field name "%s" was not found in the uri "%s"',
118
                    $this->fieldName,
119
                    $uri
120
                )
121
            );
122
            return new ActivationFlowPreferenceNotExpressed();
123
        }
124
125
        try {
126
            $option = $parameters[$this->fieldName];
127
            $option = is_string($option) ? $option : "";
128
            return ActivationFlowPreference::fromString($option);
129
        } catch (InvalidArgumentException $e) {
130
            $this->logger->notice(
131
                sprintf(
132
                    'Field "%s" contained an invalid option "%s", must be one of: %s',
133
                    $this->fieldName,
134
                    $parameters[$this->fieldName],
135
                    implode(', ', $this->options)
136
                )
137
            );
138
            return new ActivationFlowPreferenceNotExpressed();
139
        }
140
    }
141
142
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
143
     * @return ActivationFlowPreferenceInterface[]
144
     */
145
    private function getFlowPreferencesFromSamlAttributes(): array
0 ignored issues
show
Coding Style introduced by
Private method name "ActivationFlowService::getFlowPreferencesFromSamlAttributes" must be prefixed with an underscore
Loading history...
146
    {
147
        $this->logger->info('Analysing saml entitlement attributes for allowed activation flows');
148
149
        $token = $this->tokenStorage->getToken();
150
        if (!$token instanceof TokenInterface) {
151
            throw new LogicException("A authentication token should be set at this point");
152
        }
153
154
        $activationFlows = [];
155
        $attributes = $token->getAttributes();
156
157
        if (array_key_exists($this->attributeName, $attributes)) {
158
            $this->logger->debug('Found entitlement saml attributes');
159
            if (in_array($this->attributes['ra'], $attributes[$this->attributeName])) {
160
                $activationFlows[] = ActivationFlowPreference::createRa();
161
            }
162
            if (in_array($this->attributes['self'], $attributes[$this->attributeName])) {
163
                $activationFlows[] = ActivationFlowPreference::createSelf();
164
            }
165
        }
166
        return $activationFlows;
167
    }
168
}
169