Passed
Pull Request — main (#308)
by Paul
13:29 queued 06:46
created

RegistrationController::registrationEmailSent()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 12
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 8
nc 1
nop 1
dl 0
loc 12
rs 10
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * Copyright 2014 SURFnet bv
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
 */
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...
18
19
namespace Surfnet\StepupSelfService\SelfServiceBundle\Controller;
20
21
use DateInterval;
22
use Mpdf\Mpdf;
23
use Mpdf\Output\Destination as MpdfDestination;
24
use Psr\Log\LoggerInterface;
25
use Surfnet\StepupMiddlewareClientBundle\Identity\Dto\VerifiedSecondFactor;
26
use Surfnet\StepupSelfService\SamlStepupProviderBundle\Provider\ViewConfig;
27
use Surfnet\StepupSelfService\SelfServiceBundle\Service\InstitutionConfigurationOptionsService;
28
use Surfnet\StepupSelfService\SelfServiceBundle\Service\RaLocationService;
29
use Surfnet\StepupSelfService\SelfServiceBundle\Service\RaService;
30
use Surfnet\StepupSelfService\SelfServiceBundle\Service\SecondFactorAvailabilityHelper;
31
use Surfnet\StepupSelfService\SelfServiceBundle\Service\SecondFactorService;
32
use Surfnet\StepupSelfService\SelfServiceBundle\Service\VettingTypeService;
33
use Surfnet\StepupSelfService\SelfServiceBundle\Value\VettingType\VettingTypeInterface;
34
use Symfony\Bridge\Twig\Attribute\Template;
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\Annotation\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
    ) {
51
        parent::__construct($logger, $configurationOptionsService);
52
    }
53
54
55
    #[Template('registration/display_second_factor_types.html.twig')]
56
    #[Route(
57
        path: '/registration/select-token',
58
        name: 'ss_registration_display_types',
59
        methods: ['GET'],
60
    )]
61
    public function displaySecondFactorTypes(): Response|array
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 [
89
            'commonName' => $this->getIdentity()->commonName,
90
            'availableSecondFactors' => $availableTokens,
91
            'verifyEmail' => $this->emailVerificationIsRequired(),
92
        ];
93
    }
94
95
    #[Template('registration/display_vetting_types.html.twig')]
96
    #[Route(
97
        path: '/second-factor/{secondFactorId}/vetting-types',
98
        name: 'ss_second_factor_vetting_types',
99
        methods:  ['GET'],
100
    )]
101
    public function displayVettingTypes(Request $request, string $secondFactorId): array|Response
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function displayVettingTypes()
Loading history...
102
    {
103
        /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
104
         * @var VettingTypeService
105
         */
106
        $vettingTypeService = $this->vettingTypeService;
107
        $vettingTypeCollection = $vettingTypeService->vettingTypes($this->getIdentity(), $secondFactorId);
108
109
        $nudgeSelfAssertedTokens = $vettingTypeCollection->isPreferred(VettingTypeInterface::SELF_ASSERTED_TOKENS);
110
        $nudgeRaVetting = $vettingTypeCollection->isPreferred(VettingTypeInterface::ON_PREMISE);
111
112
        // Nudging section: helping the Identity into choosing the right vetting type:
113
114
        // Option 1: A self-asserted token registration nudge was requested via query string (?activate=self)
115
        if ($nudgeSelfAssertedTokens && $vettingTypeCollection->allowSelfAssertedTokens()) {
116
            $this->logger->notice('Nudging (forcing) self-asserted token registration');
117
            return $this->forward(
118
                'SurfnetStepupSelfServiceSelfServiceBundle:SelfAssertedTokens:selfAssertedTokenRegistration',
119
                ['secondFactorId' => $secondFactorId]
120
            );
121
        }
122
123
        // Option 2: A ra-vetting nudge was requested via query string (?activate=ra)
124
        if ($nudgeRaVetting) {
125
            $this->logger->notice('Nudging (forcing) RA vetting');
126
            return $this->forward(
127
                'SurfnetStepupSelfServiceSelfServiceBundle:Registration:sendRegistrationEmail',
128
                ['secondFactorId' => $secondFactorId]
129
            );
130
        }
131
132
        // Option 3: non-formal nudge, skip over selection screen. As only ra vetting is available.
133
        if (!$vettingTypeCollection->allowSelfVetting() && !$vettingTypeCollection->allowSelfAssertedTokens()) {
134
            $this->logger
135
                ->notice(
136
                    'Skipping ahead to the RA vetting option as self vetting or self-asserted tokens are not allowed'
137
                );
138
            return $this->forward(
139
                'SurfnetStepupSelfServiceSelfServiceBundle:Registration:sendRegistrationEmail',
140
                ['secondFactorId' => $secondFactorId]
141
            );
142
        }
143
144
        $institution = $this->getIdentity()->institution;
145
        $currentLocale = $request->getLocale();
146
        $vettingTypeHint = $vettingTypeService->vettingTypeHint($institution, $currentLocale);
147
148
        return [
149
            'allowSelfVetting' => $vettingTypeCollection->allowSelfVetting(),
150
            'allowSelfAssertedTokens' => $vettingTypeCollection->allowSelfAssertedTokens(),
151
            'hasVettingTypeHint' => !is_null($vettingTypeHint),
152
            'vettingTypeHint' => $vettingTypeHint,
153
            'verifyEmail' => $this->emailVerificationIsRequired(),
154
            'secondFactorId' => $secondFactorId,
155
        ];
156
    }
