RegistrationController   A
last analyzed

Complexity

Total Complexity 19

Size/Duplication

Total Lines 270
Duplicated Lines 0 %

Importance

Changes 6
Bugs 0 Features 0
Metric Value
eloc 142
c 6
b 0
f 0
dl 0
loc 270
rs 10
wmc 19

9 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 10 1
A displaySecondFactorTypes() 0 40 2
B displayVettingTypes() 0 57 6
A emailVerificationEmailSent() 0 10 1
A registrationEmailSent() 0 12 1
A buildRegistrationActionParameters() 0 30 3
A verifyEmail() 0 23 3
A sendRegistrationEmail() 0 14 1
A registrationPdf() 0 35 1
1
<?php
2
3
declare(strict_types = 1);
4
5
/**
6
 * Copyright 2014 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\Controller;
22
23
use DateInterval;
24
use Mpdf\Mpdf;
25
use Mpdf\Output\Destination as MpdfDestination;
26
use Psr\Log\LoggerInterface;
27
use Surfnet\StepupMiddlewareClientBundle\Identity\Dto\VerifiedSecondFactor;
28
use Surfnet\StepupSelfService\SelfServiceBundle\Service\ControllerCheckerService;
29
use Surfnet\StepupSelfService\SelfServiceBundle\Service\InstitutionConfigurationOptionsService;
30
use Surfnet\StepupSelfService\SelfServiceBundle\Service\RaLocationService;
31
use Surfnet\StepupSelfService\SelfServiceBundle\Service\RaService;
32
use Surfnet\StepupSelfService\SelfServiceBundle\Service\SecondFactorAvailabilityHelper;
33
use Surfnet\StepupSelfService\SelfServiceBundle\Service\SecondFactorService;
34
use Surfnet\StepupSelfService\SelfServiceBundle\Service\VettingTypeService;
35
use Surfnet\StepupSelfService\SelfServiceBundle\Value\VettingType\VettingTypeInterface;
36
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
37
use Symfony\Component\HttpFoundation\RedirectResponse;
38
use Symfony\Component\HttpFoundation\Request;
39
use Symfony\Component\HttpFoundation\Response;
40
use Symfony\Component\HttpFoundation\ResponseHeaderBag;
41
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
42
use Symfony\Component\Routing\Attribute\Route;
43
44
/**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
45
 *  @SuppressWarnings(PHPMD.CouplingBetweenObjects)
0 ignored issues
show
Coding Style introduced by
Expected 1 space after asterisk; 2 found
Loading history...
46
 * /
47
 */
0 ignored issues
show
Coding Style introduced by
Missing @category tag in class comment
Loading history...
Coding Style introduced by
Missing @package tag in class comment
Loading history...
Coding Style introduced by
Missing @author tag in class comment
Loading history...
Coding Style introduced by
Missing @license tag in class comment
Loading history...
Coding Style introduced by
Missing @link tag in class comment
Loading history...
48
class RegistrationController extends AbstractController
49
{
50
    public function __construct(
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function __construct()
Loading history...
51
        private readonly VettingTypeService $vettingTypeService,
52
        private readonly InstitutionConfigurationOptionsService $configurationOptionsService,
53
        private readonly SecondFactorService $secondFactorService,
54
        private readonly LoggerInterface $logger,
55
        private readonly SecondFactorAvailabilityHelper $secondFactorAvailabilityHelper,
56
        private readonly RaService $raService,
57
        private readonly RaLocationService $raLocationService,
58
        private readonly ControllerCheckerService $checkerService,
59
    ) {
60
    }
61
62
    #[Route(
63
        path: '/registration/select-token',
64
        name: 'ss_registration_display_types',
65
        methods: ['GET'],
66
    )]
