diff --git a/docs/blueprints.rst b/docs/blueprints.rst index 9ede8d07..bb8857de 100644 --- a/docs/blueprints.rst +++ b/docs/blueprints.rst @@ -38,12 +38,12 @@ djangorestframework-api-key --------------------------- Since `djangorestframework-api-key `_ has -not entry in ``authentication_classes``, `drf-spectacular` does not pick up this library. To alleviate +no entry in ``authentication_classes``, `drf-spectacular` cannot pick up this library. To alleviate this shortcoming, you can manually add the appropriate security scheme. -.. note:: The usage of ``APPEND_COMPONENTS`` and ``SECURITY`` is discouraged, unless there are special - circumstances like here for example. They apply globally and additional local ``security`` sections - silently override the global ``security`` section. +.. note:: Usage of the ``SECURITY`` setting is discouraged, unless there are special circumstances + like here for example. For almost all cases ``OpenApiAuthenticationExtension`` is strongly preferred, + because ``SECURITY`` will get appended to every endpoint in the schema regardless of effectiveness. .. code:: python diff --git a/drf_spectacular/openapi.py b/drf_spectacular/openapi.py index c8c8a0cb..4ff43f98 100644 --- a/drf_spectacular/openapi.py +++ b/drf_spectacular/openapi.py @@ -224,6 +224,9 @@ def get_auth(self): ) self.registry.register_on_missing(component) + if spectacular_settings.SECURITY: + auths.extend(spectacular_settings.SECURITY) + perms = [p.__class__ for p in self.view.get_permissions()] if permissions.AllowAny in perms: auths.append({}) diff --git a/drf_spectacular/plumbing.py b/drf_spectacular/plumbing.py index 234ffeb7..0d670cc4 100644 --- a/drf_spectacular/plumbing.py +++ b/drf_spectacular/plumbing.py @@ -325,8 +325,6 @@ def build_root_object(paths, components): root['info']['license'] = settings.LICENSE if settings.SERVERS: root['servers'] = settings.SERVERS - if settings.SECURITY: - root['security'] = settings.SECURITY if settings.TAGS: root['tags'] = settings.TAGS if settings.EXTERNAL_DOCS: diff --git a/drf_spectacular/settings.py b/drf_spectacular/settings.py index 71e6f392..2a35cc63 100644 --- a/drf_spectacular/settings.py +++ b/drf_spectacular/settings.py @@ -38,12 +38,13 @@ # Append OpenAPI objects to path and components in addition to the generated objects 'APPEND_PATHS': {}, + 'APPEND_COMPONENTS': {}, - # DEPRECATED - please don't use this anymore as it has tricky implications that + # DISCOURAGED - please don't use this anymore as it has tricky implications that # are hard to get right. For authentication, OpenApiAuthenticationExtension are # strongly preferred because they are more robust and easy to write. - 'APPEND_COMPONENTS': {}, - 'SECURITY': None, + # However if used, the list of methods is appended to every endpoint in the schema! + 'SECURITY': [], # Postprocessing functions that run at the end of schema generation. # must satisfy interface result = hook(generator, request, public, result) diff --git a/tests/test_regressions.py b/tests/test_regressions.py index 13875de3..937c1a18 100644 --- a/tests/test_regressions.py +++ b/tests/test_regressions.py @@ -1007,3 +1007,24 @@ class XViewset(viewsets.ReadOnlyModelViewSet): substitution = schema['components']['schemas']['PaginatedXList'] assert substitution['type'] == 'object' assert substitution['properties']['results']['items']['$ref'] == '#/components/schemas/X' + + +@mock.patch( + 'drf_spectacular.settings.spectacular_settings.SECURITY', + [{'apiKeyAuth': []}] +) +@mock.patch( + 'drf_spectacular.settings.spectacular_settings.APPEND_COMPONENTS', + {"securitySchemes": {"apiKeyAuth": {"type": "apiKey", "in": "header", "name": "Authorization"}}} +) +def test_manual_security_method_addition(no_warnings): + @extend_schema(responses=OpenApiTypes.FLOAT) + @api_view(['GET']) + def view_func(request, format=None): + pass # pragma: no cover + + schema = generate_schema('/x/', view_function=view_func) + operation_security = schema['paths']['/x/']['get']['security'] + schema_security = schema['components']['securitySchemes'] + assert len(operation_security) == 4 and any(['apiKeyAuth' in os for os in operation_security]) + assert len(schema_security) == 3 and 'apiKeyAuth' in schema_security