157
158
    #[Template('registration/email_verification_email_sent.html.twig')]
159
    #[Route(
160
        path: '/registration/{secondFactorId}/email-verification-email-sent',
161
        name: 'ss_registration_email_verification_email_sent',
162
        methods: ['GET'],
163
    )]
164
    public function emailVerificationEmailSent(): array
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function emailVerificationEmailSent()
Loading history...
165
    {
166
        return ['email' => $this->getIdentity()->email];
167
    }
168
169
170
    #[Template('registration/verify_email.html.twig')]
171
    #[Route(
172
        path: '/verify-email',
173
        name: 'ss_registration_verify_email',
174
        methods: ['GET'],
175
    )]
176
    public function verifyEmail(Request $request): RedirectResponse|array
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function verifyEmail()
Loading history...
177
    {
178
        $nonce = $request->query->get('n', '');
179
        $identityId = $this->getIdentity()->id;
180
181
182
183
        $secondFactor = $this->secondFactorService->findUnverifiedByVerificationNonce($identityId, $nonce);
184
185
        if ($secondFactor === null) {
186
            throw new NotFoundHttpException('No second factor can be verified using this URL.');
187
        }
188
189
        if ($this->secondFactorService->verifyEmail($identityId, $nonce)) {
190
            return $this->redirectToRoute(
191
                'ss_second_factor_vetting_types',
192
                ['secondFactorId' => $secondFactor->id]
193
            );
194
        }
195
196
        return [];
197
    }
198
199
    /**
0 ignored issues
show
Coding Style introduced by
Parameter $secondFactorId should have a doc-comment as per coding-style.
Loading history...
200
     * Intermediate action where the registration mail is sent. After which the
201
     * email-sent page is displayed. Preventing the mail message from being sent
202
     * over and over again when the user performs a page reload.
203
     */
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...
204
    #[Route(
205
        path: '/registration/{secondFactorId}/send-registration-email',
206
        name: 'ss_registration_send_registration_email',
207
        methods: ['GET'],
208
    )]
209
210
    public function sendRegistrationEmail(string $secondFactorId): RedirectResponse
211
    {
212
        // Send the registration email
213
        $this->container->get('self_service.service.ra')
214
            ->sendRegistrationMailMessage($this->getIdentity()->id, $secondFactorId);
215
        return $this->redirectToRoute(
216
            'ss_registration_registration_email_sent',
217
            ['secondFactorId' => $secondFactorId]
218
        );
219
    }
220
221
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
222
     * @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...
223
     * @return Response
0 ignored issues
show
Coding Style introduced by
Tag @return cannot be grouped with parameter tags in a doc comment
Loading history...
224
     */
225
    #[Route(
226
        path: '/registration/{secondFactorId}/registration-email-sent',
227
        name: 'ss_registration_registration_email_sent',
228
        methods: ['GET'],
229
    )]
230
    public function registrationEmailSent($secondFactorId): Response
231
    {
232
        $parameters = $this->buildRegistrationActionParameters($secondFactorId);
233
        // Report that it was sent
234
        return $this->render(
235
            'registration:registration_email_sent.html.twig',
236
            $parameters
237
        );
238
    }
239
240
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
241
     * @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...
242
     * @return Response
0 ignored issues
show
Coding Style introduced by
Tag @return cannot be grouped with parameter tags in a doc comment
Loading history...
243
     */
244
    #[Route(
245
        path: '/registration/{secondFactorId}/registration-pdf',
246
        name: 'ss_registration_registration_pdf',
247
        methods: ['GET'],
248
    )]
249
    public function registrationPdf($secondFactorId): Response
250
    {
251
        $parameters = $this->buildRegistrationActionParameters($secondFactorId);
252
253
        $response = $this->render(
254
            'registration:registration_email_sent_pdf.html.twig',
255
            $parameters
256
        );
257
        $content = $response->getContent();
258
259
260
        $mpdf = new Mpdf(
261
            ['tempDir' => sys_get_temp_dir()]
262
        );
263
        $mpdf->setLogger($this->container->get('logger'));
264
265
        $mpdf->WriteHTML($content);
266
        $output = $mpdf->Output('registration-code.pdf', MpdfDestination::STRING_RETURN);
267
268
        $response = new Response($output);
269
        $disposition = $response->headers->makeDisposition(
270
            ResponseHeaderBag::DISPOSITION_ATTACHMENT,
271
            'registration-code.pdf'
272
        );
273
274
        $response->headers->set('Content-Disposition', $disposition);
275
        $response->headers->set('Content-Description', 'File Transfer');
276
        $response->headers->set('Content-Transfer-Encoding', 'binary');
277
        $response->headers->set('Cache-Control', 'public, must-revalidate, max-age=0');
278
        $response->headers->set('Pragma', 'public');
279
        $response->headers->set('Expires', 'Sat, 26 Jul 1997 05:00:00 GMT');
280
        $response->headers->set('Last-Modified', '' . gmdate('D, d M Y H:i:s') . ' GMT');
281
        $response->headers->set('Content-Type', 'application/pdf');
282
283
        return $response;
284
    }
285
286
287
    private function buildRegistrationActionParameters($secondFactorId): array
0 ignored issues
show
Coding Style introduced by
Private method name "RegistrationController::buildRegistrationActionParameters" must be prefixed with an underscore
Loading history...
Coding Style introduced by
Missing doc comment for function buildRegistrationActionParameters()
Loading history...
288
    {
289
        $identity = $this->getIdentity();
290
291
        /** @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...
292
        $secondFactor = $this->container->get('surfnet_stepup_self_service_self_service.service.second_factor')
293
            ->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
        /** @var RaService $raService */
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...
307
        $raService         = $this->container->get('self_service.service.ra');
308
        /** @var RaLocationService $raLocationService */
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...
309
        $raLocationService = $this->container->get('self_service.service.ra_location');
310
311
        $institutionConfigurationOptions = $this->container->get('self_service.service.institution_configuration_options')
312
            ->getInstitutionConfigurationOptionsFor($identity->institution);
313
314
        if ($institutionConfigurationOptions->useRaLocations) {
315
            $parameters['raLocations'] = $raLocationService->listRaLocationsFor($identity->institution);
316
        } elseif (!$institutionConfigurationOptions->showRaaContactInformation) {
317
            $parameters['ras'] = $raService->listRasWithoutRaas($identity->institution);
318
        } else {
319
            $parameters['ras'] = $raService->listRas($identity->institution);
320
        }
321
322
        return $parameters;
323
    }
324
}
325