67
    public function displaySecondFactorTypes(): Response
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function displaySecondFactorTypes()
Loading history...
68
    {
69
        $institution = $this->getUser()->getIdentity()->institution;
70
        $institutionConfigurationOptions = $this->configurationOptionsService
71
            ->getInstitutionConfigurationOptionsFor($institution);
72
73
        $identity = $this->getUser()->getIdentity();
74
75
        // Get all available second factors from the config.
76
        $allSecondFactors = $this->getParameter('ss.enabled_second_factors');
77
78
        $secondFactors = $this->secondFactorService->getSecondFactorsForIdentity(
79
            $identity,
80
            $allSecondFactors,
81
            $institutionConfigurationOptions->allowedSecondFactors,
82
            $institutionConfigurationOptions->numberOfTokensPerIdentity
83
        );
84
85
        if ($secondFactors->getRegistrationsLeft() <= 0) {
86
            $this->logger->notice(
87
                'User tried to register a new token but maximum number of tokens is reached. Redirecting to overview'
88
            );
89
            return $this->forward(
90
                'Surfnet\StepupSelfService\SelfServiceBundle\Controller\SecondFactor\SecondFactorListController'
91
            );
92
        }
93
94
        $availableTokens = $this->secondFactorAvailabilityHelper->filter($secondFactors);
95
96
        return $this->render(
97
            'registration/display_second_factor_types.html.twig',
98
            [
99
                'commonName' => $this->getUser()->getIdentity()->commonName,
100
                'availableSecondFactors' => $availableTokens,
101
                'verifyEmail' => $this->checkerService->emailVerificationIsRequired(),
102
            ]
103
        );
104
    }
105
106
    #[Route(
107
        path: '/second-factor/{secondFactorId}/vetting-types',
108
        name: 'ss_second_factor_vetting_types',
109
        methods:  ['GET'],
110
    )]
111
    public function displayVettingTypes(Request $request, string $secondFactorId): Response
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function displayVettingTypes()
Loading history...
112
    {
113
        $vettingTypeCollection = $this->vettingTypeService->vettingTypes($this->getUser()->getIdentity(), $secondFactorId);
114
115
        $nudgeSelfAssertedTokens = $vettingTypeCollection->isPreferred(VettingTypeInterface::SELF_ASSERTED_TOKENS);
116
        $nudgeRaVetting = $vettingTypeCollection->isPreferred(VettingTypeInterface::ON_PREMISE);
117
118
        // Nudging section: helping the Identity into choosing the right vetting type:
119
120
        // Option 1: A self-asserted token registration nudge was requested via query string (?activate=self)
121
        if ($nudgeSelfAssertedTokens && $vettingTypeCollection->allowSelfAssertedTokens()) {
122
            $this->logger->notice('Nudging (forcing) self-asserted token registration');
123
            return $this->forward(
124
                'Surfnet\StepupSelfService\SelfServiceBundle\Controller\SelfAssertedTokensController::selfAssertedTokenRegistration',
125
                ['secondFactorId' => $secondFactorId]
126
            );
127
        }
128
129
        // Option 2: A ra-vetting nudge was requested via query string (?activate=ra)
130
        if ($nudgeRaVetting) {
131
            $this->logger->notice('Nudging (forcing) RA vetting');
132
            return $this->forward(
133
                'Surfnet\StepupSelfService\SelfServiceBundle\Controller\RegistrationController::sendRegistrationEmail',
134
                ['secondFactorId' => $secondFactorId]
135
            );
136
        }
137
138
        // Option 3: non-formal nudge, skip over selection screen. As only ra vetting is available.
139
        if (!$vettingTypeCollection->allowSelfVetting() && !$vettingTypeCollection->allowSelfAssertedTokens()) {
140
            $this->logger
141
                ->notice(
142
                    'Skipping ahead to the RA vetting option as self vetting or self-asserted tokens are not allowed'
143
                );
144
            return $this->forward(
145
                'Surfnet\StepupSelfService\SelfServiceBundle\Controller\RegistrationController::sendRegistrationEmail',
146
                ['secondFactorId' => $secondFactorId]
147
            );
148
        }
149
150
        $institution = $this->getUser()->getIdentity()->institution;
151
        $currentLocale = $request->getLocale();
152
        $vettingTypeHint = $this->vettingTypeService->vettingTypeHint($institution, $currentLocale);
153
154
        return $this->render(
155
            'registration/display_vetting_types.html.twig',
156
            [
157
                'allowSelfVetting' => $vettingTypeCollection->allowSelfVetting(),
158
                'allowSelfAssertedTokens' => $vettingTypeCollection->allowSelfAssertedTokens(),
159
                'hasVettingTypeHint' => !is_null($vettingTypeHint),
160
                'vettingTypeHint' => $vettingTypeHint,
161
                'verifyEmail' => $this->checkerService->emailVerificationIsRequired(),
162
                'secondFactorId' => $secondFactorId,
163
            ]
164
        );
165
    }
