flows: add shortcut to redirect current flow (#3192)
This commit is contained in:
		| @ -47,7 +47,8 @@ class ReevaluateMarker(StageMarker): | ||||
|         from authentik.flows.planner import PLAN_CONTEXT_PENDING_USER | ||||
|  | ||||
|         LOGGER.debug( | ||||
|             "f(plan_inst)[re-eval marker]: running re-evaluation", | ||||
|             "f(plan_inst): running re-evaluation", | ||||
|             marker="ReevaluateMarker", | ||||
|             binding=binding, | ||||
|             policy_binding=self.binding, | ||||
|         ) | ||||
| @ -56,13 +57,15 @@ class ReevaluateMarker(StageMarker): | ||||
|         ) | ||||
|         engine.use_cache = False | ||||
|         engine.request.set_http_request(http_request) | ||||
|         engine.request.context = plan.context | ||||
|         engine.request.context["flow_plan"] = plan | ||||
|         engine.request.context.update(plan.context) | ||||
|         engine.build() | ||||
|         result = engine.result | ||||
|         if result.passing: | ||||
|             return binding | ||||
|         LOGGER.warning( | ||||
|             "f(plan_inst)[re-eval marker]: binding failed re-evaluation", | ||||
|             "f(plan_inst): binding failed re-evaluation", | ||||
|             marker="ReevaluateMarker", | ||||
|             binding=binding, | ||||
|             messages=result.messages, | ||||
|         ) | ||||
|  | ||||
| @ -87,13 +87,15 @@ class Stage(SerializerModel): | ||||
|         return f"Stage {self.name}" | ||||
|  | ||||
|  | ||||
| def in_memory_stage(view: type["StageView"]) -> Stage: | ||||
| def in_memory_stage(view: type["StageView"], **kwargs) -> Stage: | ||||
|     """Creates an in-memory stage instance, based on a `view` as view.""" | ||||
|     stage = Stage() | ||||
|     # 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) | ||||
|     for key, value in kwargs.items(): | ||||
|         setattr(stage, key, value) | ||||
|     return stage | ||||
|  | ||||
|  | ||||
|  | ||||
| @ -13,7 +13,7 @@ from authentik.events.models import cleanse_dict | ||||
| from authentik.flows.apps import HIST_FLOWS_PLAN_TIME | ||||
| from authentik.flows.exceptions import EmptyFlowException, FlowNonApplicableException | ||||
| from authentik.flows.markers import ReevaluateMarker, StageMarker | ||||
| from authentik.flows.models import Flow, FlowDesignation, FlowStageBinding, Stage | ||||
| from authentik.flows.models import Flow, FlowDesignation, FlowStageBinding, Stage, in_memory_stage | ||||
| from authentik.lib.config import CONFIG | ||||
| from authentik.policies.engine import PolicyEngine | ||||
|  | ||||
| @ -62,6 +62,12 @@ class FlowPlan: | ||||
|         self.bindings.insert(1, FlowStageBinding(stage=stage, order=0)) | ||||
|         self.markers.insert(1, marker or StageMarker()) | ||||
|  | ||||
|     def redirect(self, destination: str): | ||||
|         """Insert a redirect stage as next stage""" | ||||
|         from authentik.flows.stage import RedirectStage | ||||
|  | ||||
|         self.insert_stage(in_memory_stage(RedirectStage, destination=destination)) | ||||
|  | ||||
|     def next(self, http_request: Optional[HttpRequest]) -> Optional[FlowStageBinding]: | ||||
|         """Return next pending stage from the bottom of the list""" | ||||
|         if not self.has_stages: | ||||
| @ -137,7 +143,7 @@ class FlowPlanner: | ||||
|             engine = PolicyEngine(self.flow, user, request) | ||||
|             if default_context: | ||||
|                 span.set_data("default_context", cleanse_dict(default_context)) | ||||
|                 engine.request.context = default_context | ||||
|                 engine.request.context.update(default_context) | ||||
|             engine.build() | ||||
|             result = engine.result | ||||
|             if not result.passing: | ||||
| @ -198,7 +204,8 @@ class FlowPlanner: | ||||
|                         stage=binding.stage, | ||||
|                     ) | ||||
|                     engine = PolicyEngine(binding, user, request) | ||||
|                     engine.request.context = plan.context | ||||
|                     engine.request.context["flow_plan"] = plan | ||||
|                     engine.request.context.update(plan.context) | ||||
|                     engine.build() | ||||
|                     if engine.passing: | ||||
|                         self._logger.debug( | ||||
|  | ||||
| @ -19,6 +19,7 @@ from authentik.flows.challenge import ( | ||||
|     ChallengeTypes, | ||||
|     ContextualFlowInfo, | ||||
|     HttpChallengeResponse, | ||||
|     RedirectChallenge, | ||||
|     WithUserInfoChallenge, | ||||
| ) | ||||
| from authentik.flows.models import InvalidResponseAction | ||||
| @ -219,3 +220,21 @@ class AccessDeniedChallengeView(ChallengeStageView): | ||||
|     # .get() method is called | ||||
|     def challenge_valid(self, response: ChallengeResponse) -> HttpResponse:  # pragma: no cover | ||||
|         return self.executor.cancel() | ||||
|  | ||||
|  | ||||
| class RedirectStage(ChallengeStageView): | ||||
|     """Redirect to any URL""" | ||||
|  | ||||
|     def get_challenge(self, *args, **kwargs) -> RedirectChallenge: | ||||
|         destination = getattr( | ||||
|             self.executor.current_stage, "destination", reverse("authentik_core:root-redirect") | ||||
|         ) | ||||
|         return RedirectChallenge( | ||||
|             data={ | ||||
|                 "type": ChallengeTypes.REDIRECT.value, | ||||
|                 "to": destination, | ||||
|             } | ||||
|         ) | ||||
|  | ||||
|     def challenge_valid(self, response: ChallengeResponse) -> HttpResponse: | ||||
|         return HttpChallengeResponse(self.get_challenge()) | ||||
|  | ||||
							
								
								
									
										17
									
								
								website/docs/flow/examples/snippets.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								website/docs/flow/examples/snippets.md
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,17 @@ | ||||
| --- | ||||
| title: Example policy snippets for flows | ||||
| --- | ||||
|  | ||||
| ### Redirect current flow to another URL | ||||
|  | ||||
| :::info | ||||
| Requires authentik 2022.7 | ||||
| ::: | ||||
|  | ||||
| ```python | ||||
| plan = request.context["flow_plan"] | ||||
| plan.redirect("https://foo.bar") | ||||
| return False | ||||
| ``` | ||||
|  | ||||
| This policy should be bound to the stage after your redirect should happen. For example, if you have an identification and a password stage, and you want to redirect after identification, bind the policy to the password stage. Make sure the policy binding is set to re-evaluate policies. | ||||
| @ -94,6 +94,7 @@ Additionally, when the policy is executed from a flow, every variable from the f | ||||
|  | ||||
| This includes the following: | ||||
|  | ||||
| -   `context['flow_plan']`: The actual flow plan itself, can be used to inject stages. | ||||
| -   `context['prompt_data']`: Data which has been saved from a prompt stage or an external source. | ||||
| -   `context['application']`: The application the user is in the process of authorizing. | ||||
| -   `context['pending_user']`: The currently pending user, see [User](../user-group/user.md#object-attributes) | ||||
|  | ||||
| @ -102,7 +102,11 @@ module.exports = { | ||||
|             items: [ | ||||
|                 "flow/layouts", | ||||
|                 "flow/inspector", | ||||
|                 "flow/examples", | ||||
|                 { | ||||
|                     type: "category", | ||||
|                     label: "Examples", | ||||
|                     items: ["flow/examples/flows", "flow/examples/snippets"], | ||||
|                 }, | ||||
|                 { | ||||
|                     type: "category", | ||||
|                     label: "Executors", | ||||
|  | ||||
		Reference in New Issue
	
	Block a user
	 Jens L
					Jens L