fi(api): improve auth/preferences schema generation
Applying recommendation from https://github.com/tfranzel/drf-spectacular/issues/874
This commit is contained in:
parent
9bfc83f0e4
commit
8f65318d32
|
@ -1,35 +1,97 @@
|
||||||
import os
|
import os
|
||||||
|
|
||||||
from drf_spectacular.contrib.django_oauth_toolkit import OpenApiAuthenticationExtension
|
from drf_spectacular.contrib.django_oauth_toolkit import (
|
||||||
from drf_spectacular.plumbing import build_bearer_security_scheme_object
|
DjangoOAuthToolkitScheme,
|
||||||
|
OpenApiAuthenticationExtension,
|
||||||
|
)
|
||||||
|
from drf_spectacular.extensions import (
|
||||||
|
OpenApiSerializerExtension,
|
||||||
|
OpenApiSerializerFieldExtension,
|
||||||
|
)
|
||||||
|
from drf_spectacular.plumbing import (
|
||||||
|
OpenApiTypes,
|
||||||
|
build_basic_type,
|
||||||
|
build_bearer_security_scheme_object,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class CustomOAuthExt(OpenApiAuthenticationExtension):
|
class CustomRelatedFieldScheme(OpenApiSerializerFieldExtension):
|
||||||
|
target_class = "funkwhale_api.common.serializers.RelatedField"
|
||||||
|
match_subclasses = True
|
||||||
|
|
||||||
|
def map_serializer_field(self, auto_schema, direction):
|
||||||
|
if direction == "request":
|
||||||
|
return build_basic_type(OpenApiTypes.UUID)
|
||||||
|
elif direction == "response" and self.target.serializer:
|
||||||
|
component = auto_schema.resolve_serializer(
|
||||||
|
self.target.serializer, direction
|
||||||
|
)
|
||||||
|
return component.ref
|
||||||
|
else:
|
||||||
|
# happens for
|
||||||
|
# UserViewSet: UserWriteSerializer: not sure how this works for: avatar
|
||||||
|
# AlbumViewSet: AlbumCreateSerializer: not sure how this works for: artist
|
||||||
|
return build_basic_type(OpenApiTypes.UUID)
|
||||||
|
|
||||||
|
|
||||||
|
class PreferenceSerializerScheme(OpenApiSerializerExtension):
|
||||||
|
target_class = "dynamic_preferences.api.serializers.PreferenceSerializer"
|
||||||
|
match_subclasses = True
|
||||||
|
|
||||||
|
def map_serializer(self, auto_schema, direction):
|
||||||
|
from dynamic_preferences.api.serializers import PreferenceSerializer
|
||||||
|
|
||||||
|
class Fix(PreferenceSerializer):
|
||||||
|
def get_default(self, o) -> str:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def get_verbose_name(self, o) -> str:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def get_identifier(self, o) -> str:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def get_help_text(self, o) -> str:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def get_additional_data(self, o) -> dict:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def get_field(self, o) -> dict:
|
||||||
|
pass
|
||||||
|
|
||||||
|
return auto_schema._map_serializer(Fix, direction, bypass_extensions=True)
|
||||||
|
|
||||||
|
|
||||||
|
class CustomOAuthScheme(DjangoOAuthToolkitScheme):
|
||||||
target_class = "funkwhale_api.common.authentication.OAuth2Authentication"
|
target_class = "funkwhale_api.common.authentication.OAuth2Authentication"
|
||||||
name = "oauth2"
|
|
||||||
|
|
||||||
def get_security_definition(self, auto_schema):
|
def get_security_requirement(self, auto_schema):
|
||||||
from drf_spectacular.settings import spectacular_settings
|
from funkwhale_api.users.oauth.permissions import (
|
||||||
from oauth2_provider.scopes import get_scopes_backend
|
METHOD_SCOPE_MAPPING,
|
||||||
|
ScopePermission,
|
||||||
|
)
|
||||||
|
|
||||||
flows = {}
|
for permission in auto_schema.view.get_permissions():
|
||||||
for flow_type in spectacular_settings.OAUTH2_FLOWS:
|
if isinstance(permission, ScopePermission):
|
||||||
flows[flow_type] = {}
|
scope_config = getattr(auto_schema.view, "required_scope", "noopscope")
|
||||||
if flow_type in ("implicit", "authorizationCode"):
|
|
||||||
flows[flow_type][
|
|
||||||
"authorizationUrl"
|
|
||||||
] = spectacular_settings.OAUTH2_AUTHORIZATION_URL
|
|
||||||
if flow_type in ("password", "clientCredentials", "authorizationCode"):
|
|
||||||
flows[flow_type]["tokenUrl"] = spectacular_settings.OAUTH2_TOKEN_URL
|
|
||||||
if spectacular_settings.OAUTH2_REFRESH_URL:
|
|
||||||
flows[flow_type]["refreshUrl"] = spectacular_settings.OAUTH2_REFRESH_URL
|
|
||||||
scope_backend = get_scopes_backend()
|
|
||||||
flows[flow_type]["scopes"] = scope_backend.get_all_scopes()
|
|
||||||
|
|
||||||
return {"type": "oauth2", "flows": flows}
|
if isinstance(scope_config, str):
|
||||||
|
scope_config = {
|
||||||
|
"read": f"read:{scope_config}",
|
||||||
|
"write": f"write:{scope_config}",
|
||||||
|
}
|
||||||
|
action = METHOD_SCOPE_MAPPING[
|
||||||
|
auto_schema.view.request.method.lower()
|
||||||
|
]
|
||||||
|
required_scope = scope_config[action]
|
||||||
|
else:
|
||||||
|
required_scope = scope_config[auto_schema.view.action]
|
||||||
|
|
||||||
|
return {self.name: [required_scope]}
|
||||||
|
|
||||||
|
|
||||||
class CustomApplicationTokenExt(OpenApiAuthenticationExtension):
|
class CustomApplicationTokenScheme(OpenApiAuthenticationExtension):
|
||||||
target_class = "funkwhale_api.common.authentication.ApplicationTokenAuthentication"
|
target_class = "funkwhale_api.common.authentication.ApplicationTokenAuthentication"
|
||||||
name = "ApplicationToken"
|
name = "ApplicationToken"
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue