Passed
Pull Request — main (#308)
by Michiel
14:02 queued 06:58
created

RegistrationController::registrationPdf()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 39
Code Lines 27

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 27
c 0
b 0
f 0
nc 1
nop 1
dl 0
loc 39
rs 9.488
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\InstitutionConfigurationOptionsService;
29
use Surfnet\StepupSelfService\SelfServiceBundle\Service\RaLocationService;
30
use Surfnet\StepupSelfService\SelfServiceBundle\Service\RaService;
31
use Surfnet\StepupSelfService\SelfServiceBundle\Service\SecondFactorAvailabilityHelper;
32
use Surfnet\StepupSelfService\SelfServiceBundle\Service\SecondFactorService;
33
use Surfnet\StepupSelfService\SelfServiceBundle\Service\VettingTypeService;
34
use Surfnet\StepupSelfService\SelfServiceBundle\Value\VettingType\VettingTypeInterface;
35
use Symfony\Component\HttpFoundation\RedirectResponse;
36
use Symfony\Component\HttpFoundation\Request;
37
use Symfony\Component\HttpFoundation\Response;
38
use Symfony\Component\HttpFoundation\ResponseHeaderBag;
39
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
40
use Symfony\Component\Routing\Attribute\Route;
41
42
class RegistrationController extends Controller
0 ignored issues
show
Coding Style introduced by
Missing doc comment for class RegistrationController
Loading history...
43
{
44
    public function __construct(
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function __construct()
Loading history...
45
        private readonly VettingTypeService $vettingTypeService,
46
        private readonly InstitutionConfigurationOptionsService $configurationOptionsService,
47
        private readonly SecondFactorService $secondFactorService,
48
        private readonly LoggerInterface $logger,
49
        private readonly SecondFactorAvailabilityHelper $secondFactorAvailabilityHelper,
50
        private readonly RaService $raService,
51
        private readonly RaLocationService $raLocationService,
52
    ) {
53
        parent::__construct($logger, $configurationOptionsService);
54
    }
55
56
    #[Route(
57
        path: '/registration/select-token',
58
        name: 'ss_registration_display_types',
59
        methods: ['GET'],
60
    )]
61
    public function displaySecondFactorTypes(): Response
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function displaySecondFactorTypes()
Loading history...
62
    {
63
        $institution = $this->getIdentity()->institution;
64
        $institutionConfigurationOptions = $this->configurationOptionsService
65
            ->getInstitutionConfigurationOptionsFor($institution);
66
67
        $identity = $this->getIdentity();
68
69
        // Get all available second factors from the config.
70
        $allSecondFactors = $this->getParameter('ss.enabled_second_factors');
71
72
        $secondFactors = $this->secondFactorService->getSecondFactorsForIdentity(
73
            $identity,
74
            $allSecondFactors,
75
            $institutionConfigurationOptions->allowedSecondFactors,
76
            $institutionConfigurationOptions->numberOfTokensPerIdentity
77
        );
78
79
        if ($secondFactors->getRegistrationsLeft() <= 0) {
80
            $this->logger->notice(
81
                'User tried to register a new token but maximum number of tokens is reached. Redirecting to overview'
82
            );
83
            return $this->forward('SurfnetStepupSelfServiceSelfServiceBundle:SecondFactor:list');
84
        }
85
86
        $availableTokens = $this->secondFactorAvailabilityHelper->filter($secondFactors);
87
88
        return $this->render(
89
            'registration/display_second_factor_types.html.twig',
90
            [
91
                'commonName' => $this->getIdentity()->commonName,
92
                'availableSecondFactors' => $availableTokens,
93
                'verifyEmail' => $this->emailVerificationIsRequired(),
94
            ]
95
        );
96
    }
97
98
    #[Route(
99
        path: '/second-factor/{secondFactorId}/vetting-types',
100
        name: 'ss_second_factor_vetting_types',
101
        methods:  ['GET'],
102
    )]
103
    public function displayVettingTypes(Request $request, string $secondFactorId): Response
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function displayVettingTypes()
Loading history...
104
    {
105
        /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
106
         * @var VettingTypeService
107
         */
108
        $vettingTypeService = $this->vettingTypeService;
109
        $vettingTypeCollection = $vettingTypeService->vettingTypes($this->getIdentity(), $secondFactorId);
110
111
        $nudgeSelfAssertedTokens = $vettingTypeCollection->isPreferred(VettingTypeInterface::SELF_ASSERTED_TOKENS);
112
        $nudgeRaVetting = $vettingTypeCollection->isPreferred(VettingTypeInterface::ON_PREMISE);
113
114
        // Nudging section: helping the Identity into choosing the right vetting type:
115
116
        // Option 1: A self-asserted token registration nudge was requested via query string (?activate=self)
117
        if ($nudgeSelfAssertedTokens && $vettingTypeCollection->allowSelfAssertedTokens()) {
118
            $this->logger->notice('Nudging (forcing) self-asserted token registration');
119
            return $this->forward(
120
                'SurfnetStepupSelfServiceSelfServiceBundle:SelfAssertedTokens:selfAssertedTokenRegistration',
121
                ['secondFactorId' => $secondFactorId]
122
            );
123
        }
124
125
        // Option 2: A ra-vetting nudge was requested via query string (?activate=ra)
126
        if ($nudgeRaVetting) {
127
            $this->logger->notice('Nudging (forcing) RA vetting');
128
            return $this->forward(
129
                'SurfnetStepupSelfServiceSelfServiceBundle:Registration:sendRegistrationEmail',
130
                ['secondFactorId' => $secondFactorId]
131
            );
132
        }
133
134
        // Option 3: non-formal nudge, skip over selection screen. As only ra vetting is available.
135
        if (!$vettingTypeCollection->allowSelfVetting() && !$vettingTypeCollection->allowSelfAssertedTokens()) {
136
            $this->logger
137
                ->notice(
138
                    'Skipping ahead to the RA vetting option as self vetting or self-asserted tokens are not allowed'
139
                );
140
            return $this->forward(
141
                'SurfnetStepupSelfServiceSelfServiceBundle:Registration:sendRegistrationEmail',
142
                ['secondFactorId' => $secondFactorId]
143
            );
144
        }
145
146
        $institution = $this->getIdentity()->institution;
147
        $currentLocale = $request->getLocale();
148
        $vettingTypeHint = $vettingTypeService->vettingTypeHint($institution, $currentLocale);
149
150
        return $this->render(
151
            'registration/display_vetting_types.html.twig',
152
            [
153
                'allowSelfVetting' => $vettingTypeCollection->allowSelfVetting(),
154
                'allowSelfAssertedTokens' => $vettingTypeCollection->allowSelfAssertedTokens(),
155
                'hasVettingTypeHint' => !is_null($vettingTypeHint),
156
                'vettingTypeHint' => $vettingTypeHint,
157
                'verifyEmail' => $this->emailVerificationIsRequired(),
158
                'secondFactorId' => $secondFactorId,
159
            ]
160
        );
161
    }
