Passed
Pull Request — main (#336)
by
unknown
13:19 queued 07:13
created

ActivationFlowService::process()   A

Complexity

Conditions 4
Paths 6

Size

Total Lines 39
Code Lines 26

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 4
eloc 26
c 2
b 0
f 0
nc 6
nop 1
dl 0
loc 39
rs 9.504

2 Methods

Rating   Name   Duplication   Size   Complexity  
A ActivationFlowService::processPreferenceFromUri() 0 9 2
A ActivationFlowService::getPreference() 0 20 3
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\LogicException;
25
use Surfnet\StepupSelfService\SelfServiceBundle\Security\Authentication\AuthenticatedSessionStateHandler;
26
use Surfnet\StepupSelfService\SelfServiceBundle\Value\ActivationFlowPreference;
27
use Surfnet\StepupSelfService\SelfServiceBundle\Value\ActivationFlowPreferenceInterface;
28
use Surfnet\StepupSelfService\SelfServiceBundle\Value\ActivationFlowPreferenceNotExpressed;
29
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
30
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
31
32
class ActivationFlowService
0 ignored issues
show
Coding Style introduced by
Missing doc comment for class ActivationFlowService
Loading history...
33
{
34
    private const ACTIVATION_FLOW_ENTITLEMENT_SAML_ATTRIBUTE = 'urn:mace:dir:attribute-def:eduPersonEntitlement';
35
36
    /**
0 ignored issues
show
Coding Style introduced by
Parameter $tokenStorage should have a doc-comment as per coding-style.
Loading history...
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...
37
     * Handle preferred activation flow logic
38
     *
39
     * 1. On the / path, the preferred activation flow can be set using the configured
40
     *    query string field. This field can have an option indicating the preferred flow.
41
     *    This is stored in session.
42
     * 2. If this preference was set, the list of available options is limited to just that
43
     *    option.
44
     * 3. The option can be reset by the Identity, removing it from session.
45
     *
46
     * Note that:
47
     * - fieldName and options are configured in the SelfServiceExtension
48
     *
49
     * @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...
50
     * @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...
51
     */
52
    public function __construct(
53
        private readonly AuthenticatedSessionStateHandler $sessionState,
54
        private readonly TokenStorageInterface $tokenStorage,
55
        private readonly LoggerInterface $logger,
56
        private readonly string $fieldName,
57
        private readonly array $options,
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
                'ra', 'self'
82
            ];
83
        }
84
85
        if (in_array($requestedActivationPreference, $availableActivationPreferences)) {
86
            $this->logger->info('Found allowed activation flow');
87
            return $requestedActivationPreference;
88
        }
89
90
        $this->logger->info('Not found allowed activation flow');
91
92
        return new ActivationFlowPreferenceNotExpressed();
93
    }
94
95
    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...
96
    {
97
        $this->logger->info(sprintf('Analysing uri "%s" for activation flow query parameter', $uri));
98
99
        $parts = parse_url($uri);
100
        /** @var array<string, string> $parameters */
0 ignored issues
show
Coding Style introduced by
The open comment tag must be the only content on the line
Loading history...
Coding Style introduced by
Missing short description in doc comment
Loading history...
Coding Style introduced by
The close comment tag must be the only content on the line
Loading history...
101
        $parameters = [];
102
103
        if (array_key_exists('query', $parts)) {
104
            $this->logger->debug('Found a query string in the uri');
105
            parse_str($parts['query'], $parameters);
106
        }
107
108
        // Is the configured field name in the querystring?
109
        if (!array_key_exists($this->fieldName, $parameters)) {
110
            $this->logger->notice(
111
                sprintf(
112
                    'The configured query string field name "%s" was not found in the uri "%s"',
113
                    $this->fieldName,
114
                    $uri
115
                )
116
            );
117
            return new ActivationFlowPreferenceNotExpressed();
118
        }
119
120
        $option = $parameters[$this->fieldName];
121
        switch ($option) {
122
            case 'ra':
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 8 spaces, found 12
Loading history...
123
                return ActivationFlowPreference::createRa();
124
            case 'self':
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 8 spaces, found 12
Loading history...
125
                return ActivationFlowPreference::createSelf();
126
        }
127
128
129
        $this->logger->notice(
130
            sprintf(
131
                'Field "%s" contained an invalid option "%s", must be one of: %s',
132
                $this->fieldName,
133
                $option,
134
                implode(', ', $this->options)
135
            )
136
        );
137
        return new ActivationFlowPreferenceNotExpressed();
138
    }
139
140
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
141
     * @return ActivationFlowPreferenceInterface[]
142
     */
143
    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...
144
    {
145
        $this->logger->info('Analysing saml entitlement attributes for allowed activation flows');
146
147
        $token = $this->tokenStorage->getToken();
148
        if (!$token instanceof TokenInterface) {
149
            throw new LogicException("A authentication token should be set at this point");
150
        }
151
152
        $activationFlows = [];
153
        $attributes = $token->getAttributes();
154
        if (array_key_exists(self::ACTIVATION_FLOW_ENTITLEMENT_SAML_ATTRIBUTE, $attributes)) {
155
            $this->logger->debug('Found entitlement saml attributes');
156
            if (in_array($this->attributes['ra'], $attributes[self::ACTIVATION_FLOW_ENTITLEMENT_SAML_ATTRIBUTE])) {
157
                $activationFlows[] = ActivationFlowPreference::createRa();
158
            }
159
            if (in_array($this->attributes['self'], $attributes[self::ACTIVATION_FLOW_ENTITLEMENT_SAML_ATTRIBUTE])) {
160
                $activationFlows[] = ActivationFlowPreference::createSelf();
161
            }
162
        }
163
        return $activationFlows;
164
    }
165
}
166