Exemple #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()
Exemple #2
0
 def dispatch(self, request: HttpRequest) -> HttpResponse:
     tenant: Tenant = request.tenant
     flow = None
     # First, attempt to get default flow from tenant
     if self.designation == FlowDesignation.AUTHENTICATION:
         flow = tenant.flow_authentication
     if self.designation == FlowDesignation.INVALIDATION:
         flow = tenant.flow_invalidation
     # If no flow was set, get the first based on slug and policy
     if not flow:
         flow = Flow.with_policy(request, designation=self.designation)
     # If we still don't have a flow, 404
     if not flow:
         raise Http404
     # If user already has a pending plan, clear it so we don't have to later.
     if SESSION_KEY_PLAN in self.request.session:
         plan: FlowPlan = self.request.session[SESSION_KEY_PLAN]
         if plan.flow_pk != flow.pk.hex:
             LOGGER.warning(
                 "f(def): Found existing plan for other flow, deleting plan",
                 flow_slug=flow.slug,
             )
             del self.request.session[SESSION_KEY_PLAN]
     return redirect_with_qs("authentik_core:if-flow",
                             request.GET,
                             flow_slug=flow.slug)
Exemple #3
0
 def handle_login_flow(
     self, source: SAMLSource, *stages_to_append, **kwargs
 ) -> HttpResponse:
     """Prepare Authentication Plan, redirect user FlowExecutor"""
     # Ensure redirect is carried through when user was trying to
     # authorize application
     final_redirect = self.request.session.get(SESSION_KEY_GET, {}).get(
         NEXT_ARG_NAME, "authentik_core:if-admin"
     )
     kwargs.update(
         {
             PLAN_CONTEXT_SSO: True,
             PLAN_CONTEXT_SOURCE: source,
             PLAN_CONTEXT_REDIRECT: final_redirect,
         }
     )
     # We run the Flow planner here so we can pass the Pending user in the context
     planner = FlowPlanner(source.pre_authentication_flow)
     planner.allow_empty_flows = True
     plan = planner.plan(self.request, kwargs)
     for stage in stages_to_append:
         plan.append(stage)
     self.request.session[SESSION_KEY_PLAN] = plan
     return redirect_with_qs(
         "authentik_core:if-flow",
         self.request.GET,
         flow_slug=source.pre_authentication_flow.slug,
     )
Exemple #4
0
 def _handle_login_flow(self, flow: Flow, **kwargs) -> HttpResponse:
     """Prepare Authentication Plan, redirect user FlowExecutor"""
     # Ensure redirect is carried through when user was trying to
     # authorize application
     final_redirect = self.request.session.get(SESSION_KEY_GET, {}).get(
         NEXT_ARG_NAME, "authentik_core:if-admin")
     kwargs.update({
         # Since we authenticate the user by their token, they have no backend set
         PLAN_CONTEXT_AUTHENTICATION_BACKEND:
         "django.contrib.auth.backends.ModelBackend",
         PLAN_CONTEXT_SSO: True,
         PLAN_CONTEXT_SOURCE: self.source,
         PLAN_CONTEXT_REDIRECT: final_redirect,
     })
     if not flow:
         return HttpResponseBadRequest()
     # We run the Flow planner here so we can pass the Pending user in the context
     planner = FlowPlanner(flow)
     plan = planner.plan(self.request, kwargs)
     for stage in self.get_stages_to_append(flow):
         plan.append(stage)
     self.request.session[SESSION_KEY_PLAN] = plan
     return redirect_with_qs(
         "authentik_core:if-flow",
         self.request.GET,
         flow_slug=flow.slug,
     )
Exemple #5
0
 def get(self, request: HttpRequest, application_slug: str) -> HttpResponse:
     """Verify the SAML Request, and if valid initiate the FlowPlanner for the application"""
     # Call the method handler, which checks the SAML
     # Request and returns a HTTP Response on error
     method_response = self.check_saml_request()
     if method_response:
         return method_response
     # Regardless, we start the planner and return to it
     planner = FlowPlanner(self.provider.authorization_flow)
     planner.allow_empty_flows = True
     plan = planner.plan(
         request,
         {
             PLAN_CONTEXT_SSO: True,
             PLAN_CONTEXT_APPLICATION: self.application,
             PLAN_CONTEXT_CONSENT_HEADER:
             _("You're about to sign into %(application)s.") % {
                 "application": self.application.name
             },
             PLAN_CONTEXT_CONSENT_PERMISSIONS: [],
         },
     )
     plan.append(in_memory_stage(SAMLFlowFinalView))
     request.session[SESSION_KEY_PLAN] = plan
     return redirect_with_qs(
         "authentik_core:if-flow",
         request.GET,
         flow_slug=self.provider.authorization_flow.slug,
     )
Exemple #6
0
 def get(self, request: HttpRequest, *args, **kwargs) -> HttpResponse:
     """Start FlowPLanner, return to flow executor shell"""
     # After we've checked permissions, and the user has access, check if we need
     # to re-authenticate the user
     if self.params.max_age:
         current_age: timedelta = (
             timezone.now()
             - Event.objects.filter(
                 action=EventAction.LOGIN, user=get_user(self.request.user)
             )
             .latest("created")
             .created
         )
         if current_age.total_seconds() > self.params.max_age:
             return self.handle_no_permission()
     # If prompt=login, we need to re-authenticate the user regardless
     if (
         PROMPT_LOGIN in self.params.prompt
         and SESSION_NEEDS_LOGIN not in self.request.session
     ):
         self.request.session[SESSION_NEEDS_LOGIN] = True
         return self.handle_no_permission()
     # Regardless, we start the planner and return to it
     planner = FlowPlanner(self.provider.authorization_flow)
     # planner.use_cache = False
     planner.allow_empty_flows = True
     scope_descriptions = UserInfoView().get_scope_descriptions(self.params.scope)
     plan: FlowPlan = planner.plan(
         self.request,
         {
             PLAN_CONTEXT_SSO: True,
             PLAN_CONTEXT_APPLICATION: self.application,
             # OAuth2 related params
             PLAN_CONTEXT_PARAMS: self.params,
             # Consent related params
             PLAN_CONTEXT_CONSENT_HEADER: _(
                 "You're about to sign into %(application)s."
             )
             % {"application": self.application.name},
             PLAN_CONTEXT_CONSENT_PERMISSIONS: scope_descriptions,
         },
     )
     # OpenID clients can specify a `prompt` parameter, and if its set to consent we
     # need to inject a consent stage
     if PROMPT_CONSNET in self.params.prompt:
         if not any(isinstance(x, ConsentStageView) for x in plan.stages):
             # Plan does not have any consent stage, so we add an in-memory one
             stage = ConsentStage(
                 name="OAuth2 Provider In-memory consent stage",
                 mode=ConsentMode.ALWAYS_REQUIRE,
             )
             plan.append(stage)
     plan.append(in_memory_stage(OAuthFulfillmentStage))
     self.request.session[SESSION_KEY_PLAN] = plan
     return redirect_with_qs(
         "authentik_core:if-flow",
         self.request.GET,
         flow_slug=self.provider.authorization_flow.slug,
     )
Exemple #7
0
 def _flow_response(self, request: HttpRequest, flow: Flow,
                    **kwargs) -> HttpResponse:
     kwargs[PLAN_CONTEXT_SSO] = True
     kwargs[PLAN_CONTEXT_SOURCE] = self._source
     request.session[SESSION_KEY_PLAN] = FlowPlanner(flow).plan(
         request, kwargs)
     return redirect_with_qs(
         "authentik_core:if-flow",
         request.GET,
         flow_slug=flow.slug,
     )
Exemple #8
0
 def restart_flow(self, keep_context=False) -> HttpResponse:
     """Restart the currently active flow, optionally keeping the current context"""
     planner = FlowPlanner(self.flow)
     default_context = None
     if keep_context:
         default_context = self.plan.context
     plan = planner.plan(self.request, default_context)
     self.request.session[SESSION_KEY_PLAN] = plan
     kwargs = self.kwargs
     kwargs.update({"flow_slug": self.flow.slug})
     return redirect_with_qs("authentik_api:flow-executor",
                             self.request.GET, **kwargs)
Exemple #9
0
 def _flow_done(self) -> HttpResponse:
     """User Successfully passed all stages"""
     # Since this is wrapped by the ExecutorShell, the next argument is saved in the session
     # extract the next param before cancel as that cleans it
     next_param = None
     if self.plan:
         next_param = self.plan.context.get(PLAN_CONTEXT_REDIRECT)
     if not next_param:
         next_param = self.request.session.get(SESSION_KEY_GET, {}).get(
             NEXT_ARG_NAME, "authentik_core:root-redirect")
     self.cancel()
     return to_stage_response(self.request, redirect_with_qs(next_param))
Exemple #10
0
 def _flow_done(self) -> HttpResponse:
     """User Successfully passed all stages"""
     # Since this is wrapped by the ExecutorShell, the next argument is saved in the session
     # extract the next param before cancel as that cleans it
     if self.plan and PLAN_CONTEXT_REDIRECT in self.plan.context:
         # The context `redirect` variable can only be set by
         # an expression policy or authentik itself, so we don't
         # check if its an absolute URL or a relative one
         self.cancel()
         return redirect(self.plan.context.get(PLAN_CONTEXT_REDIRECT))
     next_param = self.request.session.get(SESSION_KEY_GET, {}).get(
         NEXT_ARG_NAME, "authentik_core:root-redirect")
     self.cancel()
     return to_stage_response(self.request, redirect_with_qs(next_param))
Exemple #11
0
 def dispatch(self, request: HttpRequest) -> HttpResponse:
     flow = Flow.with_policy(request, designation=self.designation)
     if not flow:
         raise Http404
     # If user already has a pending plan, clear it so we don't have to later.
     if SESSION_KEY_PLAN in self.request.session:
         plan: FlowPlan = self.request.session[SESSION_KEY_PLAN]
         if plan.flow_pk != flow.pk.hex:
             LOGGER.warning(
                 "f(def): Found existing plan for other flow, deleteing plan",
                 flow_slug=flow.slug,
             )
             del self.request.session[SESSION_KEY_PLAN]
     return redirect_with_qs("authentik_core:if-flow",
                             request.GET,
                             flow_slug=flow.slug)
Exemple #12
0
    def get(self, request: HttpRequest, stage_uuid: str) -> HttpResponse:
        """Initiate planner for selected change flow and redirect to flow executor,
        or raise Http404 if no configure_flow has been set."""
        try:
            stage: Stage = Stage.objects.get_subclass(pk=stage_uuid)
        except Stage.DoesNotExist as exc:
            raise Http404 from exc
        if not isinstance(stage, ConfigurableStage):
            LOGGER.debug("Stage does not inherit ConfigurableStage",
                         stage=stage)
            raise Http404
        if not stage.configure_flow:
            LOGGER.debug("Stage has no configure_flow set", stage=stage)
            raise Http404

        plan = FlowPlanner(stage.configure_flow).plan(
            request, {PLAN_CONTEXT_PENDING_USER: request.user})
        request.session[SESSION_KEY_PLAN] = plan
        return redirect_with_qs(
            "authentik_core:if-flow",
            self.request.GET,
            flow_slug=stage.configure_flow.slug,
        )