162
163
164
    #[Route(
165
        path: '/registration/{secondFactorId}/email-verification-email-sent',
166
        name: 'ss_registration_email_verification_email_sent',
167
        methods: ['GET'],
168
    )]
169
    public function emailVerificationEmailSent(): Response
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function emailVerificationEmailSent()
Loading history...
170
    {
171
        return $this->render(
172
            view: 'registration/email_verification_email_sent.html.twig',
173
            parameters: ['email' => $this->getIdentity()->email]);
0 ignored issues
show
Coding Style introduced by
This line of the multi-line function call does not seem to be indented correctly. Expected 8 spaces, but found 12.
Loading history...
Coding Style introduced by
For multi-line function calls, the closing parenthesis should be on a new line.

If a function call spawns multiple lines, the coding standard suggests to move the closing parenthesis to a new line:

someFunctionCall(
    $firstArgument,
    $secondArgument,
    $thirdArgument
); // Closing parenthesis on a new line.
Loading history...
174
    }
175
176
    #[Route(
177
        path: '/verify-email',
178
        name: 'ss_registration_verify_email',
179
        methods: ['GET'],
180
    )]
181
    public function verifyEmail(Request $request): Response
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function verifyEmail()
Loading history...
182
    {
183
        $nonce = $request->query->get('n', '');
184
        $identityId = $this->getIdentity()->id;
185
        $secondFactor = $this->secondFactorService->findUnverifiedByVerificationNonce($identityId, $nonce);
186
187
        if ($secondFactor === null) {
188
            throw new NotFoundHttpException('No second factor can be verified using this URL.');
189
        }
190
191
        if ($this->secondFactorService->verifyEmail($identityId, $nonce)) {
192
            return $this->redirectToRoute(
193
                'ss_second_factor_vetting_types',
194
                ['secondFactorId' => $secondFactor->id]
195
            );
196
        }
197
198
        return $this->render('registration/verify_email.html.twig');
199
    }
200
201
    /**
0 ignored issues
show
Coding Style introduced by
Parameter $secondFactorId should have a doc-comment as per coding-style.
Loading history...
202
     * Intermediate action where the registration mail is sent. After which the
203
     * email-sent page is displayed. Preventing the mail message from being sent
204
     * over and over again when the user performs a page reload.
205
     */
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...
206
    #[Route(
207
        path: '/registration/{secondFactorId}/send-registration-email',
208
        name: 'ss_registration_send_registration_email',
209
        methods: ['GET'],
210
    )]
211
212
    public function sendRegistrationEmail(string $secondFactorId): RedirectResponse
213
    {
214
        // Send the registration email
215
        $this->raService
216
            ->sendRegistrationMailMessage($this->getIdentity()->id, $secondFactorId);
217
        return $this->redirectToRoute(
218
            'ss_registration_registration_email_sent',
219
            ['secondFactorId' => $secondFactorId]
220
        );
221
    }
222
223
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
224
     * @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...
225
     * @return Response
0 ignored issues
show
Coding Style introduced by
Tag @return cannot be grouped with parameter tags in a doc comment
Loading history...
226
     */
227
    #[Route(
228
        path: '/registration/{secondFactorId}/registration-email-sent',
229
        name: 'ss_registration_registration_email_sent',
230
        methods: ['GET'],
231
    )]
232
    public function registrationEmailSent($secondFactorId): Response
233
    {
234
        $parameters = $this->buildRegistrationActionParameters($secondFactorId);
235
        // Report that it was sent
236
        return $this->render(
237
            'registration/registration_email_sent.html.twig',
238
            $parameters
239
        );
240
    }
241
242
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
243
     * @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...
244
     * @return Response
0 ignored issues
show
Coding Style introduced by
Tag @return cannot be grouped with parameter tags in a doc comment
Loading history...
245
     * TODO: pdf generation in dedicated service
246
     */
247
    #[Route(
248
        path: '/registration/{secondFactorId}/registration-pdf',
249
        name: 'ss_registration_registration_pdf',
250
        methods: ['GET'],
251
    )]
252
    public function registrationPdf($secondFactorId): Response
253
    {
254
        $parameters = $this->buildRegistrationActionParameters($secondFactorId);
255
256
        $response = $this->render(
257
            'registration/registration_email_sent_pdf.html.twig',
258
            $parameters
259
        );
260
        $content = $response->getContent();
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(string $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->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
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...
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->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