Squashed commit of the following:

commit 88029a4335
Author: Jens Langhammer <jens.langhammer@beryju.org>
Date:   Mon Jul 20 16:55:55 2020 +0200

    admin: update to work with new form

commit 4040eb9619
Author: Jens Langhammer <jens.langhammer@beryju.org>
Date:   Mon Jul 20 16:43:30 2020 +0200

    *: remove path-based import from all PropertyMappings

commit c9663a08da
Author: Jens Langhammer <jens.langhammer@beryju.org>
Date:   Mon Jul 20 16:33:34 2020 +0200

    flows: update work with new stages

commit a3d92ebc0a
Author: Jens Langhammer <jens.langhammer@beryju.org>
Date:   Mon Jul 20 16:23:30 2020 +0200

    stages/*: remove path-based import from all stages

commit 6fa825e372
Author: Jens Langhammer <jens.langhammer@beryju.org>
Date:   Mon Jul 20 16:03:55 2020 +0200

    providers/*: remove path-based import from all providers

commit 6aefd072c8
Author: Jens Langhammer <jens.langhammer@beryju.org>
Date:   Mon Jul 20 15:58:48 2020 +0200

    policies/*: remove path-based import from all policies

commit ac2dd3611f
Author: Jens Langhammer <jens.langhammer@beryju.org>
Date:   Mon Jul 20 15:11:27 2020 +0200

    sources/*: remove path-based import from all sources

commit 74e628ce9c
Author: Jens Langhammer <jens.langhammer@beryju.org>
Date:   Mon Jul 20 14:43:38 2020 +0200

    ui: allow overriding of verbose_name

commit d4ee18ee32
Author: Jens Langhammer <jens.langhammer@beryju.org>
Date:   Mon Jul 20 14:08:27 2020 +0200

    sources/oauth: migrate from discordapp.com to discord.com
This commit is contained in:
Jens Langhammer 2020-07-20 18:17:14 +02:00
parent 9ff3ee7c0c
commit 37a432267d
42 changed files with 379 additions and 98 deletions

View File

@ -103,9 +103,9 @@ class TestProviderOAuth(SeleniumTestCase):
USER().username,
)
self.assertEqual(
self.driver.find_element(
By.CSS_SELECTOR, "input[name=name]"
).get_attribute("value"),
self.driver.find_element(By.CSS_SELECTOR, "input[name=name]").get_attribute(
"value"
),
USER().username,
)
self.assertEqual(
@ -172,9 +172,9 @@ class TestProviderOAuth(SeleniumTestCase):
USER().username,
)
self.assertEqual(
self.driver.find_element(
By.CSS_SELECTOR, "input[name=name]"
).get_attribute("value"),
self.driver.find_element(By.CSS_SELECTOR, "input[name=name]").get_attribute(
"value"
),
USER().username,
)
self.assertEqual(

View File

@ -153,9 +153,9 @@ class TestProviderOIDC(SeleniumTestCase):
USER().name,
)
self.assertEqual(
self.driver.find_element(
By.CSS_SELECTOR, "input[name=name]"
).get_attribute("value"),
self.driver.find_element(By.CSS_SELECTOR, "input[name=name]").get_attribute(
"value"
),
USER().name,
)
self.assertEqual(
@ -232,9 +232,9 @@ class TestProviderOIDC(SeleniumTestCase):
USER().name,
)
self.assertEqual(
self.driver.find_element(
By.CSS_SELECTOR, "input[name=name]"
).get_attribute("value"),
self.driver.find_element(By.CSS_SELECTOR, "input[name=name]").get_attribute(
"value"
),
USER().name,
)
self.assertEqual(

View File

@ -6,7 +6,7 @@ from django.contrib.messages.views import SuccessMessageMixin
from django.http import Http404
from django.views.generic import DeleteView, ListView, UpdateView
from passbook.lib.utils.reflection import all_subclasses, path_to_class
from passbook.lib.utils.reflection import all_subclasses
from passbook.lib.views import CreateAssignPermView
@ -40,7 +40,7 @@ class InheritanceCreateView(CreateAssignPermView):
)
except StopIteration as exc:
raise Http404 from exc
return path_to_class(model.form)
return model.form(model)
def get_context_data(self, **kwargs: Any) -> Dict[str, Any]:
kwargs = super().get_context_data(**kwargs)
@ -61,9 +61,7 @@ class InheritanceUpdateView(UpdateView):
return kwargs
def get_form_class(self):
form_class_path = self.get_object().form
form_class = path_to_class(form_class_path)
return form_class
return self.get_object().form()
def get_object(self, queryset=None):
return (

View File

@ -1,12 +1,13 @@
"""passbook core models"""
from datetime import timedelta
from typing import Any, Optional
from typing import Any, Optional, Type
from uuid import uuid4
from django.contrib.auth.models import AbstractUser
from django.contrib.postgres.fields import JSONField
from django.db import models
from django.db.models import Q, QuerySet
from django.forms import ModelForm
from django.http import HttpRequest
from django.utils.timezone import now
from django.utils.translation import gettext_lazy as _
@ -92,6 +93,10 @@ class Provider(models.Model):
objects = InheritanceManager()
def form(self) -> Type[ModelForm]:
"""Return Form class used to edit this object"""
raise NotImplementedError
# This class defines no field for easier inheritance
def __str__(self):
if hasattr(self, "name"):
@ -162,10 +167,12 @@ class Source(PolicyBindingModel):
related_name="source_enrollment",
)
form = "" # ModelForm-based class ued to create/edit instance
objects = InheritanceManager()
def form(self) -> Type[ModelForm]:
"""Return Form class used to edit this object"""
raise NotImplementedError
@property
def ui_login_button(self) -> Optional[UILoginButton]:
"""If source uses a http-based flow, return UI Information about the login
@ -261,9 +268,12 @@ class PropertyMapping(models.Model):
name = models.TextField()
expression = models.TextField()
form = ""
objects = InheritanceManager()
def form(self) -> Type[ModelForm]:
"""Return Form class used to edit this object"""
raise NotImplementedError
def evaluate(
self, user: Optional[User], request: Optional[HttpRequest], **kwargs
) -> Any:

View File

@ -3,13 +3,13 @@ from typing import TYPE_CHECKING, Optional, Type
from uuid import uuid4
from django.db import models
from django.forms import ModelForm
from django.http import HttpRequest
from django.utils.translation import gettext_lazy as _
from model_utils.managers import InheritanceManager
from structlog import get_logger
from passbook.core.types import UIUserSettings
from passbook.lib.utils.reflection import class_to_path
from passbook.policies.models import PolicyBindingModel
if TYPE_CHECKING:
@ -47,8 +47,17 @@ class Stage(models.Model):
name = models.TextField()
objects = InheritanceManager()
type = ""
form = ""
def type(self) -> Type["StageView"]:
"""Return StageView class that implements logic for this stage"""
# This is a bit of a workaround, since we can't set class methods with setattr
if hasattr(self, "__in_memory_type"):
return getattr(self, "__in_memory_type")
raise NotImplementedError
def form(self) -> Type[ModelForm]:
"""Return Form class used to edit this object"""
raise NotImplementedError
@property
def ui_user_settings(self) -> Optional[UIUserSettings]:
@ -62,9 +71,11 @@ class Stage(models.Model):
def in_memory_stage(view: Type["StageView"]) -> Stage:
"""Creates an in-memory stage instance, based on a `_type` as view."""
class_path = class_to_path(view)
stage = Stage()
stage.type = class_path
# Because we can't pickle a locally generated function,
# we set the view as a separate property and reference a generic function
# that returns that member
setattr(stage, "__in_memory_type", view)
return stage

View File

@ -21,7 +21,7 @@ from passbook.core.views.utils import PermissionDeniedView
from passbook.flows.exceptions import EmptyFlowException, FlowNonApplicableException
from passbook.flows.models import Flow, FlowDesignation, Stage
from passbook.flows.planner import FlowPlan, FlowPlanner
from passbook.lib.utils.reflection import class_to_path, path_to_class
from passbook.lib.utils.reflection import class_to_path
from passbook.lib.utils.urls import is_url_absolute, redirect_with_qs
from passbook.lib.views import bad_request_message
@ -94,7 +94,7 @@ class FlowExecutorView(View):
if not self.current_stage:
LOGGER.debug("f(exec): no more stages, flow is done.")
return self._flow_done()
stage_cls = path_to_class(self.current_stage.type)
stage_cls = self.current_stage.type()
self.current_stage_view = stage_cls(self)
self.current_stage_view.args = self.args
self.current_stage_view.kwargs = self.kwargs

View File

@ -36,7 +36,7 @@ def back(context: Context) -> str:
def fieldtype(field):
"""Return classname"""
if isinstance(field.__class__, Model) or issubclass(field.__class__, Model):
return field._meta.verbose_name
return verbose_name(field)
return field.__class__.__name__
@ -84,6 +84,9 @@ def verbose_name(obj) -> str:
"""Return Object's Verbose Name"""
if not obj:
return ""
if hasattr(obj, "verbose_name"):
print(obj.verbose_name)
return obj.verbose_name
return obj._meta.verbose_name
@ -92,7 +95,7 @@ def form_verbose_name(obj) -> str:
"""Return ModelForm's Object's Verbose Name"""
if not obj:
return ""
return obj._meta.model._meta.verbose_name
return verbose_name(obj._meta.model)
@register.filter

View File

@ -1,8 +1,10 @@
"""Dummy policy"""
from random import SystemRandom
from time import sleep
from typing import Type
from django.db import models
from django.forms import ModelForm
from django.utils.translation import gettext_lazy as _
from structlog import get_logger
@ -22,7 +24,10 @@ class DummyPolicy(Policy):
wait_min = models.IntegerField(default=5)
wait_max = models.IntegerField(default=30)
form = "passbook.policies.dummy.forms.DummyPolicyForm"
def form(self) -> Type[ModelForm]:
from passbook.policies.dummy.forms import DummyPolicyForm
return DummyPolicyForm
def passes(self, request: PolicyRequest) -> PolicyResult:
"""Wait random time then return result"""

View File

@ -1,7 +1,9 @@
"""passbook password_expiry_policy Models"""
from datetime import timedelta
from typing import Type
from django.db import models
from django.forms import ModelForm
from django.utils.timezone import now
from django.utils.translation import gettext as _
from structlog import get_logger
@ -19,7 +21,10 @@ class PasswordExpiryPolicy(Policy):
deny_only = models.BooleanField(default=False)
days = models.IntegerField()
form = "passbook.policies.expiry.forms.PasswordExpiryPolicyForm"
def form(self) -> Type[ModelForm]:
from passbook.policies.expiry.forms import PasswordExpiryPolicyForm
return PasswordExpiryPolicyForm
def passes(self, request: PolicyRequest) -> PolicyResult:
"""If password change date is more than x days in the past, call set_unusable_password

View File

@ -1,5 +1,8 @@
"""passbook expression Policy Models"""
from typing import Type
from django.db import models
from django.forms import ModelForm
from django.utils.translation import gettext as _
from passbook.policies.expression.evaluator import PolicyEvaluator
@ -12,7 +15,10 @@ class ExpressionPolicy(Policy):
expression = models.TextField()
form = "passbook.policies.expression.forms.ExpressionPolicyForm"
def form(self) -> Type[ModelForm]:
from passbook.policies.expression.forms import ExpressionPolicyForm
return ExpressionPolicyForm
def passes(self, request: PolicyRequest) -> PolicyResult:
"""Evaluate and render expression. Returns PolicyResult(false) on error."""

View File

@ -1,5 +1,8 @@
"""user field matcher models"""
from typing import Type
from django.db import models
from django.forms import ModelForm
from django.utils.translation import gettext as _
from passbook.core.models import Group
@ -12,7 +15,10 @@ class GroupMembershipPolicy(Policy):
group = models.ForeignKey(Group, null=True, blank=True, on_delete=models.SET_NULL)
form = "passbook.policies.group_membership.forms.GroupMembershipPolicyForm"
def form(self) -> Type[ModelForm]:
from passbook.policies.group_membership.forms import GroupMembershipPolicyForm
return GroupMembershipPolicyForm
def passes(self, request: PolicyRequest) -> PolicyResult:
return PolicyResult(self.group.user_set.filter(pk=request.user.pk).exists())

View File

@ -1,7 +1,9 @@
"""passbook HIBP Models"""
from hashlib import sha1
from typing import Type
from django.db import models
from django.forms import ModelForm
from django.utils.translation import gettext as _
from requests import get
from structlog import get_logger
@ -25,7 +27,10 @@ class HaveIBeenPwendPolicy(Policy):
allowed_count = models.IntegerField(default=0)
form = "passbook.policies.hibp.forms.HaveIBeenPwnedPolicyForm"
def form(self) -> Type[ModelForm]:
from passbook.policies.hibp.forms import HaveIBeenPwnedPolicyForm
return HaveIBeenPwnedPolicyForm
def passes(self, request: PolicyRequest) -> PolicyResult:
"""Check if password is in HIBP DB. Hashes given Password with SHA1, uses the first 5

View File

@ -1,7 +1,9 @@
"""Policy base models"""
from typing import Type
from uuid import uuid4
from django.db import models
from django.forms import ModelForm
from django.utils.translation import gettext_lazy as _
from model_utils.managers import InheritanceManager
@ -73,6 +75,10 @@ class Policy(CreatedUpdatedModel):
objects = InheritanceAutoManager()
def form(self) -> Type[ModelForm]:
"""Return Form class used to edit this object"""
raise NotImplementedError
def __str__(self):
return f"Policy {self.name}"

View File

@ -1,7 +1,9 @@
"""user field matcher models"""
import re
from typing import Type
from django.db import models
from django.forms import ModelForm
from django.utils.translation import gettext as _
from structlog import get_logger
@ -28,7 +30,10 @@ class PasswordPolicy(Policy):
symbol_charset = models.TextField(default=r"!\"#$%&'()*+,-./:;<=>?@[\]^_`{|}~ ")
error_message = models.TextField()
form = "passbook.policies.password.forms.PasswordPolicyForm"
def form(self) -> Type[ModelForm]:
from passbook.policies.password.forms import PasswordPolicyForm
return PasswordPolicyForm
def passes(self, request: PolicyRequest) -> PolicyResult:
if self.password_field not in request.context:

View File

@ -1,6 +1,9 @@
"""passbook reputation request policy"""
from typing import Type
from django.core.cache import cache
from django.db import models
from django.forms import ModelForm
from django.utils.translation import gettext as _
from passbook.core.models import User
@ -19,7 +22,10 @@ class ReputationPolicy(Policy):
check_username = models.BooleanField(default=True)
threshold = models.IntegerField(default=-5)
form = "passbook.policies.reputation.forms.ReputationPolicyForm"
def form(self) -> Type[ModelForm]:
from passbook.policies.reputation.forms import ReputationPolicyForm
return ReputationPolicyForm
def passes(self, request: PolicyRequest) -> PolicyResult:
remote_ip = get_client_ip(request.http_request) or "255.255.255.255"

View File

@ -1,9 +1,10 @@
"""passbook app_gw models"""
import string
from random import SystemRandom
from typing import Optional
from typing import Optional, Type
from django.db import models
from django.forms import ModelForm
from django.http import HttpRequest
from django.utils.translation import gettext as _
from oidc_provider.models import Client
@ -23,7 +24,10 @@ class ApplicationGatewayProvider(Provider):
client = models.ForeignKey(Client, on_delete=models.CASCADE)
form = "passbook.providers.app_gw.forms.ApplicationGatewayProviderForm"
def form(self) -> Type[ModelForm]:
from passbook.providers.app_gw.forms import ApplicationGatewayProviderForm
return ApplicationGatewayProviderForm
def html_setup_urls(self, request: HttpRequest) -> Optional[str]:
"""return template and context modal with URLs for authorize, token, openid-config, etc"""

View File

@ -1,7 +1,8 @@
"""Oauth2 provider product extension"""
from typing import Optional
from typing import Optional, Type
from django.forms import ModelForm
from django.http import HttpRequest
from django.shortcuts import reverse
from django.utils.translation import gettext as _
@ -16,7 +17,10 @@ class OAuth2Provider(Provider, AbstractApplication):
This Provider also supports the GitHub-pretend mode for Applications that don't support
generic OAuth."""
form = "passbook.providers.oauth.forms.OAuth2ProviderForm"
def form(self) -> Type[ModelForm]:
from passbook.providers.oauth.forms import OAuth2ProviderForm
return OAuth2ProviderForm
def __str__(self):
return self.name

View File

@ -1,7 +1,8 @@
"""oidc models"""
from typing import Optional
from typing import Optional, Type
from django.db import models
from django.forms import ModelForm
from django.http import HttpRequest
from django.shortcuts import reverse
from django.utils.translation import gettext as _
@ -20,7 +21,10 @@ class OpenIDProvider(Provider):
oidc_client = models.OneToOneField(Client, on_delete=models.CASCADE)
form = "passbook.providers.oidc.forms.OIDCProviderForm"
def form(self) -> Type[ModelForm]:
from passbook.providers.oidc.forms import OIDCProviderForm
return OIDCProviderForm
@property
def name(self):

View File

@ -1,7 +1,8 @@
"""passbook saml_idp Models"""
from typing import Optional
from typing import Optional, Type
from django.db import models
from django.forms import ModelForm
from django.http import HttpRequest
from django.shortcuts import reverse
from django.utils.translation import ugettext_lazy as _
@ -101,7 +102,10 @@ class SAMLProvider(Provider):
),
)
form = "passbook.providers.saml.forms.SAMLProviderForm"
def form(self) -> Type[ModelForm]:
from passbook.providers.saml.forms import SAMLProviderForm
return SAMLProviderForm
def __str__(self):
return self.name
@ -143,7 +147,10 @@ class SAMLPropertyMapping(PropertyMapping):
saml_name = models.TextField(verbose_name="SAML Name")
friendly_name = models.TextField(default=None, blank=True, null=True)
form = "passbook.providers.saml.forms.SAMLPropertyMappingForm"
def form(self) -> Type[ModelForm]:
from passbook.providers.saml.forms import SAMLPropertyMappingForm
return SAMLPropertyMappingForm
def __str__(self):
return f"SAML Property Mapping {self.saml_name}"

View File

@ -325,6 +325,8 @@ LOG_PRE_CHAIN = [
structlog.stdlib.add_log_level,
structlog.stdlib.add_logger_name,
structlog.processors.TimeStamper(),
structlog.processors.StackInfoRenderer(),
structlog.processors.format_exc_info,
]
LOGGING = {

View File

@ -1,8 +1,9 @@
"""passbook LDAP Models"""
from typing import Optional
from typing import Optional, Type
from django.core.validators import URLValidator
from django.db import models
from django.forms import ModelForm
from django.utils.translation import gettext_lazy as _
from ldap3 import Connection, Server
@ -53,7 +54,10 @@ class LDAPSource(Source):
Group, blank=True, null=True, default=None, on_delete=models.SET_DEFAULT
)
form = "passbook.sources.ldap.forms.LDAPSourceForm"
def form(self) -> Type[ModelForm]:
from passbook.sources.ldap.forms import LDAPSourceForm
return LDAPSourceForm
_connection: Optional[Connection] = None
@ -85,7 +89,10 @@ class LDAPPropertyMapping(PropertyMapping):
object_field = models.TextField()
form = "passbook.sources.ldap.forms.LDAPPropertyMappingForm"
def form(self) -> Type[ModelForm]:
from passbook.sources.ldap.forms import LDAPPropertyMappingForm
return LDAPPropertyMappingForm
def __str__(self):
return f"LDAP Property Mapping {self.expression} -> {self.object_field}"

View File

@ -98,9 +98,9 @@ class DiscordOAuthSourceForm(OAuthSourceForm):
overrides = {
"provider_type": "discord",
"request_token_url": "",
"authorization_url": "https://discordapp.com/api/oauth2/authorize",
"access_token_url": "https://discordapp.com/api/oauth2/token",
"profile_url": "https://discordapp.com/api/users/@me",
"authorization_url": "https://discord.com/api/oauth2/authorize",
"access_token_url": "https://discord.com/api/oauth2/token",
"profile_url": "https://discord.com/api/users/@me",
}

View File

@ -1,7 +1,8 @@
"""OAuth Client models"""
from typing import Optional
from typing import Optional, Type
from django.db import models
from django.forms import ModelForm
from django.urls import reverse, reverse_lazy
from django.utils.translation import gettext_lazy as _
@ -40,7 +41,10 @@ class OAuthSource(Source):
consumer_key = models.TextField()
consumer_secret = models.TextField()
form = "passbook.sources.oauth.forms.OAuthSourceForm"
def form(self) -> Type[ModelForm]:
from passbook.sources.oauth.forms import OAuthSourceForm
return OAuthSourceForm
@property
def ui_login_button(self) -> UILoginButton:
@ -80,7 +84,10 @@ class OAuthSource(Source):
class GitHubOAuthSource(OAuthSource):
"""Social Login using GitHub.com or a GitHub-Enterprise Instance."""
form = "passbook.sources.oauth.forms.GitHubOAuthSourceForm"
def form(self) -> Type[ModelForm]:
from passbook.sources.oauth.forms import GitHubOAuthSourceForm
return GitHubOAuthSourceForm
class Meta:
@ -92,7 +99,9 @@ class GitHubOAuthSource(OAuthSource):
# class TwitterOAuthSource(OAuthSource):
# """Social Login using Twitter.com"""
# form = "passbook.sources.oauth.forms.TwitterOAuthSourceForm"
# def form(self) -> Type[ModelForm]:
# from passbook.sources.oauth.forms import TwitterOAuthSourceForm
# return TwitterOAuthSourceForm
# class Meta:
@ -104,7 +113,10 @@ class GitHubOAuthSource(OAuthSource):
class FacebookOAuthSource(OAuthSource):
"""Social Login using Facebook.com."""
form = "passbook.sources.oauth.forms.FacebookOAuthSourceForm"
def form(self) -> Type[ModelForm]:
from passbook.sources.oauth.forms import FacebookOAuthSourceForm
return FacebookOAuthSourceForm
class Meta:
@ -116,7 +128,10 @@ class FacebookOAuthSource(OAuthSource):
class DiscordOAuthSource(OAuthSource):
"""Social Login using Discord."""
form = "passbook.sources.oauth.forms.DiscordOAuthSourceForm"
def form(self) -> Type[ModelForm]:
from passbook.sources.oauth.forms import DiscordOAuthSourceForm
return DiscordOAuthSourceForm
class Meta:
@ -128,7 +143,10 @@ class DiscordOAuthSource(OAuthSource):
class GoogleOAuthSource(OAuthSource):
"""Social Login using Google or Gsuite."""
form = "passbook.sources.oauth.forms.GoogleOAuthSourceForm"
def form(self) -> Type[ModelForm]:
from passbook.sources.oauth.forms import GoogleOAuthSourceForm
return GoogleOAuthSourceForm
class Meta:
@ -140,7 +158,10 @@ class GoogleOAuthSource(OAuthSource):
class AzureADOAuthSource(OAuthSource):
"""Social Login using Azure AD."""
form = "passbook.sources.oauth.forms.AzureADOAuthSourceForm"
def form(self) -> Type[ModelForm]:
from passbook.sources.oauth.forms import AzureADOAuthSourceForm
return AzureADOAuthSourceForm
class Meta:
@ -152,7 +173,10 @@ class AzureADOAuthSource(OAuthSource):
class OpenIDOAuthSource(OAuthSource):
"""Login using a Generic OpenID-Connect compliant provider."""
form = "passbook.sources.oauth.forms.OAuthSourceForm"
def form(self) -> Type[ModelForm]:
from passbook.sources.oauth.forms import OAuthSourceForm
return OAuthSourceForm
class Meta:

View File

@ -1,5 +1,8 @@
"""saml sp models"""
from typing import Type
from django.db import models
from django.forms import ModelForm
from django.http import HttpRequest
from django.shortcuts import reverse
from django.urls import reverse_lazy
@ -93,7 +96,10 @@ class SAMLSource(Source):
on_delete=models.PROTECT,
)
form = "passbook.sources.saml.forms.SAMLSourceForm"
def form(self) -> Type[ModelForm]:
from passbook.sources.saml.forms import SAMLSourceForm
return SAMLSourceForm
def get_issuer(self, request: HttpRequest) -> str:
"""Get Source's Issuer, falling back to our Metadata URL if none is set"""

View File

@ -1,6 +1,10 @@
"""passbook captcha stage"""
from typing import Type
from django.db import models
from django.forms import ModelForm
from django.utils.translation import gettext_lazy as _
from django.views import View
from passbook.flows.models import Stage
@ -19,8 +23,15 @@ class CaptchaStage(Stage):
)
)
type = "passbook.stages.captcha.stage.CaptchaStage"
form = "passbook.stages.captcha.forms.CaptchaStageForm"
def type(self) -> Type[View]:
from passbook.stages.captcha.stage import CaptchaStageView
return CaptchaStageView
def form(self) -> Type[ModelForm]:
from passbook.stages.captcha.forms import CaptchaStageForm
return CaptchaStageForm
def __str__(self):
return f"Captcha Stage {self.name}"

View File

@ -6,7 +6,7 @@ from passbook.flows.stage import StageView
from passbook.stages.captcha.forms import CaptchaForm
class CaptchaStage(FormView, StageView):
class CaptchaStageView(FormView, StageView):
"""Simple captcha checker, logic is handeled in django-captcha module"""
form_class = CaptchaForm

View File

@ -1,6 +1,10 @@
"""passbook consent stage"""
from django.db import models
from typing import Type
from django.forms import ModelForm
from django.utils.translation import gettext_lazy as _
from django.views import View
from passbook.core.models import Application, ExpiringModel, User
from passbook.flows.models import Stage
@ -33,8 +37,15 @@ class ConsentStage(Stage):
),
)
type = "passbook.stages.consent.stage.ConsentStageView"
form = "passbook.stages.consent.forms.ConsentStageForm"
def type(self) -> Type[View]:
from passbook.stages.consent.stage import ConsentStageView
return ConsentStageView
def form(self) -> Type[ModelForm]:
from passbook.stages.consent.forms import ConsentStageForm
return ConsentStageForm
def __str__(self):
return f"Consent Stage {self.name}"

View File

@ -1,5 +1,9 @@
"""dummy stage models"""
from typing import Type
from django.forms import ModelForm
from django.utils.translation import gettext as _
from django.views import View
from passbook.flows.models import Stage
@ -9,8 +13,15 @@ class DummyStage(Stage):
__debug_only__ = True
type = "passbook.stages.dummy.stage.DummyStage"
form = "passbook.stages.dummy.forms.DummyStageForm"
def type(self) -> Type[View]:
from passbook.stages.dummy.stage import DummyStageView
return DummyStageView
def form(self) -> Type[ModelForm]:
from passbook.stages.dummy.forms import DummyStageForm
return DummyStageForm
def __str__(self):
return f"Dummy Stage {self.name}"

View File

@ -6,7 +6,7 @@ from django.http import HttpRequest
from passbook.flows.stage import StageView
class DummyStage(StageView):
class DummyStageView(StageView):
"""Dummy stage for testing with multiple stages"""
def post(self, request: HttpRequest):

View File

@ -1,8 +1,12 @@
"""email stage models"""
from typing import Type
from django.core.mail import get_connection
from django.core.mail.backends.base import BaseEmailBackend
from django.db import models
from django.forms import ModelForm
from django.utils.translation import gettext as _
from django.views import View
from passbook.flows.models import Stage
@ -40,8 +44,15 @@ class EmailStage(Stage):
choices=EmailTemplates.choices, default=EmailTemplates.PASSWORD_RESET
)
type = "passbook.stages.email.stage.EmailStageView"
form = "passbook.stages.email.forms.EmailStageForm"
def type(self) -> Type[View]:
from passbook.stages.email.stage import EmailStageView
return EmailStageView
def form(self) -> Type[ModelForm]:
from passbook.stages.email.forms import EmailStageForm
return EmailStageForm
@property
def backend(self) -> BaseEmailBackend:

View File

@ -1,7 +1,11 @@
"""identification stage models"""
from typing import Type
from django.contrib.postgres.fields import ArrayField
from django.db import models
from django.forms import ModelForm
from django.utils.translation import gettext_lazy as _
from django.views import View
from passbook.flows.models import Flow, Stage
@ -52,8 +56,15 @@ class IdentificationStage(Stage):
),
)
type = "passbook.stages.identification.stage.IdentificationStageView"
form = "passbook.stages.identification.forms.IdentificationStageForm"
def type(self) -> Type[View]:
from passbook.stages.identification.stage import IdentificationStageView
return IdentificationStageView
def form(self) -> Type[ModelForm]:
from passbook.stages.identification.forms import IdentificationStageForm
return IdentificationStageForm
def __str__(self):
return f"Identification Stage {self.name}"

View File

@ -1,9 +1,12 @@
"""invitation stage models"""
from typing import Type
from uuid import uuid4
from django.contrib.postgres.fields import JSONField
from django.db import models
from django.forms import ModelForm
from django.utils.translation import gettext_lazy as _
from django.views import View
from passbook.core.models import User
from passbook.flows.models import Stage
@ -24,8 +27,15 @@ class InvitationStage(Stage):
),
)
type = "passbook.stages.invitation.stage.InvitationStageView"
form = "passbook.stages.invitation.forms.InvitationStageForm"
def type(self) -> Type[View]:
from passbook.stages.invitation.stage import InvitationStageView
return InvitationStageView
def form(self) -> Type[ModelForm]:
from passbook.stages.invitation.forms import InvitationStageForm
return InvitationStageForm
def __str__(self):
return f"Invitation Stage {self.name}"

View File

@ -1,9 +1,11 @@
"""OTP Static models"""
from typing import Optional
from typing import Optional, Type
from django.db import models
from django.forms import ModelForm
from django.shortcuts import reverse
from django.utils.translation import gettext_lazy as _
from django.views import View
from passbook.core.types import UIUserSettings
from passbook.flows.models import Stage
@ -14,8 +16,15 @@ class OTPStaticStage(Stage):
token_count = models.IntegerField(default=6)
type = "passbook.stages.otp_static.stage.OTPStaticStageView"
form = "passbook.stages.otp_static.forms.OTPStaticStageForm"
def type(self) -> Type[View]:
from passbook.stages.otp_static.stage import OTPStaticStageView
return OTPStaticStageView
def form(self) -> Type[ModelForm]:
from passbook.stages.otp_static.forms import OTPStaticStageForm
return OTPStaticStageForm
@property
def ui_user_settings(self) -> Optional[UIUserSettings]:

View File

@ -1,9 +1,11 @@
"""OTP Time-based models"""
from typing import Optional
from typing import Optional, Type
from django.db import models
from django.forms import ModelForm
from django.shortcuts import reverse
from django.utils.translation import gettext_lazy as _
from django.views import View
from passbook.core.types import UIUserSettings
from passbook.flows.models import Stage
@ -21,8 +23,15 @@ class OTPTimeStage(Stage):
digits = models.IntegerField(choices=TOTPDigits.choices)
type = "passbook.stages.otp_time.stage.OTPTimeStageView"
form = "passbook.stages.otp_time.forms.OTPTimeStageForm"
def type(self) -> Type[View]:
from passbook.stages.otp_time.stage import OTPTimeStageView
return OTPTimeStageView
def form(self) -> Type[ModelForm]:
from passbook.stages.otp_time.forms import OTPTimeStageForm
return OTPTimeStageForm
@property
def ui_user_settings(self) -> Optional[UIUserSettings]:

View File

@ -1,6 +1,10 @@
"""OTP Validation Stage"""
from typing import Type
from django.db import models
from django.forms import ModelForm
from django.utils.translation import gettext_lazy as _
from django.views import View
from passbook.flows.models import NotConfiguredAction, Stage
@ -12,8 +16,15 @@ class OTPValidateStage(Stage):
choices=NotConfiguredAction.choices, default=NotConfiguredAction.SKIP
)
type = "passbook.stages.otp_validate.stage.OTPValidateStageView"
form = "passbook.stages.otp_validate.forms.OTPValidateStageForm"
def type(self) -> Type[View]:
from passbook.stages.otp_validate.stage import OTPValidateStageView
return OTPValidateStageView
def form(self) -> Type[ModelForm]:
from passbook.stages.otp_validate.forms import OTPValidateStageForm
return OTPValidateStageForm
def __str__(self) -> str:
return f"OTP Validation Stage {self.name}"

View File

@ -1,11 +1,13 @@
"""password stage models"""
from typing import Optional
from typing import Optional, Type
from django.contrib.postgres.fields import ArrayField
from django.db import models
from django.forms import ModelForm
from django.shortcuts import reverse
from django.utils.http import urlencode
from django.utils.translation import gettext_lazy as _
from django.views import View
from passbook.core.types import UIUserSettings
from passbook.flows.models import Flow, Stage
@ -33,8 +35,15 @@ class PasswordStage(Stage):
),
)
type = "passbook.stages.password.stage.PasswordStage"
form = "passbook.stages.password.forms.PasswordStageForm"
def type(self) -> Type[View]:
from passbook.stages.password.stage import PasswordStageView
return PasswordStageView
def form(self) -> Type[ModelForm]:
from passbook.stages.password.forms import PasswordStageForm
return PasswordStageForm
@property
def ui_user_settings(self) -> Optional[UIUserSettings]:

View File

@ -46,7 +46,7 @@ def authenticate(
)
class PasswordStage(FormView, StageView):
class PasswordStageView(FormView, StageView):
"""Authentication stage which authenticates against django's AuthBackend"""
form_class = PasswordForm

View File

@ -1,9 +1,12 @@
"""prompt models"""
from typing import Type
from uuid import uuid4
from django import forms
from django.db import models
from django.forms import ModelForm
from django.utils.translation import gettext_lazy as _
from django.views import View
from passbook.flows.models import Stage
from passbook.policies.models import PolicyBindingModel
@ -117,8 +120,15 @@ class PromptStage(PolicyBindingModel, Stage):
fields = models.ManyToManyField(Prompt)
type = "passbook.stages.prompt.stage.PromptStageView"
form = "passbook.stages.prompt.forms.PromptStageForm"
def type(self) -> Type[View]:
from passbook.stages.prompt.stage import PromptStageView
return PromptStageView
def form(self) -> Type[ModelForm]:
from passbook.stages.prompt.forms import PromptStageForm
return PromptStageForm
def __str__(self):
return f"Prompt Stage {self.name}"

View File

@ -1,5 +1,9 @@
"""delete stage models"""
from typing import Type
from django.forms import ModelForm
from django.utils.translation import gettext_lazy as _
from django.views import View
from passbook.flows.models import Stage
@ -8,8 +12,15 @@ class UserDeleteStage(Stage):
"""Deletes the currently pending user without confirmation.
Use with caution."""
type = "passbook.stages.user_delete.stage.UserDeleteStageView"
form = "passbook.stages.user_delete.forms.UserDeleteStageForm"
def type(self) -> Type[View]:
from passbook.stages.user_delete.stage import UserDeleteStageView
return UserDeleteStageView
def form(self) -> Type[ModelForm]:
from passbook.stages.user_delete.forms import UserDeleteStageForm
return UserDeleteStageForm
def __str__(self):
return f"User Delete Stage {self.name}"

View File

@ -1,6 +1,10 @@
"""login stage models"""
from typing import Type
from django.db import models
from django.forms import ModelForm
from django.utils.translation import gettext_lazy as _
from django.views import View
from passbook.flows.models import Stage
@ -16,8 +20,15 @@ class UserLoginStage(Stage):
),
)
type = "passbook.stages.user_login.stage.UserLoginStageView"
form = "passbook.stages.user_login.forms.UserLoginStageForm"
def type(self) -> Type[View]:
from passbook.stages.user_login.stage import UserLoginStageView
return UserLoginStageView
def form(self) -> Type[ModelForm]:
from passbook.stages.user_login.forms import UserLoginStageForm
return UserLoginStageForm
def __str__(self):
return f"User Login Stage {self.name}"

View File

@ -1,5 +1,9 @@
"""logout stage models"""
from typing import Type
from django.forms import ModelForm
from django.utils.translation import gettext_lazy as _
from django.views import View
from passbook.flows.models import Stage
@ -7,8 +11,15 @@ from passbook.flows.models import Stage
class UserLogoutStage(Stage):
"""Resets the users current session."""
type = "passbook.stages.user_logout.stage.UserLogoutStageView"
form = "passbook.stages.user_logout.forms.UserLogoutStageForm"
def type(self) -> Type[View]:
from passbook.stages.user_logout.stage import UserLogoutStageView
return UserLogoutStageView
def form(self) -> Type[ModelForm]:
from passbook.stages.user_logout.forms import UserLogoutStageForm
return UserLogoutStageForm
def __str__(self):
return f"User Logout Stage {self.name}"

View File

@ -1,5 +1,9 @@
"""write stage models"""
from typing import Type
from django.forms import ModelForm
from django.utils.translation import gettext_lazy as _
from django.views import View
from passbook.flows.models import Stage
@ -8,8 +12,15 @@ class UserWriteStage(Stage):
"""Writes currently pending data into the pending user, or if no user exists,
creates a new user with the data."""
type = "passbook.stages.user_write.stage.UserWriteStageView"
form = "passbook.stages.user_write.forms.UserWriteStageForm"
def type(self) -> Type[View]:
from passbook.stages.user_write.stage import UserWriteStageView
return UserWriteStageView
def form(self) -> Type[ModelForm]:
from passbook.stages.user_write.forms import UserWriteStageForm
return UserWriteStageForm
def __str__(self):
return f"User Write Stage {self.name}"