stages/otp_time: Cleanup, use django_otp's URL generator
This commit is contained in:
parent
7c191b0984
commit
a76eb4d30f
|
@ -30,17 +30,24 @@ class SetupForm(forms.Form):
|
||||||
code = forms.CharField(
|
code = forms.CharField(
|
||||||
label=_("Code"),
|
label=_("Code"),
|
||||||
validators=[OTP_CODE_VALIDATOR],
|
validators=[OTP_CODE_VALIDATOR],
|
||||||
widget=forms.TextInput(attrs={"placeholder": _("One-Time Password")}),
|
widget=forms.TextInput(
|
||||||
|
attrs={
|
||||||
|
"autocomplete": "off",
|
||||||
|
"placeholder": "Code",
|
||||||
|
"autofocus": "autofocus",
|
||||||
|
}
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
def __init__(self, device, qr_code, *args, **kwargs):
|
def __init__(self, device, qr_code, *args, **kwargs):
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
self.device = device
|
||||||
self.fields["qr_code"].initial = qr_code
|
self.fields["qr_code"].initial = qr_code
|
||||||
|
|
||||||
def clean_code(self):
|
def clean_code(self):
|
||||||
"""Check code with new otp device"""
|
"""Check code with new otp device"""
|
||||||
if self.device is not None:
|
if self.device is not None:
|
||||||
if not self.device.verify_token(int(self.cleaned_data.get("code"))):
|
if not self.device.verify_token(self.cleaned_data.get("code")):
|
||||||
raise forms.ValidationError(_("OTP Code does not match"))
|
raise forms.ValidationError(_("OTP Code does not match"))
|
||||||
return self.cleaned_data.get("code")
|
return self.cleaned_data.get("code")
|
||||||
|
|
||||||
|
|
|
@ -3,3 +3,4 @@
|
||||||
INSTALLED_APPS = [
|
INSTALLED_APPS = [
|
||||||
"django_otp.plugins.otp_totp",
|
"django_otp.plugins.otp_totp",
|
||||||
]
|
]
|
||||||
|
OTP_TOTP_ISSUER = "passbook"
|
||||||
|
|
|
@ -1,20 +1,15 @@
|
||||||
"""TOTP Setup stage"""
|
"""TOTP Setup stage"""
|
||||||
from base64 import b32encode
|
|
||||||
from binascii import unhexlify
|
|
||||||
from typing import Any, Dict
|
from typing import Any, Dict
|
||||||
|
|
||||||
import lxml.etree as ET # nosec
|
import lxml.etree as ET # nosec
|
||||||
from django.http import HttpRequest, HttpResponse
|
from django.http import HttpRequest, HttpResponse
|
||||||
from django.utils.encoding import force_text
|
from django.utils.encoding import force_text
|
||||||
from django.utils.http import urlencode
|
|
||||||
from django.utils.translation import gettext as _
|
|
||||||
from django.views.generic import FormView
|
from django.views.generic import FormView
|
||||||
from django_otp.plugins.otp_totp.models import TOTPDevice
|
from django_otp.plugins.otp_totp.models import TOTPDevice
|
||||||
from qrcode import QRCode
|
from qrcode import QRCode
|
||||||
from qrcode.image.svg import SvgFillImage
|
from qrcode.image.svg import SvgFillImage
|
||||||
from structlog import get_logger
|
from structlog import get_logger
|
||||||
|
|
||||||
from passbook.flows.models import NotConfiguredAction, Stage
|
|
||||||
from passbook.flows.planner import PLAN_CONTEXT_PENDING_USER
|
from passbook.flows.planner import PLAN_CONTEXT_PENDING_USER
|
||||||
from passbook.flows.stage import StageView
|
from passbook.flows.stage import StageView
|
||||||
from passbook.stages.otp_time.forms import SetupForm
|
from passbook.stages.otp_time.forms import SetupForm
|
||||||
|
@ -24,26 +19,8 @@ LOGGER = get_logger()
|
||||||
SESSION_TOTP_DEVICE = "totp_device"
|
SESSION_TOTP_DEVICE = "totp_device"
|
||||||
|
|
||||||
|
|
||||||
def otp_auth_url(device: TOTPDevice) -> str:
|
|
||||||
"""Create otpauth according to
|
|
||||||
https://github.com/google/google-authenticator/wiki/Key-Uri-Format"""
|
|
||||||
# Ensure that the secret parameter is the FIRST parameter of the URI, this
|
|
||||||
# allows Microsoft Authenticator to work.
|
|
||||||
issuer = "passbook"
|
|
||||||
|
|
||||||
rawkey = unhexlify(device.key.encode("ascii"))
|
|
||||||
secret = b32encode(rawkey).decode("utf-8")
|
|
||||||
|
|
||||||
query = [
|
|
||||||
("secret", secret),
|
|
||||||
("digits", device.digits),
|
|
||||||
("issuer", issuer),
|
|
||||||
]
|
|
||||||
|
|
||||||
return "otpauth://totp/%s:%s?%s" % (issuer, device.user.username, urlencode(query))
|
|
||||||
|
|
||||||
|
|
||||||
class OTPTimeStageView(FormView, StageView):
|
class OTPTimeStageView(FormView, StageView):
|
||||||
|
"""OTP totp Setup stage"""
|
||||||
|
|
||||||
form_class = SetupForm
|
form_class = SetupForm
|
||||||
|
|
||||||
|
@ -56,9 +33,8 @@ class OTPTimeStageView(FormView, StageView):
|
||||||
|
|
||||||
def _get_qr_code(self, device: TOTPDevice) -> str:
|
def _get_qr_code(self, device: TOTPDevice) -> str:
|
||||||
"""Get QR Code SVG as string based on `device`"""
|
"""Get QR Code SVG as string based on `device`"""
|
||||||
url = otp_auth_url(device)
|
|
||||||
qr_code = QRCode(image_factory=SvgFillImage)
|
qr_code = QRCode(image_factory=SvgFillImage)
|
||||||
qr_code.add_data(url)
|
qr_code.add_data(device.config_url)
|
||||||
return force_text(ET.tostring(qr_code.make_image().get_image()))
|
return force_text(ET.tostring(qr_code.make_image().get_image()))
|
||||||
|
|
||||||
def get(self, request: HttpRequest, *args, **kwargs) -> HttpResponse:
|
def get(self, request: HttpRequest, *args, **kwargs) -> HttpResponse:
|
||||||
|
|
Reference in New Issue