Passed
Pull Request — main (#336)
by
unknown
26:05 queued 18:54
created

getFlowPreferencesFromSamlAttributes()   A

Complexity

Conditions 5
Paths 6

Size

Total Lines 20
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 13
nc 6
nop 0
dl 0
loc 20
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\LogicException;
25
use Surfnet\StepupSelfService\SelfServiceBundle\Value\ActivationFlowPreference;
26
use Surfnet\StepupSelfService\SelfServiceBundle\Value\ActivationFlowPreferenceInterface;
27
use Surfnet\StepupSelfService\SelfServiceBundle\Value\ActivationFlowPreferenceNotExpressed;
28
use Symfony\Component\HttpFoundation\RequestStack;
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_PREFERENCE_SESSION_NAME = 'self_service_activation_flow_preference';
35
    private const ACTIVATION_FLOW_ENTITLEMENT_SAML_ATTRIBUTE = 'urn:mace:dir:attribute-def:eduPersonEntitlement';
36
37
    /**
0 ignored issues
show
Coding Style introduced by
Parameter $requestStack 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 $options 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 $attributes should have a doc-comment as per coding-style.
Loading history...
38
     * Handle preferred activation flow logic
39
     *
40
     * 1. On the / path, the preferred activation flow can be set using the configured
41
     *    query string field. This field can have an option indicating the preferred flow.
42
     *    This is stored in session.
43
     * 2. If this preference was set, the list of available options is limited to just that
44
     *    option.
45
     * 3. The option can be reset by the Identity, removing it from session.
46
     *
47
     * Note that:
48
     * - fieldName and options are configured in the SelfServiceExtension
49
     */
50
    public function __construct(
51
        private readonly RequestStack $requestStack,
52
        private readonly TokenStorageInterface $tokenStorage,
53
        private readonly LoggerInterface $logger,
54
        private readonly string $fieldName,
55
        private readonly array $options,
56
        private readonly array $attributes
57
    ) {
58
    }
59
60
    public function process(string $uri): void
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function process()
Loading history...
61
    {
62
        $requestedActivationPreference = $this->getFlowPreferenceFromUri($uri);
63
        if ($requestedActivationPreference === null) {
64
            return;
65
        }
66
67
        $availableActivationPreferences = $this->getFlowPreferencesFromSamlAttributes();
68
69
        if (count($availableActivationPreferences) == 0) {
70
            $this->logger->info('No entitlement attributes found to determine the allowed flow, allowing all flows');
71
            $availableActivationPreferences = ['ra', 'self'];
72
        }
73
74
        if (in_array($requestedActivationPreference, $availableActivationPreferences)) {
75
            $this->logger->info('Storing the preference in session');
76
            $this->requestStack->getSession()->set(
77
                self::ACTIVATION_FLOW_PREFERENCE_SESSION_NAME,
78
                new ActivationFlowPreference($requestedActivationPreference)
79
            );
80
        }
81
    }
82
83
    public function hasActivationFlowPreference(): bool
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function hasActivationFlowPreference()
Loading history...
84
    {
85
        return $this->requestStack->getSession()->has(self::ACTIVATION_FLOW_PREFERENCE_SESSION_NAME);
86
    }
87
88
    public function getPreference(): ActivationFlowPreferenceInterface
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function getPreference()
Loading history...
89
    {
90
        if (!$this->hasActivationFlowPreference()) {
91
            return new ActivationFlowPreferenceNotExpressed();
92
        }
93
        return $this->requestStack->getSession()->get(self::ACTIVATION_FLOW_PREFERENCE_SESSION_NAME);
94
    }
95
96
    private function getFlowPreferenceFromUri(string $uri): ?string {
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function getFlowPreferenceFromUri()
Loading history...
Coding Style introduced by
Private method name "ActivationFlowService::getFlowPreferenceFromUri" must be prefixed with an underscore
Loading history...
Coding Style introduced by
Opening brace should be on a new line
Loading history...
97
        $this->logger->info(sprintf('Analysing uri "%s" for activation flow query parameter', $uri));
98
99
        $parts = parse_url($uri);
100
        $parameters = [];
101
102
        if (array_key_exists('query', $parts)) {
103
            $this->logger->debug('Found a query string in the uri');
104
            parse_str($parts['query'], $parameters);
105
        }
106
107
        // Is the configured field name in the querystring?
108
        if (!array_key_exists($this->fieldName, $parameters)) {
109
            $this->logger->notice(
110
                sprintf(
111
                    'The configured query string field name "%s" was not found in the uri "%s"',
112
                    $this->fieldName,
113
                    $uri
114
                )
115
            );
116
            return null;
117
        }
118
119
        $option = $parameters[$this->fieldName];
120
        if (!in_array($option, $this->options)) {
121
            $this->logger->notice(
122
                sprintf(
123
                    'Field "%s" contained an invalid option "%s", must be one of: %s',
124
                    $this->fieldName,
125
                    $option,
126
                    implode(', ', $this->options)
127
                )
128
            );
129
            return null;
130
        }
131
132
        return $option;
133
    }
134
135
    private function getFlowPreferencesFromSamlAttributes(): array {
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function getFlowPreferencesFromSamlAttributes()
Loading history...
Coding Style introduced by
Private method name "ActivationFlowService::getFlowPreferencesFromSamlAttributes" must be prefixed with an underscore
Loading history...
Coding Style introduced by
Opening brace should be on a new line
Loading history...
136
        $this->logger->info('Analysing saml entitlement attributes for allowed activation flows');
137
138
        $token = $this->tokenStorage->getToken();
139
        if (!$token instanceof TokenInterface) {
140
            throw new LogicException("A authentication token should be set at this point");
141
        }
142
143
        $activationFlows = [];
144
        $attributes = $token->getAttributes();
145
        if (array_key_exists(self::ACTIVATION_FLOW_ENTITLEMENT_SAML_ATTRIBUTE, $attributes)) {
146
            $this->logger->debug('Found entitlement saml attributes');
147
            if (in_array($this->attributes['ra'], $attributes[self::ACTIVATION_FLOW_ENTITLEMENT_SAML_ATTRIBUTE])) {
148
                $activationFlows[] = 'ra';
149
            }
150
            if (in_array($this->attributes['self'], $attributes[self::ACTIVATION_FLOW_ENTITLEMENT_SAML_ATTRIBUTE])) {
151
                $activationFlows[] = 'self';
152
            }
153
        }
154
        return $activationFlows;
155
    }
156
}
157