Skip to content

Commit 268b78d

Browse files
authored
Merge pull request #2911 from bolt/make-remember-me-configurable
Enable 'Remember me' by default, and make duration configurable
2 parents a7670d1 + 5eb1638 commit 268b78d

File tree

5 files changed

+96
-68
lines changed

5 files changed

+96
-68
lines changed

config/packages/security.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ security:
3838

3939
remember_me:
4040
secret: '%kernel.secret%'
41-
lifetime: 2592000
41+
lifetime: '%bolt.remember_lifetime%'
4242
remember_me_parameter: login[remember_me]
4343

4444
access_control:

config/services.yaml

+3
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ parameters:
1212
# default: bolt_
1313
# foo_manager: foo_
1414
bolt.backend_url: /bolt
15+
bolt.remember_lifetime: 2592000 # 30 days in seconds
1516

1617
services:
1718
# default configuration for services in *this* file
@@ -28,6 +29,8 @@ services:
2829
$projectDir: '%kernel.project_dir%'
2930
$publicFolder: '%bolt.public_folder%'
3031
$tablePrefix: '%bolt.table_prefix%'
32+
$rememberLifetime: '%bolt.remember_lifetime%'
33+
3134
_instanceof:
3235
Bolt\Menu\ExtensionBackendMenuInterface:
3336
tags: [ 'bolt.extension_backend_menu' ]

src/Form/LoginType.php

+83-64
Original file line numberDiff line numberDiff line change
@@ -1,77 +1,96 @@
11
<?php
22

3-
declare(strict_types=1);
3+
declare(strict_types=1);
44

55
namespace Bolt\Form;
66

7-
use Bolt\Form\FieldTypes\PasswordWithPreviewType;
8-
use Symfony\Component\Form\AbstractType;
9-
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
10-
use Symfony\Component\Form\Extension\Core\Type\TextType;
11-
use Symfony\Component\Form\FormBuilderInterface;
12-
use Symfony\Component\OptionsResolver\OptionsResolver;
13-
use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
14-
use Symfony\Component\Validator\Constraints\NotBlank;
15-
use Symfony\Contracts\Translation\TranslatorInterface;
7+
use Bolt\Form\FieldTypes\PasswordWithPreviewType;
8+
use Symfony\Component\Form\AbstractType;
9+
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
10+
use Symfony\Component\Form\Extension\Core\Type\HiddenType;
11+
use Symfony\Component\Form\Extension\Core\Type\TextType;
12+
use Symfony\Component\Form\FormBuilderInterface;
13+
use Symfony\Component\OptionsResolver\OptionsResolver;
14+
use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
15+
use Symfony\Component\Validator\Constraints\NotBlank;
16+
use Symfony\Contracts\Translation\TranslatorInterface;
1617

