diff --git a/authentik/core/api/providers.py b/authentik/core/api/providers.py index 429e7e29b..f4d72ff80 100644 --- a/authentik/core/api/providers.py +++ b/authentik/core/api/providers.py @@ -1,10 +1,21 @@ """Provider API Views""" +from django.shortcuts import reverse +from django.utils.translation import gettext_lazy as _ +from drf_yasg2.utils import swagger_auto_schema +from rest_framework.decorators import action from rest_framework.fields import ReadOnlyField -from rest_framework.serializers import ModelSerializer, SerializerMethodField +from rest_framework.request import Request +from rest_framework.response import Response +from rest_framework.serializers import ( + ModelSerializer, + SerializerMethodField, +) from rest_framework.viewsets import ModelViewSet -from authentik.core.api.utils import MetaNameSerializer +from authentik.core.api.utils import MetaNameSerializer, TypeCreateSerializer from authentik.core.models import Provider +from authentik.lib.templatetags.authentik_utils import verbose_name +from authentik.lib.utils.reflection import all_subclasses class ProviderSerializer(ModelSerializer, MetaNameSerializer): @@ -51,3 +62,27 @@ class ProviderViewSet(ModelViewSet): def get_queryset(self): return Provider.objects.select_subclasses() + + @swagger_auto_schema(responses={200: TypeCreateSerializer(many=True)}) + @action(detail=False) + # pylint: disable=unused-argument + def types(self, request: Request) -> Response: + """Get all creatable provider types""" + data = [] + for subclass in all_subclasses(self.queryset.model): + data.append( + { + "name": verbose_name(subclass), + "description": subclass.__doc__, + "link": reverse("authentik_admin:provider-create") + + f"?type={subclass.__name__}", + } + ) + data.append( + { + "name": _("SAML Provider from Metadata"), + "description": _("Create a SAML Provider by importing its Metadata."), + "link": reverse("authentik_admin:provider-saml-from-metadata"), + } + ) + return Response(TypeCreateSerializer(data, many=True).data) diff --git a/authentik/core/api/utils.py b/authentik/core/api/utils.py index 06c825856..36907e6f4 100644 --- a/authentik/core/api/utils.py +++ b/authentik/core/api/utils.py @@ -1,5 +1,6 @@ """API Utilities""" from django.db.models import Model +from rest_framework.fields import CharField from rest_framework.serializers import Serializer, SerializerMethodField @@ -22,3 +23,11 @@ class MetaNameSerializer(Serializer): def get_verbose_name_plural(self, obj: Model) -> str: """Return object's plural verbose_name""" return obj._meta.verbose_name_plural + + +class TypeCreateSerializer(Serializer): + """Types of an object that can be created""" + + name = CharField(read_only=True) + description = CharField(read_only=True) + link = CharField(read_only=True) diff --git a/swagger.yaml b/swagger.yaml index 3be428ecc..3f98d340e 100755 --- a/swagger.yaml +++ b/swagger.yaml @@ -4312,6 +4312,47 @@ paths: tags: - providers parameters: [] + /providers/all/types/: + get: + operationId: providers_all_types + description: Get all creatable provider types + parameters: + - name: application__isnull + 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: A page number within the paginated result set. + required: false + type: integer + - name: page_size + in: query + description: Number of results to return per page. + required: false + type: integer + responses: + '200': + description: '' + schema: + description: '' + type: array + items: + $ref: '#/definitions/TypeCreate' + tags: + - providers + parameters: [] /providers/all/{id}/: get: operationId: providers_all_read @@ -9005,6 +9046,25 @@ definitions: title: Verbose name plural type: string readOnly: true + TypeCreate: + description: '' + type: object + properties: + name: + title: Name + type: string + readOnly: true + minLength: 1 + description: + title: Description + type: string + readOnly: true + minLength: 1 + link: + title: Link + type: string + readOnly: true + minLength: 1 OAuth2Provider: description: OAuth2Provider Serializer required: diff --git a/web/src/api/Providers.ts b/web/src/api/Providers.ts index 6d1f56e17..6a5e66946 100644 --- a/web/src/api/Providers.ts +++ b/web/src/api/Providers.ts @@ -1,5 +1,11 @@ import { BaseInheritanceModel, DefaultClient, AKResponse, QueryArguments } from "./Client"; +export interface TypeCreate { + name: string; + description: string; + link: string; +} + export class Provider implements BaseInheritanceModel { pk: number; name: string; @@ -24,6 +30,10 @@ export class Provider implements BaseInheritanceModel { return DefaultClient.fetch>(["providers", "all"], filter); } + static getTypes(): Promise { + return DefaultClient.fetch(["providers", "all", "types"]); + } + static adminUrl(rest: string): string { return `/administration/providers/${rest}`; } diff --git a/web/src/pages/providers/ProviderListPage.ts b/web/src/pages/providers/ProviderListPage.ts index e759286cb..c7ca29608 100644 --- a/web/src/pages/providers/ProviderListPage.ts +++ b/web/src/pages/providers/ProviderListPage.ts @@ -8,6 +8,7 @@ import "../../elements/buttons/ModalButton"; import "../../elements/buttons/SpinnerButton"; import "../../elements/buttons/Dropdown"; import { TableColumn } from "../../elements/table/Table"; +import { until } from "lit-html/directives/until"; @customElement("ak-provider-list") export class ProviderListPage extends TablePage { @@ -81,46 +82,18 @@ export class ProviderListPage extends TablePage { ${super.renderToolbar()}`;