Completed
Pull Request — release/3.1 (#301)
by
unknown
03:11 queued 01:10
created

InstitutionConfigurationController::showAction()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 35

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 35
rs 9.36
c 0
b 0
f 0
cc 2
nc 2
nop 0
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\Controller;
20
21
use DateTime;
22
use Exception;
23
use Psr\Log\LoggerInterface;
24
use Rhumsaa\Uuid\Uuid;
25
use Surfnet\Stepup\Configuration\Value\Institution;
26
use Surfnet\Stepup\Configuration\Value\InstitutionRole;
27
use Surfnet\Stepup\Helper\JsonHelper;
28
use Doctrine\DBAL\Driver\Connection;
29
use Surfnet\Stepup\Identity\Entity\VettedSecondFactor;
30
use Surfnet\Stepup\Identity\Value\CommonName;
31
use Surfnet\Stepup\Identity\Value\Email;
32
use Surfnet\Stepup\Identity\Value\IdentityId;
33
use Surfnet\Stepup\Identity\Value\Locale;
34
use Surfnet\Stepup\Identity\Value\NameId;
35
use Surfnet\Stepup\Identity\Value\SecondFactorId;
36
use Surfnet\Stepup\Identity\Value\SecondFactorIdentifier;
37
use Surfnet\Stepup\Identity\Value\SecondFactorIdentifierFactory;
38
use Surfnet\Stepup\Identity\Value\YubikeyPublicId;
39
use Surfnet\StepupBundle\Value\SecondFactorType;
40
use Surfnet\StepupMiddleware\ApiBundle\Configuration\Service\AllowedSecondFactorListService;
41
use Surfnet\StepupMiddleware\ApiBundle\Configuration\Service\InstitutionAuthorizationService;
42
use Surfnet\StepupMiddleware\ApiBundle\Configuration\Service\InstitutionConfigurationOptionsService;
43
use Surfnet\StepupMiddleware\ApiBundle\Exception\BadCommandRequestException;
44
use Surfnet\Stepup\Identity\Identity;
45
use Surfnet\Stepup\Identity\EventSourcing\IdentityRepository;
46
use Surfnet\StepupMiddleware\CommandHandlingBundle\Command\Command;
47
use Surfnet\StepupMiddleware\CommandHandlingBundle\Configuration\Command\ReconfigureInstitutionConfigurationOptionsCommand;
48
use Surfnet\StepupMiddleware\CommandHandlingBundle\EventHandling\BufferedEventBus;
49
use Surfnet\StepupMiddleware\CommandHandlingBundle\Exception\ForbiddenException;
50
use Surfnet\StepupMiddleware\CommandHandlingBundle\Pipeline\TransactionAwarePipeline;
51
use Surfnet\StepupMiddleware\GatewayBundle\Entity\SecondFactor;
52
use Surfnet\StepupMiddleware\ManagementBundle\Service\DBALConnectionHelper;
53
use Surfnet\StepupMiddleware\ManagementBundle\Validator\Constraints\ValidReconfigureInstitutionsRequest;
54
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
55
use Symfony\Component\HttpFoundation\JsonResponse;
56
use Symfony\Component\HttpFoundation\Request;
57
use Symfony\Component\HttpFoundation\Response;
58
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
59
use Symfony\Component\Validator\Validator\ValidatorInterface;
60
61
/**
62
 * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
63
 */
64
final class InstitutionConfigurationController extends Controller
65
{
66
    /**
67
     * @return InstitutionConfigurationOptionsService
68
     */
69
    private $institutionConfigurationOptionsService;
70
71
    /**
72
     * @return InstitutionAuthorizationService
73
     */
74
    private $institutionAuthorizationService;
75
76
    /**
77
     * @return ValidatorInterface
78
     */
79
    private $validator;
80
81
    /**
82
     * @return AllowedSecondFactorListService
83
     */
84
    private $allowedSecondFactorListService;
85
86
    /**
87
     * @return LoggerInterface
88
     */
89
    private $logger;
90
91
    /**
92
     * @return TransactionAwarePipeline
93
     */
94
    private $pipeline;
95
96
    /**
97
     * @var DBALConnectionHelper
98
     */
99
    private $connectionHelper;
100
    /**
101
     * @var IdentityRepository
102
     */
103
    private $identityRepository;
104
    /**
105
     * @var Connection
106
     */
107
    private $middlewareConnection;
108
    /**
109
     * @var BufferedEventBus
110
     */
111
    private $bufferedEventBus;
112
113
    public function __construct(
114
        InstitutionConfigurationOptionsService $institutionConfigurationOptionsService,
115
        InstitutionAuthorizationService $institutionAuthorizationService,
116
        ValidatorInterface $dataCollectingValidator,
117
        AllowedSecondFactorListService $allowedSecondFactorListService,
118
        LoggerInterface $logger,
119
        TransactionAwarePipeline $pipeline,
120
        DBALConnectionHelper $dbalConnectionHelper,
121
        IdentityRepository $identityRepository,
122
        Connection $middlewareConnection,
123
        BufferedEventBus $bufferedEventBus
124
    ) {
125
        $this->institutionConfigurationOptionsService = $institutionConfigurationOptionsService;
126
        $this->institutionAuthorizationService = $institutionAuthorizationService;
127
        $this->validator = $dataCollectingValidator;
128
        $this->allowedSecondFactorListService = $allowedSecondFactorListService;
129
        $this->logger = $logger;
130
        $this->pipeline = $pipeline;
131
        $this->connectionHelper = $dbalConnectionHelper;
132
        $this->identityRepository = $identityRepository;
133
        $this->middlewareConnection = $middlewareConnection;
134
        $this->bufferedEventBus = $bufferedEventBus;
135
    }
136
137
    public function provisionAction(){
138
139
        $this->middlewareConnection->beginTransaction();
140
141
        ini_set("memory_limit", "1024M");
142
        try {
143
            for ($i = 0; $i < 1000; $i++) {
144
                $identity = Identity::create(
145
                    new IdentityId(Uuid::uuid4()->toString()),
146
                    new \Surfnet\Stepup\Identity\Value\Institution('uva.nl'),
147
                    new NameId('test-' . $i),
148
                    new CommonName('test-' . $i),
149
                    new Email('test-' . $i . '@email.tld'),
150
                    new Locale('nl_NL')
151
                );
152
153
                $secondFactorId = new SecondFactorId(Uuid::uuid4()->toString());
154
                $yubikeyId = new YubikeyPublicId(str_pad($i, 8, '0'));
155
156
                $identity->bootstrapYubikeySecondFactor($secondFactorId, $yubikeyId);
157
158
                $this->identityRepository->save($identity);
159
            }
160
161
            $this->bufferedEventBus->flush();
162
163
            $this->middlewareConnection->commit();
164
165
        } catch(Exception $e) {
166
            $this->middlewareConnection->rollback();
167
        }
168
169
170
        return new JsonResponse(['status' => 'OK']);
171
    }
172
173
    public function showAction()
174
    {
175
        $this->denyAccessUnlessGranted(['ROLE_MANAGEMENT']);
176
177
        $institutionConfigurationOptions = $this->institutionConfigurationOptionsService
178
            ->findAllInstitutionConfigurationOptions();
179
180
        $allowedSecondFactorMap = $this->allowedSecondFactorListService->getAllowedSecondFactorMap();
181
182
        $overview = [];
183
        foreach ($institutionConfigurationOptions as $options) {
184
            // Load the numberOfTokensPerIdentity from the institution config options service
185
            $numberOfTokensPerIdentity = $this->institutionConfigurationOptionsService
186
                ->getMaxNumberOfTokensFor(new Institution($options->institution->getInstitution()));
187
188
            // Get the authorization options for this institution
189
            $institutionConfigurationOptionsMap = $this->institutionAuthorizationService
190
                ->findAuthorizationsFor($options->institution);
191
192
            $overview[$options->institution->getInstitution()] = [
193
                'use_ra_locations' => $options->useRaLocationsOption,
194
                'show_raa_contact_information' => $options->showRaaContactInformationOption,
195
                'verify_email' => $options->verifyEmailOption,
196
                'number_of_tokens_per_identity' => $numberOfTokensPerIdentity,
197
                'allowed_second_factors' => $allowedSecondFactorMap->getAllowedSecondFactorListFor(
198
                    $options->institution
199
                ),
200
                'use_ra' => $institutionConfigurationOptionsMap->getAuthorizationOptionsByRole(InstitutionRole::useRa())->jsonSerialize(),
201
                'use_raa' => $institutionConfigurationOptionsMap->getAuthorizationOptionsByRole(InstitutionRole::useRaa())->jsonSerialize(),
202
                'select_raa' => $institutionConfigurationOptionsMap->getAuthorizationOptionsByRole(InstitutionRole::selectRaa())->jsonSerialize(),
203
            ];
204
        }
205
206
        return new JsonResponse($overview);
207
    }
208
209
    public function reconfigureAction(Request $request)
210
    {
211
        $this->denyAccessUnlessGranted(['ROLE_MANAGEMENT']);
212
213
        $configuration = JsonHelper::decode($request->getContent());
214
215
        $violations = $this->validator->validate($configuration, new ValidReconfigureInstitutionsRequest());
216
        if ($violations->count() > 0) {
217
            throw BadCommandRequestException::withViolations('Invalid reconfigure institutions request', $violations);
218
        }
219
220
        if (empty($configuration)) {
221
            $this->logger->notice(sprintf('No institutions to reconfigure: empty configuration received'));
222
223
            return new JsonResponse([
224
                'status'       => 'OK',
225
                'processed_by' =>  $request->server->get('SERVER_NAME') ?: $request->server->get('SERVER_ADDR'),
226
                'applied_at'   => (new DateTime())->format(DateTime::ISO8601),
227
            ]);
228
        }
229
230
        $commands = [];
231
        foreach ($configuration as $institution => $options) {
232
            $command                                  = new ReconfigureInstitutionConfigurationOptionsCommand();
233
            $command->UUID                            = (string) Uuid::uuid4();
234
            $command->institution                     = $institution;
0 ignored issues
show
Documentation Bug introduced by
It seems like $institution can also be of type integer. However, the property $institution is declared as type string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
235
            $command->useRaLocationsOption            = $options['use_ra_locations'];
236
            $command->showRaaContactInformationOption = $options['show_raa_contact_information'];
237
            $command->verifyEmailOption               = $options['verify_email'];
238
            $command->numberOfTokensPerIdentityOption = $options['number_of_tokens_per_identity'];
239
            $command->allowedSecondFactors            = $options['allowed_second_factors'];
240
241
            // The useRa, useRaa and selectRaa options are optional
242
            $command->useRaOption = isset($options['use_ra']) ? $options['use_ra'] : null;
243
            $command->useRaaOption = isset($options['use_raa']) ? $options['use_raa'] : null;
244
            $command->selectRaaOption = isset($options['select_raa']) ? $options['select_raa'] : null;
245
246
            $commands[] = $command;
247
        }
248
249
        $this->logger->notice(
250
            sprintf('Executing %s reconfigure institution configuration options commands', count($commands))
251
        );
252
253
        $this->handleCommands($commands);
254
255
        return new JsonResponse([
256
            'status'       => 'OK',
257
            'processed_by' =>  $request->server->get('SERVER_NAME') ?: $request->server->get('SERVER_ADDR'),
258
            'applied_at'   => (new DateTime())->format(DateTime::ISO8601),
259
        ]);
260
    }
261
262
    /**
263
     * @param Command[] $commands
264
     * @throws Exception
265
     */
266
    private function handleCommands(array $commands)
267
    {
268
        $connectionHelper = $this->connectionHelper;
269
270
        $connectionHelper->beginTransaction();
271
272
        foreach ($commands as $command) {
273
            try {
274
                $this->pipeline->process($command);
275
            } catch (ForbiddenException $e) {
276
                $connectionHelper->rollBack();
277
278
                throw new AccessDeniedHttpException(
279
                    sprintf('Processing of command "%s" is forbidden for this client', $command),
280
                    $e
281
                );
282
            } catch (Exception $exception) {
283
                $connectionHelper->rollBack();
284
285
                throw $exception;
286
            }
287
        }
288
289
        $connectionHelper->commit();
290
    }
291
}
292