diff --git a/authentik/api/v3/config.py b/authentik/api/v3/config.py index 7207b2abb..93daa6dd2 100644 --- a/authentik/api/v3/config.py +++ b/authentik/api/v3/config.py @@ -28,6 +28,7 @@ class Capabilities(models.TextChoices): CAN_SAVE_MEDIA = "can_save_media" CAN_GEO_IP = "can_geo_ip" CAN_IMPERSONATE = "can_impersonate" + CAN_DEBUG = "can_debug" class ErrorReportingConfigSerializer(PassiveSerializer): @@ -66,6 +67,8 @@ class ConfigView(APIView): caps.append(Capabilities.CAN_GEO_IP) if CONFIG.y_bool("impersonation"): caps.append(Capabilities.CAN_IMPERSONATE) + if settings.DEBUG: + caps.append(Capabilities.CAN_DEBUG) return caps def get_config(self) -> ConfigSerializer: diff --git a/authentik/flows/views/inspector.py b/authentik/flows/views/inspector.py index 6d3fa32a1..edf40a23d 100644 --- a/authentik/flows/views/inspector.py +++ b/authentik/flows/views/inspector.py @@ -73,17 +73,17 @@ class FlowInspectorView(APIView): flow: Flow _logger: BoundLogger + def check_permissions(self, request): + """Always allow access when in debug mode""" + if settings.DEBUG: + return None + return super().check_permissions(request) + def setup(self, request: HttpRequest, flow_slug: str): super().setup(request, flow_slug=flow_slug) self.flow = get_object_or_404(Flow.objects.select_related(), slug=flow_slug) self._logger = get_logger().bind(flow_slug=flow_slug) - # pylint: disable=unused-argument, too-many-return-statements - def dispatch(self, request: HttpRequest, flow_slug: str) -> HttpResponse: - if SESSION_KEY_HISTORY not in self.request.session: - return HttpResponse(status=400) - return super().dispatch(request, flow_slug=flow_slug) - @extend_schema( responses={ 200: FlowInspectionSerializer(), @@ -95,7 +95,7 @@ class FlowInspectorView(APIView): def get(self, request: HttpRequest, *args, **kwargs) -> HttpResponse: """Get current flow state and record it""" plans = [] - for plan in request.session[SESSION_KEY_HISTORY]: + for plan in request.session.get(SESSION_KEY_HISTORY, []): plan_serializer = FlowInspectorPlanSerializer( instance=plan, context={"request": request} ) diff --git a/schema.yml b/schema.yml index 1fe621123..89cebbc67 100644 --- a/schema.yml +++ b/schema.yml @@ -25809,6 +25809,7 @@ components: - can_save_media - can_geo_ip - can_impersonate + - can_debug type: string CaptchaChallenge: type: object diff --git a/web/src/flow/FlowExecutor.ts b/web/src/flow/FlowExecutor.ts index e75dfe7ce..d02f6a068 100644 --- a/web/src/flow/FlowExecutor.ts +++ b/web/src/flow/FlowExecutor.ts @@ -36,6 +36,7 @@ import PFTitle from "@patternfly/patternfly/components/Title/title.css"; import PFBase from "@patternfly/patternfly/patternfly-base.css"; import { + CapabilitiesEnum, ChallengeChoices, ChallengeTypes, CurrentTenant, @@ -154,7 +155,11 @@ export class FlowExecutor extends AKElement implements StageHost { super(); this.ws = new WebsocketClient(); this.flowSlug = window.location.pathname.split("/")[3]; - this.inspectorOpen = window.location.search.includes("inspector"); + this.inspectorOpen = + globalAK()?.config.capabilities.includes(CapabilitiesEnum.Debug) || false; + if (window.location.search.includes("inspector")) { + this.inspectorOpen = !this.inspectorOpen; + } tenant().then((tenant) => (this.tenant = tenant)); } diff --git a/website/docs/flow/inspector.md b/website/docs/flow/inspector.md index 79c58f4fc..6715c32fc 100644 --- a/website/docs/flow/inspector.md +++ b/website/docs/flow/inspector.md @@ -31,3 +31,7 @@ This data is not cleaned, so if your flow involves inputting a password, it will ## Session ID The unique ID for the currently used session. This can be used to debug issues with flows restarting/losing state. + +# Access to the inspector + +By default, the inspector can only be enabled when the currently authenticated user is a superuser. When running authentik with debug-mode enabled, the inspector is enabled by default and can be accessed by both unauthenticated users and standard users.