stages/otp_validate: Implement OTP Validation stage
This commit is contained in:
parent
9613fcde89
commit
7c191b0984
|
@ -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"
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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 _
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
"""OTP Validate stage settings"""
|
||||||
INSTALLED_APPS = [
|
INSTALLED_APPS = [
|
||||||
"django_otp",
|
"django_otp",
|
||||||
]
|
]
|
||||||
|
|
|
@ -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()
|
||||||
|
|
Reference in New Issue