diff --git a/drf_spectacular/contrib/__init__.py b/drf_spectacular/contrib/__init__.py index 90e8b8f6..4da50f0a 100644 --- a/drf_spectacular/contrib/__init__.py +++ b/drf_spectacular/contrib/__init__.py @@ -11,4 +11,5 @@ 'rest_framework_recursive', 'rest_framework_gis', 'pydantic', + 'knox_auth_token', ] diff --git a/drf_spectacular/contrib/knox_auth_token.py b/drf_spectacular/contrib/knox_auth_token.py new file mode 100644 index 00000000..16f11d70 --- /dev/null +++ b/drf_spectacular/contrib/knox_auth_token.py @@ -0,0 +1,13 @@ +from drf_spectacular.extensions import OpenApiAuthenticationExtension +from drf_spectacular.plumbing import build_bearer_security_scheme_object + + +class KnoxTokenScheme(OpenApiAuthenticationExtension): + target_class = 'knox.auth.TokenAuthentication' + name = 'knoxApiToken' + + def get_security_definition(self, auto_schema): + return build_bearer_security_scheme_object( + header_name='Authorization', + token_prefix=self.target.authenticate_header(""), + ) diff --git a/requirements/optionals.txt b/requirements/optionals.txt index e6cbc321..96dc77ad 100644 --- a/requirements/optionals.txt +++ b/requirements/optionals.txt @@ -15,3 +15,4 @@ drf-spectacular-sidecar djangorestframework-dataclasses>=1.0.0; python_version >= '3.7' djangorestframework-gis>=1.0.0 pydantic>=2,<3; python_version >= '3.7' +django-rest-knox>=4.1 diff --git a/tests/conftest.py b/tests/conftest.py index 3a919bf0..6218127c 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -24,6 +24,7 @@ def pytest_configure(config): 'allauth.account', 'oauth2_provider', 'django_filters', + 'knox', # this is not strictly required and when added django-polymorphic # currently breaks the whole Django/DRF upstream testing. # 'polymorphic', diff --git a/tests/contrib/test_knox_auth_token.py b/tests/contrib/test_knox_auth_token.py new file mode 100644 index 00000000..6cb80778 --- /dev/null +++ b/tests/contrib/test_knox_auth_token.py @@ -0,0 +1,49 @@ +from unittest import mock + +import pytest +from rest_framework import mixins, routers, serializers, viewsets + +from tests import assert_schema, generate_schema + +try: + from knox.auth import TokenAuthentication +except ImportError: + TokenAuthentication = None + + +class XSerializer(serializers.Serializer): + uuid = serializers.UUIDField() + + +class XViewset(mixins.ListModelMixin, viewsets.GenericViewSet): + serializer_class = XSerializer + authentication_classes = [TokenAuthentication] + required_scopes = ['x:read', 'x:write'] + + +@pytest.mark.contrib('knox_auth_token') +def test_knox_auth_token(no_warnings): + router = routers.SimpleRouter() + router.register('x', XViewset, basename="x") + + urlpatterns = [ + *router.urls + ] + + schema = generate_schema(None, patterns=urlpatterns) + + assert_schema(schema, 'tests/contrib/test_knox_auth_token.yml') + + +@pytest.mark.contrib('knox_auth_token') +@mock.patch('knox.settings.knox_settings.AUTH_HEADER_PREFIX', 'CustomPrefix') +def test_knox_auth_token_non_default_prefix(no_warnings): + schema = generate_schema('/x', XViewset) + assert schema['components']['securitySchemes'] == { + 'knoxApiToken': { + 'type': 'apiKey', + 'in': 'header', + 'name': 'Authorization', + 'description': 'Token-based authentication with required prefix "CustomPrefix"' + }, + } diff --git a/tests/contrib/test_knox_auth_token.yml b/tests/contrib/test_knox_auth_token.yml new file mode 100644 index 00000000..2ca61fbd --- /dev/null +++ b/tests/contrib/test_knox_auth_token.yml @@ -0,0 +1,38 @@ +openapi: 3.0.3 +info: + title: '' + version: 0.0.0 +paths: + /x/: + get: + operationId: x_list + tags: + - x + security: + - knoxApiToken: [] + - {} + responses: + '200': + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/X' + description: '' +components: + schemas: + X: + type: object + properties: + uuid: + type: string + format: uuid + required: + - uuid + securitySchemes: + knoxApiToken: + type: apiKey + in: header + name: Authorization + description: Token-based authentication with required prefix "Token" diff --git a/tox.ini b/tox.ini index 63ef4db0..58c4272c 100644 --- a/tox.ini +++ b/tox.ini @@ -93,6 +93,7 @@ known_third_party = polymorphic oauth2_provider djstripe + knox multi_line_output = 5 use_parentheses = true include_trailing_comma = true