-
Notifications
You must be signed in to change notification settings - Fork 36
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1958 from digitalfabrik/develop
Release `2022.12.2`
- Loading branch information
Showing
32 changed files
with
1,544 additions
and
409 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
108 changes: 108 additions & 0 deletions
108
integreat_cms/cms/forms/users/passwordless_authentication_form.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
from django import forms | ||
from django.contrib.auth import get_user_model | ||
from django.contrib.auth.forms import UsernameField | ||
from django.db.models import Q | ||
from django.forms import ValidationError | ||
from django.utils.translation import gettext_lazy as _ | ||
|
||
from ...utils.translation_utils import gettext_many_lazy as __ | ||
|
||
|
||
class PasswordlessAuthenticationForm(forms.Form): | ||
""" | ||
Form class for authenticating users without using passwords but other authentication methods like FIDO2 or TOTP. | ||
""" | ||
|
||
username = UsernameField(widget=forms.TextInput(attrs={"autofocus": True})) | ||
|
||
#: The user who tries to login without password | ||
user = None | ||
|
||
#: The different reasons why passwordless authentication might not be possible | ||
error_messages = { | ||
"invalid_login": _("The username or email address is incorrect."), | ||
"inactive": _("This account is inactive."), | ||
"disabled": __( | ||
_("Your account is not activated for passwordless authentication."), | ||
_("Please use the default login."), | ||
), | ||
"not_available": _( | ||
"In order to use passwordless authentication, you have to configure at least one 2-factor authentication method." | ||
), | ||
} | ||
|
||
def __init__( | ||
self, | ||
*args, | ||
request=None, | ||
**kwargs, | ||
): | ||
r""" | ||
Render passwordless authentication form for HTTP GET requests | ||
:param request: Object representing the user call | ||
:type request: ~django.http.HttpRequest | ||
:param \*args: The supplied arguments | ||
:type \*args: list | ||
:param \**kwargs: The supplied keyword arguments | ||
:type \**kwargs: dict | ||
""" | ||
self.request = request | ||
super().__init__(*args, **kwargs) | ||
|
||
# Set the max length and label for the "username" field. | ||
self.username_field = get_user_model()._meta.get_field( | ||
get_user_model().USERNAME_FIELD | ||
) | ||
username_max_length = self.username_field.max_length or 254 | ||
self.fields["username"].max_length = username_max_length | ||
self.fields["username"].widget.attrs["maxlength"] = username_max_length | ||
|
||
def clean_username(self): | ||
""" | ||
Checks the input of the user to enable authentication | ||
:raises ~django.core.exceptions.ValidationError: If the given username or email is invalid | ||
:return: The cleaned username | ||
:rtype: str | ||
""" | ||
username = self.cleaned_data.get("username") | ||
|
||
self.user = ( | ||
get_user_model() | ||
.objects.filter(Q(username=username) | Q(email=username)) | ||
.first() | ||
) | ||
|
||
# Check whether valid username was given | ||
if not self.user: | ||
raise ValidationError( | ||
self.error_messages["invalid_login"], | ||
code="invalid", | ||
) | ||
|
||
# Check whether the user is allowed to login | ||
if not self.user.is_active: | ||
raise ValidationError( | ||
self.error_messages["inactive"], | ||
code="inactive", | ||
) | ||
|
||
# Check whether the user is enabled for passwordless authentication | ||
if not self.user.passwordless_authentication_enabled: | ||
raise ValidationError( | ||
self.error_messages["disabled"], | ||
code="disabled", | ||
) | ||
|
||
# Check whether a 2-factor authentication method is available | ||
if not self.user.mfa_keys.exists() and not self.user.totp_key: | ||
raise ValidationError( | ||
self.error_messages["not_available"], | ||
code="not_available", | ||
) | ||
|
||
return username |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
# Generated by Django 3.2.16 on 2022-11-06 20:59 | ||
from django.db import migrations, models | ||
|
||
|
||
class Migration(migrations.Migration): | ||
""" | ||
Add a TOTP_key field to the user model to generate time-based on-time passwords for authentication. | ||
""" | ||
|
||
dependencies = [ | ||
("cms", "0053_alter_role_name"), | ||
] | ||
|
||
operations = [ | ||
migrations.AddField( | ||
model_name="user", | ||
name="totp_key", | ||
field=models.CharField( | ||
blank=True, | ||
default=None, | ||
help_text="Will be used to generate TOTP codes", | ||
max_length=128, | ||
null=True, | ||
verbose_name="TOTP key", | ||
), | ||
), | ||
migrations.AddField( | ||
model_name="user", | ||
name="passwordless_authentication_enabled", | ||
field=models.BooleanField( | ||
default=False, | ||
help_text="Enable this option to activate the passwordless login routine for this account", | ||
verbose_name="Enable passwordless authentication", | ||
), | ||
), | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
24 changes: 24 additions & 0 deletions
24
integreat_cms/cms/templates/authentication/passwordless_login.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
{% extends "authentication/_base.html" %} | ||
{% load i18n %} | ||
{% load static %} | ||
{% load widget_tweaks %} | ||
{% block heading %} | ||
{% translate "Login" %} | ||
{% endblock heading %} | ||
{% block content %} | ||
<form method="post" class="flex flex-col"> | ||
{% csrf_token %} | ||
{% if form.errors %} | ||
<div class="bg-red-100 border-l-4 border-red-500 text-red-500 px-4 py-3 mb-4" | ||
role="alert"> | ||
<p>{{ form.errors.username }}</p> | ||
</div> | ||
{% endif %} | ||
<div class="mb-4"> | ||
<label for="{{ form.username.id_for_label }}">{% translate "Username or email address" %}</label> | ||
{% blocktrans asvar username_placeholder %}Enter your username or email address here{% endblocktrans %} | ||
{% render_field form.username|add_error_class:"border-red-500" placeholder=username_placeholder %} | ||
</div> | ||
<button class="btn">{% translate "Log in" %}</button> | ||
</form> | ||
{% endblock content %} |
Oops, something went wrong.