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