Completed
Push — feature/authz-service ( 1e4c46...decc1c )
by
unknown
04:28 queued 02:04
created

ReconfigureInstitutionRequestValidator::validateAuthorizationSettings()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 52

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 52
rs 9.0472
c 0
b 0
f 0
cc 3
nc 3
nop 3

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/**
4
 * Copyright 2016 SURFnet B.V.
5
 *
6
 * Licensed under the Apache License, Version 2.0 (the "License");
7
 * you may not use this file except in compliance with the License.
8
 * You may obtain a copy of the License at
9
 *
10
 *     http://www.apache.org/licenses/LICENSE-2.0
11
 *
12
 * Unless required by applicable law or agreed to in writing, software
13
 * distributed under the License is distributed on an "AS IS" BASIS,
14
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
 * See the License for the specific language governing permissions and
16
 * limitations under the License.
17
 */
18
19
namespace Surfnet\StepupMiddleware\ManagementBundle\Validator;
20
21
use Assert\Assertion;
22
use Assert\InvalidArgumentException as AssertionException;
23
use InvalidArgumentException as CoreInvalidArgumentException;
24
use Surfnet\StepupBundle\Service\SecondFactorTypeService;
25
use Surfnet\StepupBundle\Value\SecondFactorType;
26
use Surfnet\StepupMiddleware\ApiBundle\Configuration\Entity\ConfiguredInstitution;
27
use Surfnet\StepupMiddleware\ApiBundle\Configuration\Service\ConfiguredInstitutionService;
28
use Surfnet\StepupMiddleware\ManagementBundle\Exception\InvalidArgumentException;
29
use Surfnet\StepupMiddleware\ManagementBundle\Validator\Assert as StepupAssert;
30
use Symfony\Component\Validator\Constraint;
31
use Symfony\Component\Validator\ConstraintValidator;
32
33
final class ReconfigureInstitutionRequestValidator extends ConstraintValidator
34
{
35
    /**
36
     * @var ConfiguredInstitutionService
37
     */
38
    private $configuredInstitutionsService;
39
40
    /**
41
     * @var string[] internal cache, access through getConfiguredInstitutions()
42
     */
43
    private $configuredInstitutions;
44
45
    /**
46
     * @var SecondFactorTypeService
47
     */
48
    private $secondFactorTypeService;
49
50
    public function __construct(
51
        ConfiguredInstitutionService $configuredInstitutionsService,
52
        SecondFactorTypeService $secondFactorTypeService
53
    ) {
54
        $this->configuredInstitutionsService = $configuredInstitutionsService;
55
        $this->secondFactorTypeService = $secondFactorTypeService;
56
    }
57
58 View Code Duplication
    public function validate($value, Constraint $constraint)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
59
    {
60
        /** @var \Symfony\Component\Validator\Violation\ConstraintViolationBuilder|false $violation */
61
        $violation = false;
62
63
        try {
64
            $this->validateRoot($value);
65
        } catch (AssertionException $exception) {
66
            // method is not in the interface yet, but the old method is deprecated.
67
            $violation = $this->context->buildViolation($exception->getMessage());
68
            $violation->atPath($exception->getPropertyPath());
69
        } catch (CoreInvalidArgumentException $exception) {
70
            $violation = $this->context->buildViolation($exception->getMessage());
71
        }
72
73
        if ($violation) {
74
            $violation->addViolation();
75
        }
76
    }
77
78
    public function validateRoot(array $configuration)
79
    {
80
        Assertion::isArray($configuration, 'Invalid body structure, must be an object', '(root)');
81
        $this->validateInstitutionsExist(array_keys($configuration));
82
83
        foreach ($configuration as $institution => $options) {
84
            $this->validateInstitutionConfigurationOptions($options, $institution);
85
        }
86
    }
87
88
    /**
89
     * @param array $institutions
90
     */
91
    public function validateInstitutionsExist(array $institutions)
92
    {
93
        $configuredInstitutions = $this->getConfiguredInstitutions();
94
95
        $nonExistentInstitutions = $this->determineNonExistentInstitutions($institutions, $configuredInstitutions);
96
97
        if (!empty($nonExistentInstitutions)) {
98
            throw new InvalidArgumentException(
99
                sprintf('Cannot reconfigure non-existent institution(s): %s', implode(', ', $nonExistentInstitutions))
100
            );
101
        }
102
    }
103
104
    /**
105
     * @param array $options
106
     * @param string $institution
107
     */
108
    public function validateInstitutionConfigurationOptions($options, $institution)
109
    {
110
        $propertyPath = sprintf('Institution(%s)', $institution);
111
112
        Assertion::isArray($options, 'Invalid institution configuration, must be an object', $propertyPath);
113
114
        $acceptedOptions = [
115
            'use_ra_locations',
116
            'show_raa_contact_information',
117
            'verify_email',
118
            'number_of_tokens_per_identity',
119
            'allowed_second_factors',
120
        ];
121
        
122
        StepupAssert::keysMatch(
123
            $options,
124
            $acceptedOptions,
125
            sprintf('Expected only options "%s" for "%s"', join(', ', $acceptedOptions), $institution),
126
            $propertyPath
127
        );
128
129
        Assertion::boolean(
130
            $options['use_ra_locations'],
131
            sprintf('Option "use_ra_locations" for "%s" must be a boolean value', $institution),
132
            $propertyPath
133
        );
134
135
        Assertion::boolean(
136
            $options['show_raa_contact_information'],
137
            sprintf('Option "show_raa_contact_information" for "%s" must be a boolean value', $institution),
138
            $propertyPath
139
        );
140
141
        Assertion::boolean(
142
            $options['verify_email'],
143
            sprintf('Option "verify_email" for "%s" must be a boolean value', $institution),
144
            $propertyPath
145
        );
146
147
        Assertion::integer(
148
            $options['number_of_tokens_per_identity'],
149
            sprintf('Option "number_of_tokens_per_identity" for "%s" must be an integer value', $institution),
150
            $propertyPath
151
        );
152
        
153
        Assertion::min(
154
            $options['number_of_tokens_per_identity'],
155
            0,
156
            sprintf('Option "number_of_tokens_per_identity" for "%s" must be greater than or equal to 0', $institution),
157
            $propertyPath
158
        );
159
160
        Assertion::isArray(
161
            $options['allowed_second_factors'],
162
            sprintf('Option "allowed_second_factors" for "%s" must be an array of strings', $institution),
163
            $propertyPath
164
        );
165
        Assertion::allString(
166
            $options['allowed_second_factors'],
167
            sprintf('Option "allowed_second_factors" for "%s" must be an array of strings', $institution),
168
            $propertyPath
169
        );
170
        Assertion::allInArray(
171
            $options['allowed_second_factors'],
172
            $this->secondFactorTypeService->getAvailableSecondFactorTypes(),
173
            'Option "allowed_second_factors" for "%s" must contain valid second factor types',
174
            $propertyPath
175
        );
176
    }
177
178
    /**
179
     * Accessor for configured institutions to be able to use an internal cache
180
     *
181
     * @return string[]
182
     */
183
    private function getConfiguredInstitutions()
184
    {
185
        if (!empty($this->configuredInstitutions)) {
186
            return $this->configuredInstitutions;
187
        }
188
189
        $this->configuredInstitutions = array_map(
190
            function (ConfiguredInstitution $configuredInstitution) {
191
                return $configuredInstitution->institution->getInstitution();
192
            },
193
            $this->configuredInstitutionsService->getAll()
194
        );
195
196
        return $this->configuredInstitutions;
197
    }
198
199
    /**
200
     * @param string[] $institutions
201
     * @param $configuredInstitutions
202
     * @return string[]
203
     */
204
    public function determineNonExistentInstitutions(array $institutions, $configuredInstitutions)
205
    {
206
        $normalizedConfiguredInstitutions = array_map(
207
            function ($institution) {
208
                return strtolower($institution);
209
            },
210
            $configuredInstitutions
211
        );
212
213
        return array_filter(
214
            $institutions,
215
            function ($institution) use ($normalizedConfiguredInstitutions) {
216
                $normalizedInstitution = strtolower($institution);
217
218
                return !in_array($normalizedInstitution, $normalizedConfiguredInstitutions);
219
            }
220
        );
221
    }
222
}
223