stages/otp_validate: Implement OTP Validation stage

This commit is contained in:
Jens Langhammer 2020-06-30 12:42:12 +02:00
parent 9613fcde89
commit 7c191b0984
6 changed files with 37 additions and 13 deletions

View File

@ -1,7 +1,9 @@
"""OTP Static stage"""
from django.apps import AppConfig from django.apps import AppConfig
class PassbookStageOTPStaticConfig(AppConfig): class PassbookStageOTPStaticConfig(AppConfig):
"""OTP Static stage"""
name = "passbook.stages.otp_static" name = "passbook.stages.otp_static"
label = "passbook_stages_otp_static" label = "passbook_stages_otp_static"

View File

@ -1,7 +1,9 @@
"""OTP Validation Stage"""
from django.apps import AppConfig from django.apps import AppConfig
class PassbookStageOTPValidateConfig(AppConfig): class PassbookStageOTPValidateConfig(AppConfig):
"""OTP Validation Stage"""
name = "passbook.stages.otp_validate" name = "passbook.stages.otp_validate"
label = "passbook_stages_otp_validate" label = "passbook_stages_otp_validate"

View File

@ -1,7 +1,10 @@
"""OTP Validate stage forms"""
from django import forms from django import forms
from django.core.validators import RegexValidator from django.core.validators import RegexValidator
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from django_otp import match_token
from passbook.core.models import User
from passbook.stages.otp_validate.models import OTPValidateStage from passbook.stages.otp_validate.models import OTPValidateStage
OTP_CODE_VALIDATOR = RegexValidator( OTP_CODE_VALIDATOR = RegexValidator(
@ -10,6 +13,9 @@ OTP_CODE_VALIDATOR = RegexValidator(
class ValidationForm(forms.Form): class ValidationForm(forms.Form):
"""OTP Validate stage forms"""
user: User
code = forms.CharField( code = forms.CharField(
label=_("Code"), label=_("Code"),
@ -23,11 +29,22 @@ class ValidationForm(forms.Form):
), ),
) )
def __init__(self, user, *args, **kwargs):
super().__init__(*args, **kwargs)
self.user = user
def clean_code(self): def clean_code(self):
pass """Validate code against all confirmed devices"""
code = self.cleaned_data.get("code")
device = match_token(self.user, code)
if not device:
raise forms.ValidationError(_("Invalid Token"))
return code
class OTPValidateStageForm(forms.ModelForm): class OTPValidateStageForm(forms.ModelForm):
"""OTP Validate stage forms"""
class Meta: class Meta:
model = OTPValidateStage model = OTPValidateStage

View File

@ -1,3 +1,4 @@
"""OTP Validation Stage"""
from django.db import models from django.db import models
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _

View File

@ -1,3 +1,4 @@
"""OTP Validate stage settings"""
INSTALLED_APPS = [ INSTALLED_APPS = [
"django_otp", "django_otp",
] ]

View File

@ -1,12 +1,12 @@
from django.contrib import messages """OTP Validation"""
from typing import Any, Dict
from django.http import HttpRequest, HttpResponse from django.http import HttpRequest, HttpResponse
from django.utils.translation import gettext as _
from django.views.generic import FormView from django.views.generic import FormView
from django_otp import match_token, user_has_device from django_otp import user_has_device
from django_otp.models import Device
from structlog import get_logger from structlog import get_logger
from passbook.flows.models import NotConfiguredAction, Stage from passbook.flows.models import NotConfiguredAction
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_validate.forms import ValidationForm from passbook.stages.otp_validate.forms import ValidationForm
@ -16,9 +16,15 @@ LOGGER = get_logger()
class OTPValidateStageView(FormView, StageView): class OTPValidateStageView(FormView, StageView):
"""OTP Validation"""
form_class = ValidationForm form_class = ValidationForm
def get_form_kwargs(self, **kwargs) -> Dict[str, Any]:
kwargs = super().get_form_kwargs(**kwargs)
kwargs["user"] = self.executor.plan.context.get(PLAN_CONTEXT_PENDING_USER)
return kwargs
def get(self, request: HttpRequest, *args, **kwargs) -> HttpResponse: def get(self, request: HttpRequest, *args, **kwargs) -> HttpResponse:
user = self.executor.plan.context.get(PLAN_CONTEXT_PENDING_USER) user = self.executor.plan.context.get(PLAN_CONTEXT_PENDING_USER)
if not user: if not user:
@ -35,11 +41,6 @@ class OTPValidateStageView(FormView, StageView):
def form_valid(self, form: ValidationForm) -> HttpResponse: def form_valid(self, form: ValidationForm) -> HttpResponse:
"""Verify OTP Token""" """Verify OTP Token"""
device = match_token( # Since we do token checking in the form, we know the token is valid here
self.executor.plan.context[PLAN_CONTEXT_PENDING_USER], # so we can just continue
form.cleaned_data.get("code"),
)
if not device:
messages.error(self.request, _("Invalid OTP."))
return self.form_invalid(form)
return self.executor.stage_ok() return self.executor.stage_ok()