Skip to content

Commit bb9d3a9

Browse files
committed
Transform RT delete into SF form
Works, - needs translation token attention - warn about new translations - Show ID of safe-store RT? - test coverage?
1 parent 72caae1 commit bb9d3a9

3 files changed

Lines changed: 106 additions & 10 deletions

File tree

src/Surfnet/StepupSelfService/SelfServiceBundle/Controller/RecoveryTokenController.php

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
use Surfnet\StepupSelfService\SelfServiceBundle\Command\PromiseSafeStorePossessionCommand;
3535
use Surfnet\StepupSelfService\SelfServiceBundle\Command\RevokeRecoveryTokenCommand;
3636
use Surfnet\StepupSelfService\SelfServiceBundle\Exception\LogicException;
37+
use Surfnet\StepupSelfService\SelfServiceBundle\Form\Type\RevokeRecoveryTokenType;
3738
use Surfnet\StepupSelfService\SelfServiceBundle\Form\Type\PromiseSafeStorePossessionType;
3839
use Surfnet\StepupSelfService\SelfServiceBundle\Service\SecondFactorService;
3940
use Surfnet\StepupSelfService\SelfServiceBundle\Service\SelfAssertedTokens\AuthenticationRequestFactory;
@@ -47,6 +48,7 @@
4748
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
4849
use Symfony\Component\Routing\Attribute\Route;
4950
use Symfony\Component\Security\Core\Exception\AuthenticationException;
51+
use function sprintf;
5052

5153
/**
5254
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
@@ -207,29 +209,42 @@ public function proveSmsPossession(Request $request): Response
207209
#[Route(
208210
path: '/recovery-token/delete/{recoveryTokenId}',
209211
name: 'ss_recovery_token_delete',
210-
methods: ['GET'],
212+
methods: ['GET', 'POST'],
211213
)]
212-
public function delete(string $recoveryTokenId): Response
214+
public function delete(Request $request, string $recoveryTokenId): Response
213215
{
214216
$this->assertRecoveryTokenInPossession($recoveryTokenId, $this->getUser()->getIdentity());
215217
try {
216218
$recoveryToken = $this->recoveryTokenService->getRecoveryToken($recoveryTokenId);
217219
$command = new RevokeRecoveryTokenCommand();
218220
$command->identity = $this->getUser()->getIdentity();
219221
$command->recoveryToken = $recoveryToken;
220-
$executionResult = $this->safeStoreService->revokeRecoveryToken($command);
221-
if ($executionResult->getErrors() !== []) {
222-
$this->addFlash('error', 'ss.form.recovery_token.delete.success');
223-
foreach ($executionResult->getErrors() as $error) {
224-
$this->logger->error(sprintf('Recovery Token revocation failed with message: "%s"', $error));
222+
223+
$form = $this->createForm(RevokeRecoveryTokenType::class, $command)->handleRequest($request);
224+
225+
if ($form->isSubmitted() && $form->isValid()) {
226+
$executionResult = $this->safeStoreService->revokeRecoveryToken($command);
227+
228+
if ($executionResult->isSuccessful()) {
229+
$this->addFlash('error', 'ss.form.recovery_token.delete.success');
230+
} else {
231+
foreach ($executionResult->getErrors() as $error) {
232+
$this->logger->error(sprintf('Recovery Token revocation failed with message: "%s"', $error));
233+
}
234+
$this->addFlash('error', 'ss.form.recovery_token.delete.failed');
225235
}
226-
return $this->redirect($this->generateUrl('ss_second_factor_list'));
236+
return $this->redirectToRoute('ss_second_factor_list');
227237
}
228238
} catch (NotFoundException) {
229239
throw new LogicException('Identity %s tried to remove an unpossessed recovery token');
230240
}
231-
$this->addFlash('success', 'ss.form.recovery_token.delete.success');
232-
return $this->redirect($this->generateUrl('ss_second_factor_list'));
241+
return $this->render(
242+
'second_factor/revoke-recovery-token.html.twig',
243+
[
244+
'form' => $form->createView(),
245+
'recoveryToken' => $recoveryToken,
246+
]
247+
);
233248
}
234249

235250
/**
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
<?php
2+
3+
declare(strict_types = 1);
4+
5+
/**
6+
* Copyright 2025 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+
*/
20+
21+
namespace Surfnet\StepupSelfService\SelfServiceBundle\Form\Type;
22+
23+
use Surfnet\StepupSelfService\SelfServiceBundle\Command\RevokeRecoveryTokenCommand;
24+
use Symfony\Component\Form\AbstractType;
25+
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
26+
use Symfony\Component\Form\FormBuilderInterface;
27+
use Symfony\Component\OptionsResolver\OptionsResolver;
28+
29+
class RevokeRecoveryTokenType extends AbstractType
30+
{
31+
public function buildForm(FormBuilderInterface $builder, array $options): void
32+
{
33+
$builder->add('delete', SubmitType::class, [
34+
'label' => 'ss.form.ss_revoke_recovery_token.revoke',
35+
'attr' => [ 'class' => 'btn btn-danger pull-right' ],
36+
]);
37+
$builder->add('cancel', AnchorType::class, [
38+
'label' => 'ss.form.ss_revoke_recovery_token.cancel',
39+
'attr' => [ 'class' => 'btn pull-right' ],
40+
'route' => 'ss_second_factor_list',
41+
]);
42+
}
43+
44+
public function configureOptions(OptionsResolver $resolver): void
45+
{
46+
$resolver->setDefaults([
47+
'data_class' => RevokeRecoveryTokenCommand::class,
48+
]);
49+
}
50+
51+
public function getBlockPrefix(): string
52+
{
53+
return 'ss_revoke_recovery_token';
54+
}
55+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
{% extends "base.html.twig" %}
2+
{% import _self as macro %}
3+
4+
{% block page_title %}{{ 'ss.recovery_token.revoke.title'|trans }}{% endblock %}
5+
6+
{% block content %}
7+
<h2>{{ block('page_title') }}</h2>
8+
9+
<p>{{ 'ss.recovery_token.revoke.text.are_you_sure'|trans }}</p>
10+
11+
<table class="table table-bordered">
12+
<tbody>
13+
<tr>
14+
<th scope="row">{{ 'ss.recovery_token.revoke.table_header.recovery_token.method'|trans }}</th>
15+
<th scope="row">{{ 'ss.recovery_token.revoke.table_header.recovery_token.identifier'|trans }}</th>
16+
17+
</tr>
18+
<tr>
19+
<td>{{ recoveryToken.type|trans }}</td>
20+
<td>{% if recoveryToken.type == 'sms' %}{{ recoveryToken.identifier }}{% else %}-{% endif %}</td>
21+
</tr>
22+
</tbody>
23+
</table>
24+
25+
{{ form(form) }}
26+
{% endblock %}

0 commit comments

Comments
 (0)