From a76eb4d30f45067f36b6f7947a5b5a6f29e3b95f Mon Sep 17 00:00:00 2001 From: Jens Langhammer Date: Tue, 30 Jun 2020 12:42:39 +0200 Subject: [PATCH] stages/otp_time: Cleanup, use django_otp's URL generator --- passbook/stages/otp_time/forms.py | 11 +++++++++-- passbook/stages/otp_time/settings.py | 1 + passbook/stages/otp_time/stage.py | 28 ++-------------------------- 3 files changed, 12 insertions(+), 28 deletions(-) diff --git a/passbook/stages/otp_time/forms.py b/passbook/stages/otp_time/forms.py index af991fa06..494053fa0 100644 --- a/passbook/stages/otp_time/forms.py +++ b/passbook/stages/otp_time/forms.py @@ -30,17 +30,24 @@ class SetupForm(forms.Form): code = forms.CharField( label=_("Code"), 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): super().__init__(*args, **kwargs) + self.device = device self.fields["qr_code"].initial = qr_code def clean_code(self): """Check code with new otp device""" 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")) return self.cleaned_data.get("code") diff --git a/passbook/stages/otp_time/settings.py b/passbook/stages/otp_time/settings.py index 74f973300..5392069f7 100644 --- a/passbook/stages/otp_time/settings.py +++ b/passbook/stages/otp_time/settings.py @@ -3,3 +3,4 @@ INSTALLED_APPS = [ "django_otp.plugins.otp_totp", ] +OTP_TOTP_ISSUER = "passbook" diff --git a/passbook/stages/otp_time/stage.py b/passbook/stages/otp_time/stage.py index 1f756dcff..1dae7ffd0 100644 --- a/passbook/stages/otp_time/stage.py +++ b/passbook/stages/otp_time/stage.py @@ -1,20 +1,15 @@ """TOTP Setup stage""" -from base64 import b32encode -from binascii import unhexlify from typing import Any, Dict import lxml.etree as ET # nosec from django.http import HttpRequest, HttpResponse 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_otp.plugins.otp_totp.models import TOTPDevice from qrcode import QRCode from qrcode.image.svg import SvgFillImage from structlog import get_logger -from passbook.flows.models import NotConfiguredAction, Stage from passbook.flows.planner import PLAN_CONTEXT_PENDING_USER from passbook.flows.stage import StageView from passbook.stages.otp_time.forms import SetupForm @@ -24,26 +19,8 @@ LOGGER = get_logger() 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): + """OTP totp Setup stage""" form_class = SetupForm @@ -56,9 +33,8 @@ class OTPTimeStageView(FormView, StageView): def _get_qr_code(self, device: TOTPDevice) -> str: """Get QR Code SVG as string based on `device`""" - url = otp_auth_url(device) 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())) def get(self, request: HttpRequest, *args, **kwargs) -> HttpResponse: