Exemplo n.º 1
0
 def execute(self, request: Request, slug: str):
     """Execute flow for current user"""
     # Because we pre-plan the flow here, and not in the planner, we need to manually clear
     # the history of the inspector
     request.session[SESSION_KEY_HISTORY] = []
     flow: Flow = self.get_object()
     planner = FlowPlanner(flow)
     planner.use_cache = False
     try:
         plan = planner.plan(self.request, {PLAN_CONTEXT_PENDING_USER: request.user})
         self.request.session[SESSION_KEY_PLAN] = plan
     except FlowNonApplicableException as exc:
         return bad_request_message(
             request,
             _(
                 "Flow not applicable to current user/request: %(messages)s"
                 % {"messages": str(exc)}
             ),
         )
     return Response(
         {
             "link": request._request.build_absolute_uri(
                 reverse("authentik_core:if-flow", kwargs={"flow_slug": flow.slug})
             )
         }
     )
Exemplo n.º 2
0
 def pre_permission_check(self):
     """Check prompt parameter before checking permission/authentication,
     see https://openid.net/specs/openid-connect-core-1_0.html#rfc.section.3.1.2.6"""
     try:
         self.params = OAuthAuthorizationParams.from_request(self.request)
     except AuthorizeError as error:
         error.to_event(redirect_uri=error.redirect_uri).from_http(self.request)
         raise RequestValidationError(HttpResponseRedirect(error.create_uri()))
     except OAuth2Error as error:
         error.to_event().from_http(self.request)
         raise RequestValidationError(
             bad_request_message(self.request, error.description, title=error.error)
         )
     except OAuth2Provider.DoesNotExist:
         raise Http404
     if PROMPT_NONE in self.params.prompt and not self.request.user.is_authenticated:
         # When "prompt" is set to "none" but the user is not logged in, show an error message
         error = AuthorizeError(
             self.params.redirect_uri,
             "login_required",
             self.params.grant_type,
             self.params.state,
         )
         error.to_event(redirect_uri=error.redirect_uri).from_http(self.request)
         raise RequestValidationError(HttpResponseRedirect(error.create_uri()))
Exemplo n.º 3
0
    def post(self, request: HttpRequest, source_slug: str) -> HttpResponse:
        """Handles a POSTed SSO Assertion and logs the user in."""
        source: SAMLSource = get_object_or_404(SAMLSource, slug=source_slug)
        if not source.enabled:
            raise Http404
        processor = ResponseProcessor(source)
        try:
            processor.parse(request)
        except MissingSAMLResponse as exc:
            return bad_request_message(request, str(exc))
        except VerificationError as exc:
            return bad_request_message(request, str(exc))

        try:
            return processor.prepare_flow(request)
        except UnsupportedNameIDFormat as exc:
            return bad_request_message(request, str(exc))
Exemplo n.º 4
0
    def check_saml_request(self) -> Optional[HttpRequest]:
        """Handle POST bindings"""
        if REQUEST_KEY_SAML_REQUEST not in self.request.POST:
            LOGGER.info("check_saml_request: SAML payload missing")
            return bad_request_message(self.request,
                                       "The SAML request payload is missing.")

        try:
            auth_n_request = AuthNRequestParser(self.provider).parse(
                self.request.POST[REQUEST_KEY_SAML_REQUEST],
                self.request.POST.get(REQUEST_KEY_RELAY_STATE),
            )
            self.request.session[SESSION_KEY_AUTH_N_REQUEST] = auth_n_request
        except CannotHandleAssertion as exc:
            LOGGER.info(str(exc))
            return bad_request_message(self.request, str(exc))
        return None
Exemplo n.º 5
0
 def get(self, request: HttpRequest, source_slug: str) -> HttpResponse:
     """Replies with an XHTML SSO Request."""
     source: SAMLSource = get_object_or_404(SAMLSource, slug=source_slug)
     if not source.enabled:
         raise Http404
     relay_state = request.GET.get("next", "")
     auth_n_req = RequestProcessor(source, request, relay_state)
     # If the source is configured for Redirect bindings, we can just redirect there
     if source.binding_type == SAMLBindingTypes.REDIRECT:
         # Parse the initial SSO URL
         sso_url = urlparse(source.sso_url)
         # Parse the querystring into a dict...
         url_kwargs = dict(parse_qsl(sso_url.query))
         # ... and update it with the SAML args
         url_kwargs.update(auth_n_req.build_auth_n_detached())
         # Encode it back into a string
         res = ParseResult(
             scheme=sso_url.scheme,
             netloc=sso_url.netloc,
             path=sso_url.path,
             params=sso_url.params,
             query=urlencode(url_kwargs),
             fragment=sso_url.fragment,
         )
         # and merge it back into a URL
         final_url = urlunparse(res)
         return redirect(final_url)
     # As POST Binding we show a form
     try:
         saml_request = nice64(auth_n_req.build_auth_n())
     except InternalError as exc:
         LOGGER.warning(str(exc))
         return bad_request_message(request, str(exc))
     injected_stages = []
     plan_kwargs = {
         PLAN_CONTEXT_TITLE:
         _("Redirecting to %(app)s..." % {"app": source.name}),
         PLAN_CONTEXT_CONSENT_TITLE:
         _("Redirecting to %(app)s..." % {"app": source.name}),
         PLAN_CONTEXT_ATTRS: {
             "SAMLRequest": saml_request,
             "RelayState": relay_state,
         },
         PLAN_CONTEXT_URL:
         source.sso_url,
     }
     # For just POST we add a consent stage,
     # otherwise we default to POST_AUTO, with direct redirect
     if source.binding_type == SAMLBindingTypes.POST:
         injected_stages.append(in_memory_stage(ConsentStageView))
         plan_kwargs[
             PLAN_CONTEXT_CONSENT_HEADER] = f"Continue to {source.name}"
     injected_stages.append(in_memory_stage(AutosubmitStageView))
     return self.handle_login_flow(
         source,
         *injected_stages,
         **plan_kwargs,
     )
Exemplo n.º 6
0
    def get(self, request: HttpRequest, *args, **kwargs) -> HttpResponse:
        application: Application = self.executor.plan.context[
            PLAN_CONTEXT_APPLICATION]
        provider: SAMLProvider = get_object_or_404(SAMLProvider,
                                                   pk=application.provider_id)
        if SESSION_KEY_AUTH_N_REQUEST not in self.request.session:
            return self.executor.stage_invalid()

        auth_n_request: AuthNRequest = self.request.session.pop(
            SESSION_KEY_AUTH_N_REQUEST)
        try:
            response = AssertionProcessor(provider, request,
                                          auth_n_request).build_response()
        except SAMLException as exc:
            Event.new(
                EventAction.CONFIGURATION_ERROR,
                message=f"Failed to process SAML assertion: {str(exc)}",
                provider=provider,
            ).from_http(self.request)
            return self.executor.stage_invalid()

        # Log Application Authorization
        Event.new(
            EventAction.AUTHORIZE_APPLICATION,
            authorized_application=application,
            flow=self.executor.plan.flow_pk,
        ).from_http(self.request)

        if provider.sp_binding == SAMLBindings.POST:
            form_attrs = {
                "ACSUrl": provider.acs_url,
                REQUEST_KEY_SAML_RESPONSE: nice64(response),
            }
            if auth_n_request.relay_state:
                form_attrs[
                    REQUEST_KEY_RELAY_STATE] = auth_n_request.relay_state
            return super().get(
                self.request,
                **{
                    "type": ChallengeTypes.NATIVE.value,
                    "component": "ak-stage-autosubmit",
                    "title": "Redirecting to %(app)s..." % {
                        "app": application.name
                    },
                    "url": provider.acs_url,
                    "attrs": form_attrs,
                },
            )
        if provider.sp_binding == SAMLBindings.REDIRECT:
            url_args = {
                REQUEST_KEY_SAML_RESPONSE: deflate_and_base64_encode(response),
            }
            if auth_n_request.relay_state:
                url_args[REQUEST_KEY_RELAY_STATE] = auth_n_request.relay_state
            querystring = urlencode(url_args)
            return redirect(f"{provider.acs_url}?{querystring}")
        return bad_request_message(request, "Invalid sp_binding specified")