166
167
168
    #[Route(
169
        path: '/registration/{secondFactorId}/email-verification-email-sent',
170
        name: 'ss_registration_email_verification_email_sent',
171
        methods: ['GET'],
172
    )]
173
    public function emailVerificationEmailSent(): Response
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function emailVerificationEmailSent()
Loading history...
174
    {
175
        return $this->render(
176
            view: 'registration/email_verification_email_sent.html.twig',
177
            parameters: ['email' => $this->getUser()->getIdentity()->email]
178
        );
179
    }
180
181
    #[Route(
182
        path: '/verify-email',
183
        name: 'ss_registration_verify_email',
184
        methods: ['GET'],
185
    )]
186
    public function verifyEmail(Request $request): Response
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function verifyEmail()
Loading history...
187
    {
188
        $nonce = $request->query->get('n', '');
189
        $identityId = $this->getUser()->getIdentity()->id;
190
        $secondFactor = $this->secondFactorService->findUnverifiedByVerificationNonce($identityId, $nonce);
191
192
        if ($secondFactor === null) {
193
            throw new NotFoundHttpException('No second factor can be verified using this URL.');
194
        }
195
196
        if ($this->secondFactorService->verifyEmail($identityId, $nonce)) {
197
            return $this->redirectToRoute(
198
                'ss_second_factor_vetting_types',
199
                ['secondFactorId' => $secondFactor->id]
200
            );
201
        }
202
203
        return $this->render('registration/verify_email.html.twig');
204
    }
205
206
    /**
0 ignored issues
show
Coding Style introduced by
Parameter $secondFactorId should have a doc-comment as per coding-style.
Loading history...
207
     * Intermediate action where the registration mail is sent. After which the
208
     * email-sent page is displayed. Preventing the mail message from being sent
209
     * over and over again when the user performs a page reload.
210
     */
0 ignored issues
show
Coding Style introduced by
There must be no blank lines after the function comment
Loading history...
Coding Style introduced by
Missing @return tag in function comment
Loading history...
211
    #[Route(
212
        path: '/registration/{secondFactorId}/send-registration-email',
213
        name: 'ss_registration_send_registration_email',
214
        methods: ['GET'],
215
    )]
216
217
    public function sendRegistrationEmail(string $secondFactorId): RedirectResponse
218
    {
219
        // Send the registration email
220
        $this->raService
221
            ->sendRegistrationMailMessage($this->getUser()->getIdentity()->id, $secondFactorId);
222
        return $this->redirectToRoute(
223
            'ss_registration_registration_email_sent',
224
            ['secondFactorId' => $secondFactorId]
225
        );
226
    }
227
228
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
229
     * @param $secondFactorId
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
230
     * @return Response
0 ignored issues
show
Coding Style introduced by
Tag @return cannot be grouped with parameter tags in a doc comment
Loading history...
231
     */
232
    #[Route(
233
        path: '/registration/{secondFactorId}/registration-email-sent',
234
        name: 'ss_registration_registration_email_sent',
235
        methods: ['GET'],
236
    )]
237
    public function registrationEmailSent($secondFactorId): Response
