core: create FlowToken instead of regular token for generated recovery links (#3193)
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> #2749
This commit is contained in:
parent
a9636b5727
commit
c39a5933e1
|
@ -63,6 +63,9 @@ from authentik.core.models import (
|
|||
User,
|
||||
)
|
||||
from authentik.events.models import EventAction
|
||||
from authentik.flows.models import FlowToken
|
||||
from authentik.flows.planner import PLAN_CONTEXT_PENDING_USER, FlowPlanner
|
||||
from authentik.flows.views.executor import QS_KEY_TOKEN
|
||||
from authentik.stages.email.models import EmailStage
|
||||
from authentik.stages.email.tasks import send_mails
|
||||
from authentik.stages.email.utils import TemplateEmailMessage
|
||||
|
@ -294,12 +297,23 @@ class UserViewSet(UsedByMixin, ModelViewSet):
|
|||
LOGGER.debug("No recovery flow set")
|
||||
return None, None
|
||||
user: User = self.get_object()
|
||||
token, __ = Token.objects.get_or_create(
|
||||
identifier=f"{user.uid}-password-reset",
|
||||
user=user,
|
||||
intent=TokenIntents.INTENT_RECOVERY,
|
||||
planner = FlowPlanner(flow)
|
||||
planner.allow_empty_flows = True
|
||||
plan = planner.plan(
|
||||
self.request._request,
|
||||
{
|
||||
PLAN_CONTEXT_PENDING_USER: user,
|
||||
},
|
||||
)
|
||||
querystring = urlencode({"token": token.key})
|
||||
token, __ = FlowToken.objects.update_or_create(
|
||||
identifier=f"{user.uid}-password-reset",
|
||||
defaults={
|
||||
"user": user,
|
||||
"flow": flow,
|
||||
"_plan": FlowToken.pickle(plan),
|
||||
},
|
||||
)
|
||||
querystring = urlencode({QS_KEY_TOKEN: token.key})
|
||||
link = self.request.build_absolute_uri(
|
||||
reverse_lazy("authentik_core:if-flow", kwargs={"flow_slug": flow.slug})
|
||||
+ f"?{querystring}"
|
||||
|
|
|
@ -16,6 +16,7 @@ from authentik.core.models import AuthenticatedSession, User
|
|||
from authentik.events.models import Event, EventAction, Notification
|
||||
from authentik.events.signals import EventNewThread
|
||||
from authentik.events.utils import model_to_dict
|
||||
from authentik.flows.models import FlowToken
|
||||
from authentik.lib.sentry import before_send
|
||||
from authentik.lib.utils.errors import exception_to_string
|
||||
|
||||
|
@ -26,6 +27,7 @@ IGNORED_MODELS = [
|
|||
AuthenticatedSession,
|
||||
StaticToken,
|
||||
Session,
|
||||
FlowToken,
|
||||
]
|
||||
if settings.DEBUG:
|
||||
from silk.models import Request, Response, SQLQuery
|
||||
|
|
|
@ -27,6 +27,7 @@ def get_attrs(obj: SerializerModel) -> dict[str, Any]:
|
|||
"promptstage_set",
|
||||
"policybindingmodel_ptr_id",
|
||||
"export_url",
|
||||
"meta_model_name",
|
||||
)
|
||||
for to_remove_name in to_remove:
|
||||
if to_remove_name in data:
|
||||
|
|
|
@ -138,12 +138,11 @@ class FlowExecutorView(APIView):
|
|||
message = exc.__doc__ if exc.__doc__ else str(exc)
|
||||
return self.stage_invalid(error_message=message)
|
||||
|
||||
def _check_flow_token(self, get_params: QueryDict):
|
||||
def _check_flow_token(self, key: str) -> Optional[FlowPlan]:
|
||||
"""Check if the user is using a flow token to restore a plan"""
|
||||
tokens = FlowToken.filter_not_expired(key=get_params[QS_KEY_TOKEN])
|
||||
if not tokens.exists():
|
||||
return False
|
||||
token: FlowToken = tokens.first()
|
||||
token: Optional[FlowToken] = FlowToken.filter_not_expired(key=key).first()
|
||||
if not token:
|
||||
return None
|
||||
try:
|
||||
plan = token.plan
|
||||
except (AttributeError, EOFError, ImportError, IndexError) as exc:
|
||||
|
@ -164,7 +163,7 @@ class FlowExecutorView(APIView):
|
|||
span.set_data("authentik Flow", self.flow.slug)
|
||||
get_params = QueryDict(request.GET.get("query", ""))
|
||||
if QS_KEY_TOKEN in get_params:
|
||||
plan = self._check_flow_token(get_params)
|
||||
plan = self._check_flow_token(get_params[QS_KEY_TOKEN])
|
||||
if plan:
|
||||
self.request.session[SESSION_KEY_PLAN] = plan
|
||||
# Early check if there's an active Plan for the current session
|
||||
|
|
|
@ -122,6 +122,7 @@ class Migration(migrations.Migration):
|
|||
default=list,
|
||||
help_text="Specify which sources should be shown.",
|
||||
to="authentik_core.Source",
|
||||
blank=True,
|
||||
),
|
||||
),
|
||||
migrations.RunPython(
|
||||
|
|
|
@ -87,7 +87,7 @@ class IdentificationStage(Stage):
|
|||
)
|
||||
|
||||
sources = models.ManyToManyField(
|
||||
Source, default=list, help_text=_("Specify which sources should be shown.")
|
||||
Source, default=list, help_text=_("Specify which sources should be shown."), blank=True
|
||||
)
|
||||
show_source_labels = models.BooleanField(default=False)
|
||||
|
||||
|
|
|
@ -10,21 +10,11 @@
|
|||
"attrs": {
|
||||
"name": "Default recovery flow",
|
||||
"title": "Reset your password",
|
||||
"designation": "recovery"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identifiers": {
|
||||
"pk": "1ff91927-e33d-4615-95b0-c258e5f0df62"
|
||||
},
|
||||
"model": "authentik_stages_prompt.prompt",
|
||||
"attrs": {
|
||||
"field_key": "email",
|
||||
"label": "Email",
|
||||
"type": "email",
|
||||
"required": true,
|
||||
"placeholder": "Email",
|
||||
"order": 1
|
||||
"designation": "recovery",
|
||||
"cache_count": 0,
|
||||
"policy_engine_mode": "any",
|
||||
"compatibility_mode": false,
|
||||
"layout": "stacked"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -38,7 +28,9 @@
|
|||
"type": "password",
|
||||
"required": true,
|
||||
"placeholder": "Password",
|
||||
"order": 0
|
||||
"order": 0,
|
||||
"sub_text": "",
|
||||
"placeholder_expression": false
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -52,33 +44,38 @@
|
|||
"type": "password",
|
||||
"required": true,
|
||||
"placeholder": "Password (repeat)",
|
||||
"order": 1
|
||||
"order": 1,
|
||||
"sub_text": "",
|
||||
"placeholder_expression": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"identifiers": {
|
||||
"pk": "e54045a7-6ecb-4ad9-ad37-28e72d8e565e",
|
||||
"name": "default-recovery-identification"
|
||||
"pk": "1c5709ae-1b3e-413a-a117-260ab509bf5c"
|
||||
},
|
||||
"model": "authentik_stages_identification.identificationstage",
|
||||
"model": "authentik_policies_expression.expressionpolicy",
|
||||
"attrs": {
|
||||
"user_fields": ["email", "username"],
|
||||
"template": "stages/identification/recovery.html",
|
||||
"enrollment_flow": null,
|
||||
"recovery_flow": null
|
||||
"name": "default-recovery-skip-if-restored",
|
||||
"execution_logging": false,
|
||||
"bound_to": 2,
|
||||
"expression": "return request.context.get('is_restored', False)"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identifiers": {
|
||||
"pk": "3909fd60-b013-4668-8806-12e9507dab97",
|
||||
"name": "default-recovery-user-write"
|
||||
"pk": "1c5709ae-1b3e-413a-a117-260ab509bf5c"
|
||||
},
|
||||
"model": "authentik_stages_user_write.userwritestage",
|
||||
"attrs": {}
|
||||
"model": "authentik_policies_expression.expressionpolicy",
|
||||
"attrs": {
|
||||
"name": "default-recovery-skip-if-restored",
|
||||
"execution_logging": false,
|
||||
"bound_to": 2,
|
||||
"expression": "return request.context.get('is_restored', False)"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identifiers": {
|
||||
"pk": "66f948dc-3f74-42b2-b26b-b8b9df109efb",
|
||||
"pk": "4ac5719f-32c0-441c-8a7e-33c5ea0db7da",
|
||||
"name": "default-recovery-email"
|
||||
},
|
||||
"model": "authentik_stages_email.emailstage",
|
||||
|
@ -99,20 +96,40 @@
|
|||
},
|
||||
{
|
||||
"identifiers": {
|
||||
"pk": "975d5502-1e22-4d10-b560-fbc5bd70ff4d",
|
||||
"name": "Change your password"
|
||||
"pk": "68b25ad5-318a-496e-95a7-cf4d94247f0d",
|
||||
"name": "default-recovery-user-write"
|
||||
},
|
||||
"model": "authentik_stages_prompt.promptstage",
|
||||
"model": "authentik_stages_user_write.userwritestage",
|
||||
"attrs": {
|
||||
"fields": [
|
||||
"7db91ee8-4290-4e08-8d39-63f132402515",
|
||||
"d30b5eb4-7787-4072-b1ba-65b46e928920"
|
||||
]
|
||||
"create_users_as_inactive": false,
|
||||
"create_users_group": null,
|
||||
"user_path_template": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"identifiers": {
|
||||
"pk": "fcdd4206-0d35-4ad2-a59f-5a72422936bb",
|
||||
"pk": "94843ef6-28fe-4939-bd61-cd46bb34f1de",
|
||||
"name": "default-recovery-identification"
|
||||
},
|
||||
"model": "authentik_stages_identification.identificationstage",
|
||||
"attrs": {
|
||||
"user_fields": [
|
||||
"email",
|
||||
"username"
|
||||
],
|
||||
"password_stage": null,
|
||||
"case_insensitive_matching": true,
|
||||
"show_matched_user": true,
|
||||
"enrollment_flow": null,
|
||||
"recovery_flow": null,
|
||||
"passwordless_flow": null,
|
||||
"sources": [],
|
||||
"show_source_labels": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"identifiers": {
|
||||
"pk": "e74230b2-82bc-4843-8b18-2c3a66a62d57",
|
||||
"name": "default-recovery-user-login"
|
||||
},
|
||||
"model": "authentik_stages_user_login.userloginstage",
|
||||
|
@ -120,64 +137,121 @@
|
|||
"session_duration": "seconds=0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identifiers": {
|
||||
"pk": "fa2d8d65-1809-4dcc-bdc0-56266e0f7971",
|
||||
"name": "Change your password"
|
||||
},
|
||||
"model": "authentik_stages_prompt.promptstage",
|
||||
"attrs": {
|
||||
"fields": [
|
||||
"7db91ee8-4290-4e08-8d39-63f132402515",
|
||||
"d30b5eb4-7787-4072-b1ba-65b46e928920"
|
||||
],
|
||||
"validation_policies": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"identifiers": {
|
||||
"pk": "7af7558e-2196-4b9f-a08e-d38420b7cfbb",
|
||||
"target": "a5993183-89c0-43d2-a7f4-ddffb17baba7",
|
||||
"stage": "e54045a7-6ecb-4ad9-ad37-28e72d8e565e",
|
||||
"stage": "94843ef6-28fe-4939-bd61-cd46bb34f1de",
|
||||
"order": 10
|
||||
},
|
||||
"model": "authentik_flows.flowstagebinding",
|
||||
"attrs": {
|
||||
"re_evaluate_policies": false
|
||||
"evaluate_on_plan": true,
|
||||
"re_evaluate_policies": true,
|
||||
"policy_engine_mode": "any",
|
||||
"invalid_response_action": "retry"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identifiers": {
|
||||
"pk": "29446fd6-dd93-4e92-9830-2d81debad5ae",
|
||||
"target": "a5993183-89c0-43d2-a7f4-ddffb17baba7",
|
||||
"stage": "66f948dc-3f74-42b2-b26b-b8b9df109efb",
|
||||
"stage": "4ac5719f-32c0-441c-8a7e-33c5ea0db7da",
|
||||
"order": 20
|
||||
},
|
||||
"model": "authentik_flows.flowstagebinding",
|
||||
"attrs": {
|
||||
"re_evaluate_policies": false
|
||||
"evaluate_on_plan": true,
|
||||
"re_evaluate_policies": true,
|
||||
"policy_engine_mode": "any",
|
||||
"invalid_response_action": "retry"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identifiers": {
|
||||
"pk": "1219d06e-2c06-4c5b-a162-78e3959c6cf0",
|
||||
"target": "a5993183-89c0-43d2-a7f4-ddffb17baba7",
|
||||
"stage": "975d5502-1e22-4d10-b560-fbc5bd70ff4d",
|
||||
"stage": "fa2d8d65-1809-4dcc-bdc0-56266e0f7971",
|
||||
"order": 30
|
||||
},
|
||||
"model": "authentik_flows.flowstagebinding",
|
||||
"attrs": {
|
||||
"re_evaluate_policies": false
|
||||
"evaluate_on_plan": true,
|
||||
"re_evaluate_policies": false,
|
||||
"policy_engine_mode": "any",
|
||||
"invalid_response_action": "retry"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identifiers": {
|
||||
"pk": "66de86ba-0707-46a0-8475-ff2e260d6935",
|
||||
"target": "a5993183-89c0-43d2-a7f4-ddffb17baba7",
|
||||
"stage": "3909fd60-b013-4668-8806-12e9507dab97",
|
||||
"stage": "68b25ad5-318a-496e-95a7-cf4d94247f0d",
|
||||
"order": 40
|
||||
},
|
||||
"model": "authentik_flows.flowstagebinding",
|
||||
"attrs": {
|
||||
"re_evaluate_policies": false
|
||||
"evaluate_on_plan": true,
|
||||
"re_evaluate_policies": false,
|
||||
"policy_engine_mode": "any",
|
||||
"invalid_response_action": "retry"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identifiers": {
|
||||
"pk": "9cec2334-d4a2-4895-a2b2-bc5ae4e9639a",
|
||||
"target": "a5993183-89c0-43d2-a7f4-ddffb17baba7",
|
||||
"stage": "fcdd4206-0d35-4ad2-a59f-5a72422936bb",
|
||||
"stage": "e74230b2-82bc-4843-8b18-2c3a66a62d57",
|
||||
"order": 100
|
||||
},
|
||||
"model": "authentik_flows.flowstagebinding",
|
||||
"attrs": {
|
||||
"re_evaluate_policies": false
|
||||
"evaluate_on_plan": true,
|
||||
"re_evaluate_policies": false,
|
||||
"policy_engine_mode": "any",
|
||||
"invalid_response_action": "retry"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identifiers": {
|
||||
"pk": "95aad215-8729-4177-953d-41ffbe86239e",
|
||||
"policy": "1c5709ae-1b3e-413a-a117-260ab509bf5c",
|
||||
"target": "7af7558e-2196-4b9f-a08e-d38420b7cfbb",
|
||||
"order": 0
|
||||
},
|
||||
"model": "authentik_policies.policybinding",
|
||||
"attrs": {
|
||||
"negate": false,
|
||||
"enabled": true,
|
||||
"timeout": 30
|
||||
}
|
||||
},
|
||||
{
|
||||
"identifiers": {
|
||||
"pk": "a5454cbc-d2e4-403a-84af-6af999990b12",
|
||||
"policy": "1c5709ae-1b3e-413a-a117-260ab509bf5c",
|
||||
"target": "29446fd6-dd93-4e92-9830-2d81debad5ae",
|
||||
"order": 0
|
||||
},
|
||||
"model": "authentik_policies.policybinding",
|
||||
"attrs": {
|
||||
"negate": false,
|
||||
"enabled": true,
|
||||
"timeout": 30
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
Reference in New Issue