Exemplo n.º 7
0
    def check_saml_request(self) -> Optional[HttpRequest]:
        """Handle POST bindings"""
        payload = self.request.POST
        # Restore the post body from the session
        # This happens when using POST bindings but the user isn't logged in
        # (user gets redirected and POST body is 'lost')
        if SESSION_KEY_POST in self.request.session:
            payload = self.request.session.pop(SESSION_KEY_POST)
        if REQUEST_KEY_SAML_REQUEST not in payload:
            LOGGER.info("check_saml_request: SAML payload missing")
            return bad_request_message(self.request,
                                       "The SAML request payload is missing.")

        try:
            auth_n_request = AuthNRequestParser(self.provider).parse(
                payload[REQUEST_KEY_SAML_REQUEST],
                payload.get(REQUEST_KEY_RELAY_STATE),
            )
            self.request.session[SESSION_KEY_AUTH_N_REQUEST] = auth_n_request
        except CannotHandleAssertion as exc:
            LOGGER.info(str(exc))
            return bad_request_message(self.request, str(exc))
        return None
Exemplo n.º 8
0
    def check_saml_request(self) -> Optional[HttpRequest]:
        """Handle REDIRECT bindings"""
        if REQUEST_KEY_SAML_REQUEST not in self.request.GET:
            LOGGER.info("handle_saml_request: SAML payload missing")
            return bad_request_message(self.request,
                                       "The SAML request payload is missing.")

        try:
            auth_n_request = AuthNRequestParser(self.provider).parse_detached(
                self.request.GET[REQUEST_KEY_SAML_REQUEST],
                self.request.GET.get(REQUEST_KEY_RELAY_STATE),
                self.request.GET.get(REQUEST_KEY_SAML_SIGNATURE),
                self.request.GET.get(REQUEST_KEY_SAML_SIG_ALG),
            )
            self.request.session[SESSION_KEY_AUTH_N_REQUEST] = auth_n_request
        except CannotHandleAssertion as exc:
            Event.new(
                EventAction.CONFIGURATION_ERROR,
                provider=self.provider,
                message=str(exc),
            ).save()
            LOGGER.info(str(exc))
            return bad_request_message(self.request, str(exc))
        return None
Exemplo n.º 9
0
 def get(self, request: HttpRequest, *args, **kwargs) -> HttpResponse:
     """final Stage of an OAuth2 Flow"""
     if PLAN_CONTEXT_PARAMS not in self.executor.plan.context:
         LOGGER.warning("Got to fulfillment stage with no pending context")
         return HttpResponseBadRequest()
     self.params: OAuthAuthorizationParams = self.executor.plan.context.pop(
         PLAN_CONTEXT_PARAMS)
     application: Application = self.executor.plan.context.pop(
         PLAN_CONTEXT_APPLICATION)
     self.provider = get_object_or_404(OAuth2Provider,
                                       pk=application.provider_id)
     try:
         # At this point we don't need to check permissions anymore
         if {PROMPT_NONE, PROMPT_CONSNET}.issubset(self.params.prompt):
             raise AuthorizeError(
                 self.params.redirect_uri,
                 "consent_required",
                 self.params.grant_type,
                 self.params.state,
             )
         Event.new(
             EventAction.AUTHORIZE_APPLICATION,
             authorized_application=application,
             flow=self.executor.plan.flow_pk,
             scopes=", ".join(self.params.scope),
         ).from_http(self.request)
         return self.redirect(self.create_response_uri())
     except (ClientIdError, RedirectUriError) as error:
         error.to_event(application=application).from_http(request)
         self.executor.stage_invalid()
         # pylint: disable=no-member
         return bad_request_message(request,
                                    error.description,
                                    title=error.error)
     except AuthorizeError as error:
         error.to_event(application=application).from_http(request)
         self.executor.stage_invalid()
         return self.redirect(error.create_uri())
Exemplo n.º 10
0
 def test_bad_request_message(self):
     """test bad_request_message"""
     request = self.factory.get("/")
     self.assertEqual(bad_request_message(request, "foo").status_code, 400)