238
    {
239
        $parameters = $this->buildRegistrationActionParameters($secondFactorId);
240
        // Report that it was sent
241
        return $this->render(
242
            'registration/registration_email_sent.html.twig',
243
            $parameters
244
        );
245
    }
246
247
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
248
     * @param $secondFactorId
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
249
     * @return Response
0 ignored issues
show
Coding Style introduced by
Tag @return cannot be grouped with parameter tags in a doc comment
Loading history...
250
     */
251
    #[Route(
252
        path: '/registration/{secondFactorId}/registration-pdf',
253
        name: 'ss_registration_registration_pdf',
254
        methods: ['GET'],
255
    )]
256
    public function registrationPdf($secondFactorId): Response
257
    {
258
        $parameters = $this->buildRegistrationActionParameters($secondFactorId);
259
260
        $content = $this->renderView('registration/registration_email_sent_pdf.html.twig', $parameters);
261
262
        $mpdf = new Mpdf(
263
            ['tempDir' => sys_get_temp_dir()]
264
        );
265
        $mpdf->setLogger($this->logger);
266
267
        $mpdf->WriteHTML($content);
268
        $output = $mpdf->Output('registration-code.pdf', MpdfDestination::STRING_RETURN);
269
270
        $response = new Response($output);
271
        $disposition = $response->headers->makeDisposition(
272
            ResponseHeaderBag::DISPOSITION_ATTACHMENT,
273
            'registration-code.pdf'
274
        );
275
276
        $response->headers->set('Content-Disposition', $disposition);
277
        $response->headers->set('Content-Description', 'File Transfer');
278
        $response->headers->set('Content-Transfer-Encoding', 'binary');
279
        $response->headers->set('Cache-Control', 'public, must-revalidate, max-age=0');
280
        $response->headers->set('Pragma', 'public');
281
        $response->headers->set('Expires', 'Sat, 26 Jul 1997 05:00:00 GMT');
282
        $response->headers->set('Last-Modified', '' . gmdate('D, d M Y H:i:s') . ' GMT');
283
        $response->headers->set('Content-Type', 'application/pdf');
284
285
        return $response;
286
    }
287
288
    private function buildRegistrationActionParameters($secondFactorId): array
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function buildRegistrationActionParameters()
Loading history...
Coding Style introduced by
Private method name "RegistrationController::buildRegistrationActionParameters" must be prefixed with an underscore
Loading history...
289
    {
290
        $identity = $this->getUser()->getIdentity();
291
292
        /** @var VerifiedSecondFactor $secondFactor */
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
The close comment tag must be the only content on the line
Loading history...
Coding Style introduced by
Missing short description in doc comment
Loading history...
293
        $secondFactor = $this->secondFactorService->findOneVerified($secondFactorId);
294
295
        $parameters = [
296
            'email'            => $identity->email,
297
            'secondFactorId'   => $secondFactor->id,
298
            'registrationCode' => $secondFactor->registrationCode,
299
            'expirationDate'   => $secondFactor->registrationRequestedAt->add(
300
                new DateInterval('P14D')
301
            ),
302
            'locale'           => $identity->preferredLocale,
303
            'verifyEmail'      => $this->checkerService->emailVerificationIsRequired(),
304
        ];
305
306
        $institutionConfigurationOptions = $this->configurationOptionsService
307
            ->getInstitutionConfigurationOptionsFor($identity->institution);
308
309
        if ($institutionConfigurationOptions->useRaLocations) {
310
            $parameters['raLocations'] = $this->raLocationService->listRaLocationsFor($identity->institution);
311
        } elseif (!$institutionConfigurationOptions->showRaaContactInformation) {
312
            $parameters['ras'] = $this->raService->listRasWithoutRaas($identity->institution);
313
        } else {
314
            $parameters['ras'] = $this->raService->listRas($identity->institution);
315
        }
316
317
        return $parameters;
318
    }
319
}
320