authentik.api.v3.schema.response

  1from typing import Any
  2
  3from django.utils.translation import gettext_lazy as _
  4from drf_spectacular.generators import SchemaGenerator
  5from drf_spectacular.plumbing import (
  6    ResolvedComponent,
  7    build_array_type,
  8    build_basic_type,
  9    build_object_type,
 10)
 11from drf_spectacular.settings import spectacular_settings
 12from drf_spectacular.types import OpenApiTypes
 13from rest_framework.settings import api_settings
 14from structlog.stdlib import get_logger
 15
 16from authentik.api.v3.schema.pagination import PAGINATION
 17from authentik.api.v3.schema.query import QUERY_PARAMS
 18
 19LOGGER = get_logger()
 20
 21GENERIC_ERROR = ResolvedComponent(
 22    name="GenericError",
 23    type=ResolvedComponent.SCHEMA,
 24    object="GenericError",
 25    schema=build_object_type(
 26        description=_("Generic API Error"),
 27        properties={
 28            "detail": build_basic_type(OpenApiTypes.STR),
 29            "code": build_basic_type(OpenApiTypes.STR),
 30        },
 31        required=["detail"],
 32    ),
 33)
 34GENERIC_ERROR_RESPONSE = ResolvedComponent(
 35    name="GenericErrorResponse",
 36    type=ResolvedComponent.RESPONSE,
 37    object="GenericErrorResponse",
 38    schema={
 39        "content": {"application/json": {"schema": GENERIC_ERROR.ref}},
 40        "description": "",
 41    },
 42)
 43VALIDATION_ERROR = ResolvedComponent(
 44    "ValidationError",
 45    object="ValidationError",
 46    type=ResolvedComponent.SCHEMA,
 47    schema=build_object_type(
 48        description=_("Validation Error"),
 49        properties={
 50            api_settings.NON_FIELD_ERRORS_KEY: build_array_type(build_basic_type(OpenApiTypes.STR)),
 51            "code": build_basic_type(OpenApiTypes.STR),
 52        },
 53        required=[],
 54        additionalProperties={},
 55    ),
 56)
 57VALIDATION_ERROR_RESPONSE = ResolvedComponent(
 58    name="ValidationErrorResponse",
 59    type=ResolvedComponent.RESPONSE,
 60    object="ValidationErrorResponse",
 61    schema={
 62        "content": {
 63            "application/json": {
 64                "schema": VALIDATION_ERROR.ref,
 65            }
 66        },
 67        "description": "",
 68    },
 69)
 70
 71
 72def postprocess_schema_register(
 73    result: dict[str, Any], generator: SchemaGenerator, **kwargs
 74) -> dict[str, Any]:
 75    """Register custom schema components"""
 76    LOGGER.debug("Registering custom schemas")
 77    generator.registry.register_on_missing(PAGINATION)
 78    generator.registry.register_on_missing(GENERIC_ERROR)
 79    generator.registry.register_on_missing(GENERIC_ERROR_RESPONSE)
 80    generator.registry.register_on_missing(VALIDATION_ERROR)
 81    generator.registry.register_on_missing(VALIDATION_ERROR_RESPONSE)
 82    for query in QUERY_PARAMS.values():
 83        generator.registry.register_on_missing(query)
 84    return result
 85
 86
 87def postprocess_schema_responses(
 88    result: dict[str, Any], generator: SchemaGenerator, **kwargs
 89) -> dict[str, Any]:
 90    """Default error responses"""
 91    LOGGER.debug("Adding default error responses")
 92    for path in result["paths"].values():
 93        for method in path.values():
 94            method["responses"].setdefault("400", VALIDATION_ERROR_RESPONSE.ref)
 95            method["responses"].setdefault("403", GENERIC_ERROR_RESPONSE.ref)
 96
 97    result["components"] = generator.registry.build(spectacular_settings.APPEND_COMPONENTS)
 98
 99    # This is a workaround for authentik/stages/prompt/stage.py
100    # since the serializer PromptChallengeResponse
101    # accepts dynamic keys
102    for component in result["components"]["schemas"]:
103        if component == "PromptChallengeResponseRequest":
104            comp = result["components"]["schemas"][component]
105            comp["additionalProperties"] = {}
106    return result
LOGGER = <BoundLoggerLazyProxy(logger=None, wrapper_class=None, processors=None, context_class=None, initial_values={}, logger_factory_args=())>
GENERIC_ERROR = <drf_spectacular.plumbing.ResolvedComponent object>
GENERIC_ERROR_RESPONSE = <drf_spectacular.plumbing.ResolvedComponent object>
VALIDATION_ERROR = <drf_spectacular.plumbing.ResolvedComponent object>
VALIDATION_ERROR_RESPONSE = <drf_spectacular.plumbing.ResolvedComponent object>
def postprocess_schema_register( result: dict[str, typing.Any], generator: drf_spectacular.generators.SchemaGenerator, **kwargs) -> dict[str, typing.Any]:
73def postprocess_schema_register(
74    result: dict[str, Any], generator: SchemaGenerator, **kwargs
75) -> dict[str, Any]:
76    """Register custom schema components"""
77    LOGGER.debug("Registering custom schemas")
78    generator.registry.register_on_missing(PAGINATION)
79    generator.registry.register_on_missing(GENERIC_ERROR)
80    generator.registry.register_on_missing(GENERIC_ERROR_RESPONSE)
81    generator.registry.register_on_missing(VALIDATION_ERROR)
82    generator.registry.register_on_missing(VALIDATION_ERROR_RESPONSE)
83    for query in QUERY_PARAMS.values():
84        generator.registry.register_on_missing(query)
85    return result

Register custom schema components

def postprocess_schema_responses( result: dict[str, typing.Any], generator: drf_spectacular.generators.SchemaGenerator, **kwargs) -> dict[str, typing.Any]:
 88def postprocess_schema_responses(
 89    result: dict[str, Any], generator: SchemaGenerator, **kwargs
 90) -> dict[str, Any]:
 91    """Default error responses"""
 92    LOGGER.debug("Adding default error responses")
 93    for path in result["paths"].values():
 94        for method in path.values():
 95            method["responses"].setdefault("400", VALIDATION_ERROR_RESPONSE.ref)
 96            method["responses"].setdefault("403", GENERIC_ERROR_RESPONSE.ref)
 97
 98    result["components"] = generator.registry.build(spectacular_settings.APPEND_COMPONENTS)
 99
100    # This is a workaround for authentik/stages/prompt/stage.py
101    # since the serializer PromptChallengeResponse
102    # accepts dynamic keys
103    for component in result["components"]["schemas"]:
104        if component == "PromptChallengeResponseRequest":
105            comp = result["components"]["schemas"][component]
106            comp["additionalProperties"] = {}
107    return result

Default error responses