From 9ad10863de6e421642f30e120b2d8cfcf6e1e93f Mon Sep 17 00:00:00 2001 From: Jens Langhammer Date: Thu, 18 Mar 2021 15:59:38 +0100 Subject: [PATCH] providers/oauth2: add API for auth codes and refresh tokens Signed-off-by: Jens Langhammer --- authentik/api/v2/urls.py | 10 +- authentik/providers/oauth2/api/__init__.py | 0 .../oauth2/{api.py => api/provider.py} | 29 +- authentik/providers/oauth2/api/scope.py | 30 ++ authentik/providers/oauth2/api/tokens.py | 64 +++ authentik/providers/oauth2/models.py | 4 +- swagger.yaml | 464 +++++++++++++----- 7 files changed, 456 insertions(+), 145 deletions(-) create mode 100644 authentik/providers/oauth2/api/__init__.py rename authentik/providers/oauth2/{api.py => api/provider.py} (82%) create mode 100644 authentik/providers/oauth2/api/scope.py create mode 100644 authentik/providers/oauth2/api/tokens.py diff --git a/authentik/api/v2/urls.py b/authentik/api/v2/urls.py index 180f47969..2548064e2 100644 --- a/authentik/api/v2/urls.py +++ b/authentik/api/v2/urls.py @@ -46,7 +46,12 @@ from authentik.policies.reputation.api import ( ReputationPolicyViewSet, UserReputationViewSet, ) -from authentik.providers.oauth2.api import OAuth2ProviderViewSet, ScopeMappingViewSet +from authentik.providers.oauth2.api.provider import OAuth2ProviderViewSet +from authentik.providers.oauth2.api.scope import ScopeMappingViewSet +from authentik.providers.oauth2.api.tokens import ( + AuthorizationCodeViewSet, + RefreshTokenViewSet, +) from authentik.providers.proxy.api import ( ProxyOutpostConfigViewSet, ProxyProviderViewSet, @@ -141,6 +146,9 @@ router.register("providers/proxy", ProxyProviderViewSet) router.register("providers/oauth2", OAuth2ProviderViewSet) router.register("providers/saml", SAMLProviderViewSet) +router.register("oauth2/authorization_codes", AuthorizationCodeViewSet) +router.register("oauth2/refresh_tokens", RefreshTokenViewSet) + router.register("propertymappings/all", PropertyMappingViewSet) router.register("propertymappings/ldap", LDAPPropertyMappingViewSet) router.register("propertymappings/saml", SAMLPropertyMappingViewSet) diff --git a/authentik/providers/oauth2/api/__init__.py b/authentik/providers/oauth2/api/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/authentik/providers/oauth2/api.py b/authentik/providers/oauth2/api/provider.py similarity index 82% rename from authentik/providers/oauth2/api.py rename to authentik/providers/oauth2/api/provider.py index 84982d475..8a5a2e271 100644 --- a/authentik/providers/oauth2/api.py +++ b/authentik/providers/oauth2/api/provider.py @@ -6,13 +6,12 @@ from rest_framework.fields import ReadOnlyField from rest_framework.generics import get_object_or_404 from rest_framework.request import Request from rest_framework.response import Response -from rest_framework.serializers import ModelSerializer, Serializer +from rest_framework.serializers import Serializer from rest_framework.viewsets import ModelViewSet from authentik.core.api.providers import ProviderSerializer -from authentik.core.api.utils import MetaNameSerializer from authentik.core.models import Provider -from authentik.providers.oauth2.models import OAuth2Provider, ScopeMapping +from authentik.providers.oauth2.models import OAuth2Provider class OAuth2ProviderSerializer(ProviderSerializer): @@ -102,27 +101,3 @@ class OAuth2ProviderViewSet(ModelViewSet): except Provider.application.RelatedObjectDoesNotExist: # pylint: disable=no-member pass return Response(data) - - -class ScopeMappingSerializer(ModelSerializer, MetaNameSerializer): - """ScopeMapping Serializer""" - - class Meta: - - model = ScopeMapping - fields = [ - "pk", - "name", - "scope_name", - "description", - "expression", - "verbose_name", - "verbose_name_plural", - ] - - -class ScopeMappingViewSet(ModelViewSet): - """ScopeMapping Viewset""" - - queryset = ScopeMapping.objects.all() - serializer_class = ScopeMappingSerializer diff --git a/authentik/providers/oauth2/api/scope.py b/authentik/providers/oauth2/api/scope.py new file mode 100644 index 000000000..3c4d6a077 --- /dev/null +++ b/authentik/providers/oauth2/api/scope.py @@ -0,0 +1,30 @@ +"""OAuth2Provider API Views""" +from rest_framework.serializers import ModelSerializer +from rest_framework.viewsets import ModelViewSet + +from authentik.core.api.utils import MetaNameSerializer +from authentik.providers.oauth2.models import ScopeMapping + + +class ScopeMappingSerializer(ModelSerializer, MetaNameSerializer): + """ScopeMapping Serializer""" + + class Meta: + + model = ScopeMapping + fields = [ + "pk", + "name", + "scope_name", + "description", + "expression", + "verbose_name", + "verbose_name_plural", + ] + + +class ScopeMappingViewSet(ModelViewSet): + """ScopeMapping Viewset""" + + queryset = ScopeMapping.objects.all() + serializer_class = ScopeMappingSerializer diff --git a/authentik/providers/oauth2/api/tokens.py b/authentik/providers/oauth2/api/tokens.py new file mode 100644 index 000000000..8f9ebc4ba --- /dev/null +++ b/authentik/providers/oauth2/api/tokens.py @@ -0,0 +1,64 @@ +"""OAuth2Provider API Views""" +from rest_framework import mixins +from rest_framework.serializers import ModelSerializer +from rest_framework.viewsets import GenericViewSet + +from authentik.core.api.users import UserSerializer +from authentik.core.api.utils import MetaNameSerializer +from authentik.providers.oauth2.api.provider import OAuth2ProviderSerializer +from authentik.providers.oauth2.models import AuthorizationCode, RefreshToken + + +class ExpiringBaseGrantModelSerializer(ModelSerializer, MetaNameSerializer): + """Serializer for BaseGrantModel and ExpiringBaseGrant""" + + user = UserSerializer() + provider = OAuth2ProviderSerializer() + + class Meta: + + model = AuthorizationCode + fields = ["pk", "provider", "user", "is_expired", "expires", "scope"] + depth = 2 + + +class AuthorizationCodeViewSet( + mixins.RetrieveModelMixin, + mixins.DestroyModelMixin, + mixins.ListModelMixin, + GenericViewSet, +): + """AuthorizationCode Viewset""" + + queryset = AuthorizationCode.objects.all() + serializer_class = ExpiringBaseGrantModelSerializer + filterset_fields = ["user", "provider"] + ordering = ["provider", "expires"] + + def get_queryset(self): + if not self.request: + return super().get_queryset() + if self.request.user.is_superuser: + return super().get_queryset() + return super().get_queryset().filter(user=self.request.user) + + +class RefreshTokenViewSet( + mixins.RetrieveModelMixin, + mixins.DestroyModelMixin, + mixins.ListModelMixin, + GenericViewSet, +): + """RefreshToken Viewset""" + + queryset = RefreshToken.objects.all() + serializer_class = ExpiringBaseGrantModelSerializer + filterset_fields = ["user", "provider"] + ordering = ["provider", "expires"] + + def get_queryset(self): + if not self.request: + return super().get_queryset() + if self.request.user.is_superuser: + return super().get_queryset() + return super().get_queryset().filter(user=self.request.user) diff --git a/authentik/providers/oauth2/models.py b/authentik/providers/oauth2/models.py index 53a1b78b3..1539dbf2a 100644 --- a/authentik/providers/oauth2/models.py +++ b/authentik/providers/oauth2/models.py @@ -119,7 +119,7 @@ class ScopeMapping(PropertyMapping): @property def serializer(self) -> Type[Serializer]: - from authentik.providers.oauth2.api import ScopeMappingSerializer + from authentik.providers.oauth2.api.scope import ScopeMappingSerializer return ScopeMappingSerializer @@ -287,7 +287,7 @@ class OAuth2Provider(Provider): @property def serializer(self) -> Type[Serializer]: - from authentik.providers.oauth2.api import OAuth2ProviderSerializer + from authentik.providers.oauth2.api.provider import OAuth2ProviderSerializer return OAuth2ProviderSerializer diff --git a/swagger.yaml b/swagger.yaml index b9755c193..93245dde1 100755 --- a/swagger.yaml +++ b/swagger.yaml @@ -2820,6 +2820,212 @@ paths: type: string format: slug pattern: ^[-a-zA-Z0-9_]+$ + /oauth2/authorization_codes/: + get: + operationId: oauth2_authorization_codes_list + description: AuthorizationCode Viewset + parameters: + - name: user + in: query + description: '' + required: false + type: string + - name: provider + in: query + description: '' + required: false + type: string + - name: ordering + in: query + description: Which field to use when ordering the results. + required: false + type: string + - name: search + in: query + description: A search term. + required: false + type: string + - name: page + in: query + description: Page Index + required: false + type: integer + - name: page_size + in: query + description: Page Size + required: false + type: integer + responses: + '200': + description: '' + schema: + required: + - results + - pagination + type: object + properties: + pagination: + required: + - next + - previous + - count + - current + - total_pages + - start_index + - end_index + type: object + properties: + next: + type: number + previous: + type: number + count: + type: number + current: + type: number + total_pages: + type: number + start_index: + type: number + end_index: + type: number + results: + type: array + items: + $ref: '#/definitions/ExpiringBaseGrantModel' + tags: + - oauth2 + parameters: [] + /oauth2/authorization_codes/{id}/: + get: + operationId: oauth2_authorization_codes_read + description: AuthorizationCode Viewset + parameters: [] + responses: + '200': + description: '' + schema: + $ref: '#/definitions/ExpiringBaseGrantModel' + tags: + - oauth2 + delete: + operationId: oauth2_authorization_codes_delete + description: AuthorizationCode Viewset + parameters: [] + responses: + '204': + description: '' + tags: + - oauth2 + parameters: + - name: id + in: path + description: A unique integer value identifying this Authorization Code. + required: true + type: integer + /oauth2/refresh_tokens/: + get: + operationId: oauth2_refresh_tokens_list + description: RefreshToken Viewset + parameters: + - name: user + in: query + description: '' + required: false + type: string + - name: provider + in: query + description: '' + required: false + type: string + - name: ordering + in: query + description: Which field to use when ordering the results. + required: false + type: string + - name: search + in: query + description: A search term. + required: false + type: string + - name: page + in: query + description: Page Index + required: false + type: integer + - name: page_size + in: query + description: Page Size + required: false + type: integer + responses: + '200': + description: '' + schema: + required: + - results + - pagination + type: object + properties: + pagination: + required: + - next + - previous + - count + - current + - total_pages + - start_index + - end_index + type: object + properties: + next: + type: number + previous: + type: number + count: + type: number + current: + type: number + total_pages: + type: number + start_index: + type: number + end_index: + type: number + results: + type: array + items: + $ref: '#/definitions/ExpiringBaseGrantModel' + tags: + - oauth2 + parameters: [] + /oauth2/refresh_tokens/{id}/: + get: + operationId: oauth2_refresh_tokens_read + description: RefreshToken Viewset + parameters: [] + responses: + '200': + description: '' + schema: + $ref: '#/definitions/ExpiringBaseGrantModel' + tags: + - oauth2 + delete: + operationId: oauth2_refresh_tokens_delete + description: RefreshToken Viewset + parameters: [] + responses: + '204': + description: '' + tags: + - oauth2 + parameters: + - name: id + in: path + description: A unique integer value identifying this OAuth2 Token. + required: true + type: integer /outposts/outposts/: get: operationId: outposts_outposts_list @@ -11123,6 +11329,149 @@ definitions: type: string readOnly: true minLength: 1 + OAuth2Provider: + title: Provider + description: OAuth2Provider Serializer + required: + - name + - application + - authorization_flow + type: object + properties: + pk: + title: ID + type: integer + readOnly: true + name: + title: Name + type: string + minLength: 1 + application: + title: Application + type: string + authorization_flow: + title: Authorization flow + description: Flow used when authorizing this provider. + type: string + format: uuid + property_mappings: + type: array + items: + type: string + format: uuid + uniqueItems: true + object_type: + title: Object type + type: string + readOnly: true + assigned_application_slug: + title: Assigned application slug + type: string + readOnly: true + assigned_application_name: + title: Assigned application name + type: string + readOnly: true + verbose_name: + title: Verbose name + type: string + readOnly: true + verbose_name_plural: + title: Verbose name plural + type: string + readOnly: true + client_type: + title: Client Type + description: |- + Confidential clients are capable of maintaining the confidentiality + of their credentials. Public clients are incapable. + type: string + enum: + - confidential + - public + client_id: + title: Client ID + type: string + maxLength: 255 + minLength: 1 + client_secret: + title: Client Secret + type: string + maxLength: 255 + token_validity: + title: Token validity + description: 'Tokens not valid on or after current time + this value (Format: + hours=1;minutes=2;seconds=3).' + type: string + minLength: 1 + include_claims_in_id_token: + title: Include claims in id_token + description: Include User claims from scopes in the id_token, for applications + that don't access the userinfo endpoint. + type: boolean + jwt_alg: + title: JWT Algorithm + description: Algorithm used to sign the JWT Token + type: string + enum: + - HS256 + - RS256 + rsa_key: + title: RSA Key + description: Key used to sign the tokens. Only required when JWT Algorithm + is set to RS256. + type: string + format: uuid + x-nullable: true + redirect_uris: + title: Redirect URIs + description: Enter each URI on a new line. + type: string + minLength: 1 + sub_mode: + title: Sub mode + description: Configure what data should be used as unique User Identifier. + For most cases, the default should be fine. + type: string + enum: + - hashed_user_id + - user_username + - user_email + - user_upn + issuer_mode: + title: Issuer mode + description: Configure how the issuer field of the ID Token should be filled. + type: string + enum: + - global + - per_provider + ExpiringBaseGrantModel: + description: Serializer for BaseGrantModel and ExpiringBaseGrant + required: + - provider + - user + type: object + properties: + pk: + title: ID + type: integer + readOnly: true + provider: + $ref: '#/definitions/OAuth2Provider' + user: + $ref: '#/definitions/User' + is_expired: + title: Is expired + type: string + readOnly: true + expires: + title: Expires + type: string + format: date-time + scope: + title: Scope + type: string + readOnly: true Outpost: description: Outpost Serializer required: @@ -12499,121 +12848,6 @@ definitions: title: Verbose name plural type: string readOnly: true - OAuth2Provider: - description: OAuth2Provider Serializer - required: - - name - - application - - authorization_flow - type: object - properties: - pk: - title: ID - type: integer - readOnly: true - name: - title: Name - type: string - minLength: 1 - application: - title: Application - type: string - authorization_flow: - title: Authorization flow - description: Flow used when authorizing this provider. - type: string - format: uuid - property_mappings: - type: array - items: - type: string - format: uuid - uniqueItems: true - object_type: - title: Object type - type: string - readOnly: true - assigned_application_slug: - title: Assigned application slug - type: string - readOnly: true - assigned_application_name: - title: Assigned application name - type: string - readOnly: true - verbose_name: - title: Verbose name - type: string - readOnly: true - verbose_name_plural: - title: Verbose name plural - type: string - readOnly: true - client_type: - title: Client Type - description: |- - Confidential clients are capable of maintaining the confidentiality - of their credentials. Public clients are incapable. - type: string - enum: - - confidential - - public - client_id: - title: Client ID - type: string - maxLength: 255 - minLength: 1 - client_secret: - title: Client Secret - type: string - maxLength: 255 - token_validity: - title: Token validity - description: 'Tokens not valid on or after current time + this value (Format: - hours=1;minutes=2;seconds=3).' - type: string - minLength: 1 - include_claims_in_id_token: - title: Include claims in id_token - description: Include User claims from scopes in the id_token, for applications - that don't access the userinfo endpoint. - type: boolean - jwt_alg: - title: JWT Algorithm - description: Algorithm used to sign the JWT Token - type: string - enum: - - HS256 - - RS256 - rsa_key: - title: RSA Key - description: Key used to sign the tokens. Only required when JWT Algorithm - is set to RS256. - type: string - format: uuid - x-nullable: true - redirect_uris: - title: Redirect URIs - description: Enter each URI on a new line. - type: string - minLength: 1 - sub_mode: - title: Sub mode - description: Configure what data should be used as unique User Identifier. - For most cases, the default should be fine. - type: string - enum: - - hashed_user_id - - user_username - - user_email - - user_upn - issuer_mode: - title: Issuer mode - description: Configure how the issuer field of the ID Token should be filled. - type: string - enum: - - global - - per_provider OAuth2ProviderSetupURLs: description: OAuth2 Provider Metadata serializer type: object