From 35b156fd80a590f8d8a9194be6d3fc6a8baed994 Mon Sep 17 00:00:00 2001 From: Benjamin Dauvergne Date: Fri, 3 Aug 2018 17:12:30 +0200 Subject: [PATCH] add a remember me button (fixes #25579) It simply use session.set_expiry() to augment the session duration. --- src/authentic2/app_settings.py | 4 ++++ src/authentic2/auth_frontends.py | 3 +++ src/authentic2/forms/__init__.py | 8 +++++++ tests/test_login.py | 41 ++++++++++++++++++++++++++++++++ tests/utils.py | 4 +++- tox.ini | 1 + 6 files changed, 60 insertions(+), 1 deletion(-) diff --git a/src/authentic2/app_settings.py b/src/authentic2/app_settings.py index 10bd4237..19154118 100644 --- a/src/authentic2/app_settings.py +++ b/src/authentic2/app_settings.py @@ -193,6 +193,10 @@ default_settings = dict( default={}, definition='Exclusion filter (as in QuerySet.exclude() to apply to User queryset before ' 'authentication'), + A2_USER_REMEMBER_ME=Setting( + default=None, + definition='Session duration as seconds when using the remember me ' + 'checkbox. Truthiness activates the checkbox.'), A2_LOGIN_REDIRECT_AUTHENTICATED_USERS_TO_HOMEPAGE=Setting( default=False, definition='Redirect authenticated users to homepage'), diff --git a/src/authentic2/auth_frontends.py b/src/authentic2/auth_frontends.py index 36917dc5..0a66e57d 100644 --- a/src/authentic2/auth_frontends.py +++ b/src/authentic2/auth_frontends.py @@ -34,6 +34,9 @@ class LoginPasswordBackend(object): how = 'password-on-https' else: how = 'password' + if form.cleaned_data.get('remember_me'): + request.session['remember_me'] = True + request.session.set_expiry(app_settings.A2_USER_REMEMBER_ME) return utils.login(request, form.get_user(), how, service_slug=request.GET.get(constants.SERVICE_FIELD_NAME)) context['form'] = form diff --git a/src/authentic2/forms/__init__.py b/src/authentic2/forms/__init__.py index 97f4c4ab..ee887e14 100644 --- a/src/authentic2/forms/__init__.py +++ b/src/authentic2/forms/__init__.py @@ -159,6 +159,11 @@ def modelform_factory(model, **kwargs): class AuthenticationForm(auth_forms.AuthenticationForm): password = PasswordField(label=_('Password')) + remember_me = forms.BooleanField( + initial=False, + required=False, + label=_('Remember me'), + help_text=_('Do not ask for authentication next time')) def __init__(self, *args, **kwargs): super(AuthenticationForm, self).__init__(*args, **kwargs) @@ -167,6 +172,9 @@ class AuthenticationForm(auth_forms.AuthenticationForm): duration=app_settings.A2_LOGIN_EXPONENTIAL_RETRY_TIMEOUT_DURATION, factor=app_settings.A2_LOGIN_EXPONENTIAL_RETRY_TIMEOUT_FACTOR) + if not app_settings.A2_USER_REMEMBER_ME: + del self.fields['remember_me'] + if self.request: self.remote_addr = self.request.META['REMOTE_ADDR'] else: diff --git a/tests/test_login.py b/tests/test_login.py index 6d8dddaf..476c1f50 100644 --- a/tests/test_login.py +++ b/tests/test_login.py @@ -76,3 +76,44 @@ def test_encoded_utf8_in_next_url(app, db): response = response.follow() needle = 'next=%s' % quote(url) assert needle in response.content + + +def test_session_expire(app, simple_user, freezer): + freezer.move_to('2018-01-01') + # Verify session work as usual + login(app, simple_user) + response = app.get('/') + assert simple_user.first_name in response + freezer.move_to('2018-01-15') + response = app.get('/') + assert simple_user.first_name not in response + + +def test_session_remember_me_ok(app, settings, simple_user, freezer): + settings.A2_USER_REMEMBER_ME = 3600 * 24 * 30 + freezer.move_to('2018-01-01') + # Verify session are longer + login(app, simple_user, remember_me=True) + + response = app.get('/') + assert simple_user.first_name in response + + # less than 30 days, session is still alive + freezer.move_to('2018-01-30') + response = app.get('/') + assert simple_user.first_name in response + + +def test_session_remember_me_nok(app, settings, simple_user, freezer): + settings.A2_USER_REMEMBER_ME = 3600 * 24 * 30 + freezer.move_to('2018-01-01') + # Verify session are longer + login(app, simple_user, remember_me=True) + + response = app.get('/') + assert simple_user.first_name in response + + # more than 30 days, session is dead + freezer.move_to('2018-01-31') + response = app.get('/') + assert simple_user.first_name not in response diff --git a/tests/utils.py b/tests/utils.py index 00d3b28f..62273761 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -16,7 +16,7 @@ skipif_sqlite = pytest.mark.skipif('sqlite' in settings.DATABASES['default']['EN reason='this test does not work with sqlite') -def login(app, user, path=None, password=None): +def login(app, user, path=None, password=None, remember_me=None): if path: login_page = app.get(path, status=302).maybe_follow() else: @@ -26,6 +26,8 @@ def login(app, user, path=None, password=None): form.set('username', user.username if hasattr(user, 'username') else user) # password is supposed to be the same as username form.set('password', password or user.username) + if remember_me is not None: + form.set('remember_me', bool(remember_me)) response = form.submit(name='login-password-submit').follow() if path: assert response.request.path == path diff --git a/tox.ini b/tox.ini index b52b5aed..e9f54f51 100644 --- a/tox.ini +++ b/tox.ini @@ -43,6 +43,7 @@ deps = pyquery httmock pytz + pytest-freezegun commands = ./getlasso.sh authentic: py.test {env:FAST:} {env:REUSEDB:} {env:COVERAGE:} {posargs:tests/ --random} -- 2.18.0