*: remove unused templates and code, move avatar to User model
This commit is contained in:
parent
cf7e7c44ff
commit
890e0e9054
|
@ -2,28 +2,20 @@
|
|||
from drf_yasg2.utils import swagger_auto_schema
|
||||
from guardian.utils import get_anonymous_user
|
||||
from rest_framework.decorators import action
|
||||
from rest_framework.fields import CharField
|
||||
from rest_framework.request import Request
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.serializers import (
|
||||
BooleanField,
|
||||
ModelSerializer,
|
||||
SerializerMethodField,
|
||||
)
|
||||
from rest_framework.serializers import BooleanField, ModelSerializer
|
||||
from rest_framework.viewsets import ModelViewSet
|
||||
|
||||
from authentik.core.models import User
|
||||
from authentik.lib.templatetags.authentik_utils import avatar
|
||||
|
||||
|
||||
class UserSerializer(ModelSerializer):
|
||||
"""User Serializer"""
|
||||
|
||||
is_superuser = BooleanField(read_only=True)
|
||||
avatar = SerializerMethodField()
|
||||
|
||||
def get_avatar(self, user: User) -> str:
|
||||
"""Add user's avatar as URL"""
|
||||
return avatar(user)
|
||||
avatar = CharField(read_only=True)
|
||||
|
||||
class Meta:
|
||||
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
"""authentik core models"""
|
||||
from datetime import timedelta
|
||||
from hashlib import sha256
|
||||
from hashlib import md5, sha256
|
||||
from typing import Any, Optional, Type
|
||||
from urllib.parse import urlencode
|
||||
from uuid import uuid4
|
||||
|
||||
from django.conf import settings
|
||||
|
@ -11,7 +12,9 @@ from django.db import models
|
|||
from django.db.models import Q, QuerySet
|
||||
from django.forms import ModelForm
|
||||
from django.http import HttpRequest
|
||||
from django.templatetags.static import static
|
||||
from django.utils.functional import cached_property
|
||||
from django.utils.html import escape
|
||||
from django.utils.timezone import now
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from guardian.mixins import GuardianUserMixin
|
||||
|
@ -23,6 +26,7 @@ from authentik.core.exceptions import PropertyMappingExpressionException
|
|||
from authentik.core.signals import password_changed
|
||||
from authentik.core.types import UILoginButton
|
||||
from authentik.flows.models import Flow
|
||||
from authentik.lib.config import CONFIG
|
||||
from authentik.lib.models import CreatedUpdatedModel, SerializerModel
|
||||
from authentik.managed.models import ManagedModel
|
||||
from authentik.policies.models import PolicyBindingModel
|
||||
|
@ -31,6 +35,9 @@ LOGGER = get_logger()
|
|||
USER_ATTRIBUTE_DEBUG = "goauthentik.io/user/debug"
|
||||
USER_ATTRIBUTE_SA = "goauthentik.io/user/service-account"
|
||||
|
||||
GRAVATAR_URL = "https://secure.gravatar.com"
|
||||
DEFAULT_AVATAR = static("authentik/user_default.png")
|
||||
|
||||
|
||||
def default_token_duration():
|
||||
"""Default duration a Token is valid"""
|
||||
|
@ -126,6 +133,25 @@ class User(GuardianUserMixin, AbstractUser):
|
|||
"""Generate a globall unique UID, based on the user ID and the hashed secret key"""
|
||||
return sha256(f"{self.id}-{settings.SECRET_KEY}".encode("ascii")).hexdigest()
|
||||
|
||||
@property
|
||||
def avatar(self) -> str:
|
||||
"""Get avatar, depending on authentik.avatar setting"""
|
||||
mode = CONFIG.raw.get("authentik").get("avatars")
|
||||
if mode == "none":
|
||||
return DEFAULT_AVATAR
|
||||
if mode == "gravatar":
|
||||
parameters = [
|
||||
("s", "158"),
|
||||
("r", "g"),
|
||||
]
|
||||
# gravatar uses md5 for their URLs, so md5 can't be avoided
|
||||
mail_hash = md5(self.email.encode("utf-8")).hexdigest() # nosec
|
||||
gravatar_url = (
|
||||
f"{GRAVATAR_URL}/avatar/{mail_hash}?{urlencode(parameters, doseq=True)}"
|
||||
)
|
||||
return escape(gravatar_url)
|
||||
raise ValueError(f"Invalid avatar mode {mode}")
|
||||
|
||||
class Meta:
|
||||
|
||||
permissions = (
|
||||
|
|
|
@ -1,19 +0,0 @@
|
|||
{% extends 'login/base.html' %}
|
||||
|
||||
{% load static %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block card %}
|
||||
<form method="POST" class="pf-c-form">
|
||||
{% block above_form %}
|
||||
{% endblock %}
|
||||
|
||||
{% include 'partials/form.html' %}
|
||||
|
||||
{% block beneath_form %}
|
||||
{% endblock %}
|
||||
<div class="pf-c-form__group pf-m-action">
|
||||
<button class="pf-c-button pf-m-primary pf-m-block" type="submit">{% trans primary_action %}</button>
|
||||
</div>
|
||||
</form>
|
||||
{% endblock %}
|
|
@ -1,18 +0,0 @@
|
|||
{% extends 'login/form.html' %}
|
||||
|
||||
{% load i18n %}
|
||||
{% load authentik_utils %}
|
||||
|
||||
{% block above_form %}
|
||||
<div class="pf-c-form__group">
|
||||
<div class="form-control-static">
|
||||
<div class="left">
|
||||
<img class="pf-c-avatar" src="{% avatar user %}" alt="">
|
||||
{{ user.username }}
|
||||
</div>
|
||||
<div class="right">
|
||||
<a href="{% url 'authentik_flows:cancel' %}">{% trans 'Not you?' %}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
|
@ -1,24 +0,0 @@
|
|||
{% extends 'login/base.html' %}
|
||||
|
||||
{% load static %}
|
||||
{% load i18n %}
|
||||
{% load authentik_utils %}
|
||||
|
||||
{% block title %}
|
||||
{% trans title %}
|
||||
{% endblock %}
|
||||
|
||||
{% block head %}
|
||||
<meta http-equiv="refresh" content="0; url={{ target_url }}" />
|
||||
{% endblock %}
|
||||
|
||||
{% block card %}
|
||||
<header class="login-pf-header">
|
||||
<h1>{% trans title %}</h1>
|
||||
</header>
|
||||
<form>
|
||||
<div class="form-group">
|
||||
<div class="spinner spinner-lg"></div>
|
||||
</div>
|
||||
</form>
|
||||
{% endblock %}
|
|
@ -1,14 +1,12 @@
|
|||
"""authentik stage Base view"""
|
||||
from typing import Any, Optional
|
||||
|
||||
from django.contrib.auth.models import AnonymousUser
|
||||
from django.http import HttpRequest
|
||||
from django.http.request import QueryDict
|
||||
from django.http.response import HttpResponse
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django.views.generic import TemplateView
|
||||
from django.views.generic.base import View
|
||||
from structlog.stdlib import get_logger
|
||||
|
||||
from authentik.core.models import User
|
||||
from authentik.core.models import DEFAULT_AVATAR, User
|
||||
from authentik.flows.challenge import (
|
||||
Challenge,
|
||||
ChallengeResponse,
|
||||
|
@ -17,53 +15,29 @@ from authentik.flows.challenge import (
|
|||
)
|
||||
from authentik.flows.planner import PLAN_CONTEXT_PENDING_USER
|
||||
from authentik.flows.views import FlowExecutorView
|
||||
from authentik.lib.templatetags.authentik_utils import avatar
|
||||
|
||||
PLAN_CONTEXT_PENDING_USER_IDENTIFIER = "pending_user_identifier"
|
||||
LOGGER = get_logger()
|
||||
|
||||
|
||||
class StageView(TemplateView):
|
||||
class StageView(View):
|
||||
"""Abstract Stage, inherits TemplateView but can be combined with FormView"""
|
||||
|
||||
template_name = "login/form_with_user.html"
|
||||
|
||||
executor: FlowExecutorView
|
||||
|
||||
request: HttpRequest = None
|
||||
|
||||
def __init__(self, executor: FlowExecutorView):
|
||||
def __init__(self, executor: FlowExecutorView, **kwargs):
|
||||
self.executor = executor
|
||||
super().__init__(**kwargs)
|
||||
|
||||
def get_context_data(self, **kwargs: Any) -> dict[str, Any]:
|
||||
kwargs["title"] = self.executor.flow.title
|
||||
# Either show the matched User object or show what the user entered,
|
||||
# based on what the earlier stage (mostly IdentificationStage) set.
|
||||
# _USER_IDENTIFIER overrides the first User, as PENDING_USER is used for
|
||||
# other things besides the form display
|
||||
if PLAN_CONTEXT_PENDING_USER in self.executor.plan.context:
|
||||
kwargs["user"] = self.executor.plan.context[PLAN_CONTEXT_PENDING_USER]
|
||||
if PLAN_CONTEXT_PENDING_USER_IDENTIFIER in self.executor.plan.context:
|
||||
kwargs["user"] = User(
|
||||
username=self.executor.plan.context.get(
|
||||
PLAN_CONTEXT_PENDING_USER_IDENTIFIER
|
||||
),
|
||||
email="",
|
||||
)
|
||||
kwargs["primary_action"] = _("Continue")
|
||||
return super().get_context_data(**kwargs)
|
||||
|
||||
|
||||
class ChallengeStageView(StageView):
|
||||
"""Stage view which response with a challenge"""
|
||||
|
||||
response_class = ChallengeResponse
|
||||
|
||||
def get_pending_user(self) -> Optional[User]:
|
||||
def get_pending_user(self) -> User:
|
||||
"""Either show the matched User object or show what the user entered,
|
||||
based on what the earlier stage (mostly IdentificationStage) set.
|
||||
_USER_IDENTIFIER overrides the first User, as PENDING_USER is used for
|
||||
other things besides the form display"""
|
||||
other things besides the form display.
|
||||
|
||||
If no user is pending, returns request.user"""
|
||||
if PLAN_CONTEXT_PENDING_USER_IDENTIFIER in self.executor.plan.context:
|
||||
return User(
|
||||
username=self.executor.plan.context.get(
|
||||
|
@ -73,13 +47,20 @@ class ChallengeStageView(StageView):
|
|||
)
|
||||
if PLAN_CONTEXT_PENDING_USER in self.executor.plan.context:
|
||||
return self.executor.plan.context[PLAN_CONTEXT_PENDING_USER]
|
||||
return None
|
||||
return self.request.user
|
||||
|
||||
|
||||
class ChallengeStageView(StageView):
|
||||
"""Stage view which response with a challenge"""
|
||||
|
||||
response_class = ChallengeResponse
|
||||
|
||||
def get_response_instance(self, data: QueryDict) -> ChallengeResponse:
|
||||
"""Return the response class type"""
|
||||
return self.response_class(None, data=data, stage=self)
|
||||
|
||||
def get(self, request: HttpRequest, *args, **kwargs) -> HttpResponse:
|
||||
"""Return a challenge for the frontend to solve"""
|
||||
challenge = self._get_challenge(*args, **kwargs)
|
||||
if not challenge.is_valid():
|
||||
LOGGER.warning(challenge.errors)
|
||||
|
@ -103,7 +84,9 @@ class ChallengeStageView(StageView):
|
|||
# If there's no user set, an error is raised later.
|
||||
if user := self.get_pending_user():
|
||||
challenge.initial_data["pending_user"] = user.username
|
||||
challenge.initial_data["pending_user_avatar"] = avatar(user)
|
||||
challenge.initial_data["pending_user_avatar"] = DEFAULT_AVATAR
|
||||
if not isinstance(user, AnonymousUser):
|
||||
challenge.initial_data["pending_user_avatar"] = user.avatar
|
||||
return challenge
|
||||
|
||||
def get_challenge(self, *args, **kwargs) -> Challenge:
|
||||
|
|
|
@ -8,6 +8,7 @@ from django.urls import reverse
|
|||
from django.utils.encoding import force_str
|
||||
|
||||
from authentik.core.models import User
|
||||
from authentik.flows.challenge import ChallengeTypes
|
||||
from authentik.flows.exceptions import FlowNonApplicableException
|
||||
from authentik.flows.markers import ReevaluateMarker, StageMarker
|
||||
from authentik.flows.models import Flow, FlowDesignation, FlowStageBinding
|
||||
|
@ -404,7 +405,14 @@ class TestFlowExecutor(TestCase):
|
|||
# First request, run the planner
|
||||
response = self.client.get(exec_url)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertIn("dummy1", force_str(response.content))
|
||||
self.assertJSONEqual(
|
||||
force_str(response.content),
|
||||
{
|
||||
"type": ChallengeTypes.native.value,
|
||||
"component": "",
|
||||
"title": binding.stage.name,
|
||||
},
|
||||
)
|
||||
|
||||
plan: FlowPlan = self.client.session[SESSION_KEY_PLAN]
|
||||
|
||||
|
@ -427,7 +435,14 @@ class TestFlowExecutor(TestCase):
|
|||
# but it won't save it, hence we cant' check the plan
|
||||
response = self.client.get(exec_url)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertIn("dummy4", force_str(response.content))
|
||||
self.assertJSONEqual(
|
||||
force_str(response.content),
|
||||
{
|
||||
"type": ChallengeTypes.native.value,
|
||||
"component": "",
|
||||
"title": binding4.stage.name,
|
||||
},
|
||||
)
|
||||
|
||||
# fourth request, this confirms the last stage (dummy4)
|
||||
# We do this request without the patch, so the policy results in false
|
||||
|
@ -466,4 +481,4 @@ class TestFlowExecutor(TestCase):
|
|||
executor.flow = flow
|
||||
|
||||
stage_view = StageView(executor)
|
||||
self.assertEqual(ident, stage_view.get_context_data()["user"].username)
|
||||
self.assertEqual(ident, stage_view.get_pending_user().username)
|
||||
|
|
|
@ -1,17 +0,0 @@
|
|||
{% load authentik_utils %}
|
||||
|
||||
{% spaceless %}
|
||||
<div class="dynamic-array-widget">
|
||||
{% for widget in widget.subwidgets %}
|
||||
<div class="array-item input-group">
|
||||
{% include widget.template_name %}
|
||||
<div class="input-group-btn">
|
||||
<button class="array-remove btn btn-danger" type="button">
|
||||
<span class="pficon-delete"></span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
<div><button type="button" class="add-array-item btn btn-default">Add another</button></div>
|
||||
</div>
|
||||
{% endspaceless %}
|
|
@ -1,25 +1,15 @@
|
|||
"""authentik lib Templatetags"""
|
||||
from hashlib import md5
|
||||
from urllib.parse import urlencode
|
||||
|
||||
from django import template
|
||||
from django.db.models import Model
|
||||
from django.http.request import HttpRequest
|
||||
from django.template import Context
|
||||
from django.templatetags.static import static
|
||||
from django.utils.html import escape, mark_safe
|
||||
from structlog.stdlib import get_logger
|
||||
|
||||
from authentik.core.models import User
|
||||
from authentik.lib.config import CONFIG
|
||||
from authentik.lib.utils.urls import is_url_absolute
|
||||
|
||||
register = template.Library()
|
||||
LOGGER = get_logger()
|
||||
|
||||
GRAVATAR_URL = "https://secure.gravatar.com"
|
||||
DEFAULT_AVATAR = static("authentik/user_default.png")
|
||||
|
||||
|
||||
@register.simple_tag(takes_context=True)
|
||||
def back(context: Context) -> str:
|
||||
|
@ -46,38 +36,12 @@ def fieldtype(field):
|
|||
return field.__class__.__name__
|
||||
|
||||
|
||||
@register.simple_tag
|
||||
def config(path, default=""):
|
||||
"""Get a setting from the database. Returns default is setting doesn't exist."""
|
||||
return CONFIG.y(path, default)
|
||||
|
||||
|
||||
@register.filter(name="css_class")
|
||||
def css_class(field, css):
|
||||
"""Add css class to form field"""
|
||||
return field.as_widget(attrs={"class": css})
|
||||
|
||||
|
||||
@register.simple_tag
|
||||
def avatar(user: User) -> str:
|
||||
"""Get avatar, depending on authentik.avatar setting"""
|
||||
mode = CONFIG.raw.get("authentik").get("avatars")
|
||||
if mode == "none":
|
||||
return DEFAULT_AVATAR
|
||||
if mode == "gravatar":
|
||||
parameters = [
|
||||
("s", "158"),
|
||||
("r", "g"),
|
||||
]
|
||||
# gravatar uses md5 for their URLs, so md5 can't be avoided
|
||||
mail_hash = md5(user.email.encode("utf-8")).hexdigest() # nosec
|
||||
gravatar_url = (
|
||||
f"{GRAVATAR_URL}/avatar/{mail_hash}?{urlencode(parameters, doseq=True)}"
|
||||
)
|
||||
return escape(gravatar_url)
|
||||
raise ValueError(f"Invalid avatar mode {mode}")
|
||||
|
||||
|
||||
@register.filter
|
||||
def verbose_name(obj) -> str:
|
||||
"""Return Object's Verbose Name"""
|
||||
|
@ -94,21 +58,3 @@ def form_verbose_name(obj) -> str:
|
|||
if not obj:
|
||||
return ""
|
||||
return verbose_name(obj._meta.model)
|
||||
|
||||
|
||||
@register.filter
|
||||
def doc(obj) -> str:
|
||||
"""Return docstring of object"""
|
||||
return mark_safe(obj.__doc__.replace("\n", "<br>"))
|
||||
|
||||
|
||||
@register.simple_tag(takes_context=True)
|
||||
def query_transform(context: Context, **kwargs) -> str:
|
||||
"""Append objects to the current querystring"""
|
||||
if "request" not in context:
|
||||
return ""
|
||||
request: HttpRequest = context["request"]
|
||||
updated = request.GET.copy()
|
||||
for key, value in kwargs.items():
|
||||
updated[key] = value
|
||||
return updated.urlencode()
|
||||
|
|
|
@ -1,11 +0,0 @@
|
|||
"""authentik UI utils"""
|
||||
from typing import Any
|
||||
|
||||
|
||||
def human_list(_list: list[Any]) -> str:
|
||||
"""Convert a list of items into 'a, b or c'"""
|
||||
last_item = _list.pop()
|
||||
if len(_list) < 1:
|
||||
return last_item
|
||||
result = ", ".join(_list)
|
||||
return "%s or %s" % (result, last_item)
|
|
@ -233,7 +233,9 @@ class OAuthFulfillmentStage(StageView):
|
|||
params: OAuthAuthorizationParams
|
||||
provider: OAuth2Provider
|
||||
|
||||
# pylint: disable=unused-argument
|
||||
def get(self, request: HttpRequest, *args, **kwargs) -> HttpResponse:
|
||||
"""final Stage of an OAuth2 Flow"""
|
||||
self.params: OAuthAuthorizationParams = self.executor.plan.context.pop(
|
||||
PLAN_CONTEXT_PARAMS
|
||||
)
|
||||
|
|
|
@ -14,7 +14,9 @@ class PostUserEnrollmentStage(StageView):
|
|||
"""Dynamically injected stage which saves the OAuth Connection after
|
||||
the user has been enrolled."""
|
||||
|
||||
# pylint: disable=unused-argument
|
||||
def get(self, request: HttpRequest, *args, **kwargs) -> HttpResponse:
|
||||
"""Stage used after the user has been enrolled"""
|
||||
access: UserOAuthSourceConnection = self.executor.plan.context[
|
||||
PLAN_CONTEXT_SOURCES_OAUTH_ACCESS
|
||||
]
|
||||
|
|
|
@ -16,7 +16,6 @@ from webauthn.webauthn import (
|
|||
)
|
||||
|
||||
from authentik.core.models import User
|
||||
from authentik.lib.templatetags.authentik_utils import avatar
|
||||
from authentik.stages.authenticator_webauthn.models import WebAuthnDevice
|
||||
from authentik.stages.authenticator_webauthn.utils import generate_challenge
|
||||
|
||||
|
@ -57,7 +56,7 @@ def get_webauthn_challenge(request: HttpRequest, device: WebAuthnDevice) -> dict
|
|||
device.user.uid,
|
||||
device.user.username,
|
||||
device.user.name,
|
||||
avatar(device.user),
|
||||
device.user.avatar,
|
||||
device.credential_id,
|
||||
device.public_key,
|
||||
device.sign_count,
|
||||
|
@ -92,7 +91,7 @@ def validate_challenge_webauthn(data: dict, request: HttpRequest, user: User) ->
|
|||
user.uid,
|
||||
user.username,
|
||||
user.name,
|
||||
avatar(user),
|
||||
user.avatar,
|
||||
device.credential_id,
|
||||
device.public_key,
|
||||
device.sign_count,
|
||||
|
|
|
@ -16,7 +16,6 @@ from authentik.core.models import User
|
|||
from authentik.flows.challenge import Challenge, ChallengeResponse, ChallengeTypes
|
||||
from authentik.flows.planner import PLAN_CONTEXT_PENDING_USER
|
||||
from authentik.flows.stage import ChallengeStageView
|
||||
from authentik.lib.templatetags.authentik_utils import avatar
|
||||
from authentik.stages.authenticator_webauthn.models import WebAuthnDevice
|
||||
from authentik.stages.authenticator_webauthn.utils import (
|
||||
generate_challenge,
|
||||
|
@ -118,7 +117,7 @@ class AuthenticatorWebAuthnStageView(ChallengeStageView):
|
|||
user.uid,
|
||||
user.username,
|
||||
user.name,
|
||||
avatar(user or User()),
|
||||
user.avatar,
|
||||
)
|
||||
|
||||
return AuthenticatorWebAuthnChallenge(
|
||||
|
|
|
@ -12,7 +12,6 @@ from authentik.flows.challenge import (
|
|||
)
|
||||
from authentik.flows.planner import PLAN_CONTEXT_APPLICATION, PLAN_CONTEXT_PENDING_USER
|
||||
from authentik.flows.stage import ChallengeStageView
|
||||
from authentik.lib.templatetags.authentik_utils import avatar
|
||||
from authentik.lib.utils.time import timedelta_from_string
|
||||
from authentik.stages.consent.models import ConsentMode, ConsentStage, UserConsent
|
||||
|
||||
|
@ -56,7 +55,7 @@ class ConsentStageView(ChallengeStageView):
|
|||
# If there's no user set, an error is raised later.
|
||||
if user := self.get_pending_user():
|
||||
challenge.initial_data["pending_user"] = user.username
|
||||
challenge.initial_data["pending_user_avatar"] = avatar(user)
|
||||
challenge.initial_data["pending_user_avatar"] = user.avatar
|
||||
return challenge
|
||||
|
||||
def get(self, request: HttpRequest, *args, **kwargs) -> HttpResponse:
|
||||
|
|
|
@ -1,19 +1,31 @@
|
|||
"""authentik multi-stage authentication engine"""
|
||||
from typing import Any
|
||||
from django.http.response import HttpResponse
|
||||
|
||||
from django.http import HttpRequest
|
||||
|
||||
from authentik.flows.stage import StageView
|
||||
from authentik.flows.challenge import Challenge, ChallengeResponse, ChallengeTypes
|
||||
from authentik.flows.stage import ChallengeStageView
|
||||
|
||||
|
||||
class DummyStageView(StageView):
|
||||
class DummyChallenge(Challenge):
|
||||
"""Dummy challenge"""
|
||||
|
||||
|
||||
class DummyChallengeResponse(ChallengeResponse):
|
||||
"""Dummy challenge response"""
|
||||
|
||||
|
||||
class DummyStageView(ChallengeStageView):
|
||||
"""Dummy stage for testing with multiple stages"""
|
||||
|
||||
def post(self, request: HttpRequest):
|
||||
"""Just redirect to next stage"""
|
||||
response_class = DummyChallengeResponse
|
||||
|
||||
def challenge_valid(self, response: ChallengeResponse) -> HttpResponse:
|
||||
return self.executor.stage_ok()
|
||||
|
||||
def get_context_data(self, **kwargs: dict[str, Any]) -> dict[str, Any]:
|
||||
kwargs = super().get_context_data(**kwargs)
|
||||
kwargs["title"] = self.executor.current_stage.name
|
||||
return kwargs
|
||||
def get_challenge(self, *args, **kwargs) -> Challenge:
|
||||
return DummyChallenge(
|
||||
data={
|
||||
"type": ChallengeTypes.native,
|
||||
"component": "",
|
||||
"title": self.executor.current_stage.name,
|
||||
}
|
||||
)
|
||||
|
|
|
@ -15,6 +15,7 @@ class InvitationStageView(StageView):
|
|||
"""Finalise Authentication flow by logging the user in"""
|
||||
|
||||
def get(self, request: HttpRequest) -> HttpResponse:
|
||||
"""Apply data to the current flow based on a URL"""
|
||||
stage: InvitationStage = self.executor.current_stage
|
||||
if INVITATION_TOKEN_KEY not in request.GET:
|
||||
# No Invitation was given, raise error or continue
|
||||
|
|
|
@ -88,7 +88,9 @@ class PasswordStageView(ChallengeStageView):
|
|||
"authentik_flows:flow-executor-shell",
|
||||
kwargs={"flow_slug": recovery_flow.first().slug},
|
||||
)
|
||||
challenge.initial_data["recovery_url"] = self.request.build_absolute_uri(recover_url)
|
||||
challenge.initial_data["recovery_url"] = self.request.build_absolute_uri(
|
||||
recover_url
|
||||
)
|
||||
return challenge
|
||||
|
||||
def challenge_invalid(self, response: PasswordChallengeResponse) -> HttpResponse:
|
||||
|
|
|
@ -17,6 +17,7 @@ class UserLoginStageView(StageView):
|
|||
"""Finalise Authentication flow by logging the user in"""
|
||||
|
||||
def get(self, request: HttpRequest) -> HttpResponse:
|
||||
"""Attach the currently pending user to the current session"""
|
||||
if PLAN_CONTEXT_PENDING_USER not in self.executor.plan.context:
|
||||
message = _("No Pending user to login.")
|
||||
messages.error(request, message)
|
||||
|
|
|
@ -12,6 +12,7 @@ class UserLogoutStageView(StageView):
|
|||
"""Finalise Authentication flow by logging the user in"""
|
||||
|
||||
def get(self, request: HttpRequest) -> HttpResponse:
|
||||
"""Remove the user from the current session"""
|
||||
LOGGER.debug(
|
||||
"Logged out",
|
||||
user=request.user,
|
||||
|
|
|
@ -22,6 +22,8 @@ class UserWriteStageView(StageView):
|
|||
"""Finalise Enrollment flow by creating a user object."""
|
||||
|
||||
def get(self, request: HttpRequest) -> HttpResponse:
|
||||
"""Save data in the current flow to the currently pending user. If no user is pending,
|
||||
a new user is created."""
|
||||
if PLAN_CONTEXT_PROMPT not in self.executor.plan.context:
|
||||
message = _("No Pending data.")
|
||||
messages.error(request, message)
|
||||
|
|
Reference in New Issue