Beispiel #1
0
 def stage_ok(self) -> HttpResponse:
     """Callback called by stages upon successful completion.
     Persists updated plan and context to session."""
     self._logger.debug(
         "f(exec): Stage ok",
         stage_class=class_to_path(self.current_stage_view.__class__),
     )
     self.request.session.get(SESSION_KEY_HISTORY,
                              []).append(deepcopy(self.plan))
     self.plan.pop()
     self.request.session[SESSION_KEY_PLAN] = self.plan
     if self.plan.bindings:
         self._logger.debug(
             "f(exec): Continuing with next stage",
             remaining=len(self.plan.bindings),
         )
         kwargs = self.kwargs
         kwargs.update({"flow_slug": self.flow.slug})
         return redirect_with_qs("authentik_api:flow-executor",
                                 self.request.GET, **kwargs)
     # User passed all stages
     self._logger.debug(
         "f(exec): User passed all stages",
         context=cleanse_dict(self.plan.context),
     )
     return self._flow_done()
Beispiel #2
0
    def plan(
        self, request: HttpRequest, default_context: Optional[dict[str, Any]] = None
    ) -> FlowPlan:
        """Check each of the flows' policies, check policies for each stage with PolicyBinding
        and return ordered list"""
        with Hub.current.start_span(
            op="authentik.flow.planner.plan", description=self.flow.slug
        ) as span:
            span: Span
            span.set_data("flow", self.flow)
            span.set_data("request", request)

            self._logger.debug(
                "f(plan): starting planning process",
            )
            # Bit of a workaround here, if there is a pending user set in the default context
            # we use that user for our cache key
            # to make sure they don't get the generic response
            if default_context and PLAN_CONTEXT_PENDING_USER in default_context:
                user = default_context[PLAN_CONTEXT_PENDING_USER]
            else:
                user = request.user
            # First off, check the flow's direct policy bindings
            # to make sure the user even has access to the flow
            engine = PolicyEngine(self.flow, user, request)
            if default_context:
                span.set_data("default_context", cleanse_dict(default_context))
                engine.request.context = default_context
            engine.build()
            result = engine.result
            if not result.passing:
                exc = FlowNonApplicableException(",".join(result.messages))
                exc.policy_result = result
                raise exc
            # User is passing so far, check if we have a cached plan
            cached_plan_key = cache_key(self.flow, user)
            cached_plan = cache.get(cached_plan_key, None)
            if cached_plan and self.use_cache:
                self._logger.debug(
                    "f(plan): taking plan from cache",
                    key=cached_plan_key,
                )
                # Reset the context as this isn't factored into caching
                cached_plan.context = default_context or {}
                return cached_plan
            self._logger.debug(
                "f(plan): building plan",
            )
            plan = self._build_plan(user, request, default_context)
            cache.set(cache_key(self.flow, user), plan, CACHE_TIMEOUT)
            if not plan.bindings and not self.allow_empty_flows:
                raise EmptyFlowException()
            return plan