2019-02-25 11:29:40 +00:00
|
|
|
"""passbook OTP Forms"""
|
|
|
|
|
|
|
|
from django import forms
|
2019-03-10 17:34:09 +00:00
|
|
|
from django.contrib.admin.widgets import FilteredSelectMultiple
|
2019-02-25 11:29:40 +00:00
|
|
|
from django.core.validators import RegexValidator
|
|
|
|
from django.utils.safestring import mark_safe
|
2020-02-27 15:39:30 +00:00
|
|
|
from django.utils.translation import gettext_lazy as _
|
2019-10-07 14:33:48 +00:00
|
|
|
from django_otp.models import Device
|
2019-02-25 11:29:40 +00:00
|
|
|
|
2019-10-07 14:33:48 +00:00
|
|
|
from passbook.factors.otp.models import OTPFactor
|
2020-05-07 19:30:52 +00:00
|
|
|
from passbook.flows.forms import GENERAL_FIELDS
|
2019-02-25 11:29:40 +00:00
|
|
|
|
2019-12-31 11:51:16 +00:00
|
|
|
OTP_CODE_VALIDATOR = RegexValidator(
|
|
|
|
r"^[0-9a-z]{6,8}$", _("Only alpha-numeric characters are allowed.")
|
|
|
|
)
|
2019-02-25 11:29:40 +00:00
|
|
|
|
|
|
|
|
|
|
|
class PictureWidget(forms.widgets.Widget):
|
|
|
|
"""Widget to render value as img-tag"""
|
|
|
|
|
|
|
|
def render(self, name, value, attrs=None, renderer=None):
|
2019-12-31 11:51:16 +00:00
|
|
|
return mark_safe(f'<img src="{value}" />') # nosec
|
2019-02-25 11:29:40 +00:00
|
|
|
|
|
|
|
|
|
|
|
class OTPVerifyForm(forms.Form):
|
|
|
|
"""Simple Form to verify OTP Code"""
|
|
|
|
|
2019-12-31 11:51:16 +00:00
|
|
|
order = ["code"]
|
|
|
|
|
|
|
|
code = forms.CharField(
|
|
|
|
label=_("Code"),
|
|
|
|
validators=[OTP_CODE_VALIDATOR],
|
|
|
|
widget=forms.TextInput(attrs={"autocomplete": "off", "placeholder": "Code"}),
|
|
|
|
)
|
2019-02-25 11:29:40 +00:00
|
|
|
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
|
|
super().__init__(*args, **kwargs)
|
|
|
|
# This is a little helper so the field is focused by default
|
2019-12-31 11:51:16 +00:00
|
|
|
self.fields["code"].widget.attrs.update(
|
|
|
|
{"autofocus": "autofocus", "autocomplete": "off"}
|
|
|
|
)
|
2019-02-25 11:29:40 +00:00
|
|
|
|
|
|
|
|
|
|
|
class OTPSetupForm(forms.Form):
|
|
|
|
"""OTP Setup form"""
|
2019-12-31 11:51:16 +00:00
|
|
|
|
|
|
|
title = _("Set up OTP")
|
2019-10-07 14:33:48 +00:00
|
|
|
device: Device = None
|
2019-12-31 11:51:16 +00:00
|
|
|
qr_code = forms.CharField(
|
|
|
|
widget=PictureWidget,
|
|
|
|
disabled=True,
|
|
|
|
required=False,
|
|
|
|
label=_("Scan this Code with your OTP App."),
|
|
|
|
)
|
|
|
|
code = forms.CharField(
|
|
|
|
label=_("Code"),
|
|
|
|
validators=[OTP_CODE_VALIDATOR],
|
|
|
|
widget=forms.TextInput(attrs={"placeholder": _("One-Time Password")}),
|
|
|
|
)
|
2019-02-25 11:29:40 +00:00
|
|
|
|
|
|
|
tokens = forms.MultipleChoiceField(disabled=True, required=False)
|
|
|
|
|
|
|
|
def clean_code(self):
|
|
|
|
"""Check code with new otp device"""
|
|
|
|
if self.device is not None:
|
2019-12-31 11:51:16 +00:00
|
|
|
if not self.device.verify_token(int(self.cleaned_data.get("code"))):
|
2019-02-25 11:29:40 +00:00
|
|
|
raise forms.ValidationError(_("OTP Code does not match"))
|
2019-12-31 11:51:16 +00:00
|
|
|
return self.cleaned_data.get("code")
|
|
|
|
|
2019-02-25 11:29:40 +00:00
|
|
|
|
|
|
|
class OTPFactorForm(forms.ModelForm):
|
|
|
|
"""Form to edit OTPFactor instances"""
|
|
|
|
|
|
|
|
class Meta:
|
|
|
|
|
|
|
|
model = OTPFactor
|
2019-12-31 11:51:16 +00:00
|
|
|
fields = GENERAL_FIELDS + ["enforced"]
|
2019-02-25 11:29:40 +00:00
|
|
|
widgets = {
|
2019-12-31 11:51:16 +00:00
|
|
|
"name": forms.TextInput(),
|
|
|
|
"order": forms.NumberInput(),
|
|
|
|
"policies": FilteredSelectMultiple(_("policies"), False),
|
2019-02-25 11:29:40 +00:00
|
|
|
}
|
2020-02-27 15:39:30 +00:00
|
|
|
help_texts = {
|
|
|
|
"policies": _(
|
|
|
|
"Policies which determine if this factor applies to the current user."
|
|
|
|
)
|
|
|
|
}
|