17-
class LoginType extends AbstractType
18-
{
19-
/** @var AuthenticationUtils */
20-
private $authenticationUtils;
18+
class LoginType extends AbstractType
19+
{
20+
/** @var AuthenticationUtils */
21+
private $authenticationUtils;
2122

22-
/** @var TranslatorInterface */
23-
private $translator;
23+
/** @var TranslatorInterface */
24+
private $translator;
2425

25-
public function __construct(AuthenticationUtils $authenticationUtils, TranslatorInterface $translator)
26-
{
27-
$this->authenticationUtils = $authenticationUtils;
28-
$this->translator = $translator;
29-
}
26+
/** @var int */
27+
private $rememberLifetime;
3028

31-
public function buildForm(FormBuilderInterface $builder, array $options): void
32-
{
33-
$builder
34-
->add('username', TextType::class, [
35-
'label' => 'label.username_or_email',
36-
'constraints' => [
37-
new NotBlank([
38-
'message' => $this->translator->trans('form.empty_username_email'),
39-
]),
40-
],
41-
'attr' => [
42-
'placeholder' => 'placeholder.username_or_email',
43-
],
44-
'data' => $this->authenticationUtils->getLastUsername(),
45-
])
46-
->add('password', PasswordWithPreviewType::class, [
47-
'label' => 'label.password',
48-
'constraints' => [
49-
new NotBlank([
50-
'message' => $this->translator->trans('form.empty_password'),
51-
]),
52-
],
53-
// do not show the * red star
54-
'required' => false,
55-
'attr' => [
56-
'placeholder' => 'placeholder.password',
57-
],
58-
])->add('remember_me', CheckboxType::class, [
59-
'label' => 'label.rememberme',
60-
'required' => false,
61-
]);
62-
}
29+
public function __construct(AuthenticationUtils $authenticationUtils, TranslatorInterface $translator, int $rememberLifetime = 2592000)
30+
{
31+
$this->authenticationUtils = $authenticationUtils;
32+
$this->translator = $translator;
33+
34+
// Defaults to 2592000, 30 days in seconds
35+
$this->rememberLifetime = $rememberLifetime;
36+
}
6337

64-
// https://symfony.com/doc/current/security/csrf.html#csrf-protection-in-symfony-forms
65-
public function configureOptions(OptionsResolver $resolver): void
66-
{
67-
$resolver->setDefaults([
68-
// enable/disable CSRF protection for this form
69-
'csrf_protection' => true,
70-
// the name of the hidden HTML field that stores the token
71-
'csrf_field_name' => '_token',
72-
// an arbitrary string used to generate the value of the token
73-
// using a different string for each form improves its security
74-
'csrf_token_id' => 'login_csrf_token',
38+
public function buildForm(FormBuilderInterface $builder, array $options): void
39+
{
40+
$builder
41+
->add('username', TextType::class, [
42+
'label' => 'label.username_or_email',
43+
'constraints' => [
44+
new NotBlank([
45+
'message' => $this->translator->trans('form.empty_username_email'),
46+
]),
47+
],
48+
'attr' => [
49+
'placeholder' => 'placeholder.username_or_email',
50+
],
51+
'data' => $this->authenticationUtils->getLastUsername(),
52+
])
53+
->add('password', PasswordWithPreviewType::class, [
54+
'label' => 'label.password',
55+
'constraints' => [
56+
new NotBlank([
57+
'message' => $this->translator->trans('form.empty_password'),
58+
]),
59+
],
60+
// do not show the * red star
61+
'required' => false,
62+
'attr' => [
63+
'placeholder' => 'placeholder.password',
64+
],
65+
]);
66+
67+
if ($this->rememberLifetime > 0) {
68+
$builder->add('remember_me', CheckboxType::class, [
69+
'label' => 'label.remembermeduration',
70+
'label_translation_parameters' => [
71+
'%duration%' => 0 + sprintf('%0.1f', $this->rememberLifetime / 3600 / 24),
72+
],
73+
'required' => false,
74+
'attr' => [
75+
'checked' => 'checked',
76+
],
7577
]);
78+
} else {
79+
$builder->add('remember_me', HiddenType::class);
7680
}
7781
}
82+
83+
// https://symfony.com/doc/current/security/csrf.html#csrf-protection-in-symfony-forms
84+
public function configureOptions(OptionsResolver $resolver): void
85+
{
86+
$resolver->setDefaults([
87+
// enable/disable CSRF protection for this form
88+
'csrf_protection' => true,
89+
// the name of the hidden HTML field that stores the token
90+
'csrf_field_name' => '_token',
91+
// an arbitrary string used to generate the value of the token
92+
// using a different string for each form improves its security
93+
'csrf_token_id' => 'login_csrf_token',
94+
]);
95+
}
96+
}

translations/messages.en.xlf

+6
Original file line numberDiff line numberDiff line change
@@ -2673,5 +2673,11 @@
26732673
<target>Select all</target>
26742674
</segment>
26752675
</unit>
2676+
<unit id="2hoKa1k" name="label.remembermeduration">
2677+
<segment>
2678+
<source>label.remembermeduration</source>
2679+
<target>Remember me? (%duration% days)</target>
2680+
</segment>
2681+
</unit>
26762682
</file>
26772683
</xliff>

translations/messages.nl.xlf

+3-3
Original file line numberDiff line numberDiff line change
@@ -181,13 +181,13 @@
181181
<target>Copyright</target>
182182
</segment>
183183
</unit>
184-
<unit id="IaF1MjB" name="label.rememberme">
184+
<unit id="IaF1MjB" name="label.remembermeduration">
185185
<notes>
186186
<note>templates/security/login.twig:80</note>
187187
</notes>
188188
<segment>
189-
<source>label.rememberme</source>
190-
<target>Onthoud me?</target>
189+
<source>label.remembermeduration</source>
190+
<target>Onthoud me? (%duration% dagen)</target>
191191
</segment>
192192
</unit>
193193
<unit id="k.JymqB" name="about.visit_bolt">

0 commit comments

Comments
 (0)