core: add providers/types endpoint

This commit is contained in:
Jens Langhammer 2021-02-10 20:11:54 +01:00
parent e6712a50d2
commit 2b7a22a29a
5 changed files with 129 additions and 42 deletions

View File

@ -1,10 +1,21 @@
"""Provider API Views""" """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.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 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.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): class ProviderSerializer(ModelSerializer, MetaNameSerializer):
@ -51,3 +62,27 @@ class ProviderViewSet(ModelViewSet):
def get_queryset(self): def get_queryset(self):
return Provider.objects.select_subclasses() 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)

View File

@ -1,5 +1,6 @@
"""API Utilities""" """API Utilities"""
from django.db.models import Model from django.db.models import Model
from rest_framework.fields import CharField
from rest_framework.serializers import Serializer, SerializerMethodField from rest_framework.serializers import Serializer, SerializerMethodField
@ -22,3 +23,11 @@ class MetaNameSerializer(Serializer):
def get_verbose_name_plural(self, obj: Model) -> str: def get_verbose_name_plural(self, obj: Model) -> str:
"""Return object's plural verbose_name""" """Return object's plural verbose_name"""
return obj._meta.verbose_name_plural 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)

View File

@ -4312,6 +4312,47 @@ paths:
tags: tags:
- providers - providers
parameters: [] 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}/: /providers/all/{id}/:
get: get:
operationId: providers_all_read operationId: providers_all_read
@ -9005,6 +9046,25 @@ definitions:
title: Verbose name plural title: Verbose name plural
type: string type: string
readOnly: true 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: OAuth2Provider:
description: OAuth2Provider Serializer description: OAuth2Provider Serializer
required: required:

View File

@ -1,5 +1,11 @@
import { BaseInheritanceModel, DefaultClient, AKResponse, QueryArguments } from "./Client"; import { BaseInheritanceModel, DefaultClient, AKResponse, QueryArguments } from "./Client";
export interface TypeCreate {
name: string;
description: string;
link: string;
}
export class Provider implements BaseInheritanceModel { export class Provider implements BaseInheritanceModel {
pk: number; pk: number;
name: string; name: string;
@ -24,6 +30,10 @@ export class Provider implements BaseInheritanceModel {
return DefaultClient.fetch<AKResponse<Provider>>(["providers", "all"], filter); return DefaultClient.fetch<AKResponse<Provider>>(["providers", "all"], filter);
} }
static getTypes(): Promise<TypeCreate[]> {
return DefaultClient.fetch<TypeCreate[]>(["providers", "all", "types"]);
}
static adminUrl(rest: string): string { static adminUrl(rest: string): string {
return `/administration/providers/${rest}`; return `/administration/providers/${rest}`;
} }

View File

@ -8,6 +8,7 @@ import "../../elements/buttons/ModalButton";
import "../../elements/buttons/SpinnerButton"; import "../../elements/buttons/SpinnerButton";
import "../../elements/buttons/Dropdown"; import "../../elements/buttons/Dropdown";
import { TableColumn } from "../../elements/table/Table"; import { TableColumn } from "../../elements/table/Table";
import { until } from "lit-html/directives/until";
@customElement("ak-provider-list") @customElement("ak-provider-list")
export class ProviderListPage extends TablePage<Provider> { export class ProviderListPage extends TablePage<Provider> {
@ -81,46 +82,18 @@ export class ProviderListPage extends TablePage<Provider> {
<i class="fas fa-caret-down pf-c-dropdown__toggle-icon" aria-hidden="true"></i> <i class="fas fa-caret-down pf-c-dropdown__toggle-icon" aria-hidden="true"></i>
</button> </button>
<ul class="pf-c-dropdown__menu" hidden> <ul class="pf-c-dropdown__menu" hidden>
<li> ${until(Provider.getTypes().then((types) => {
<ak-modal-button href="${Provider.adminUrl("/create/?type=OAuth2Provider")}"> return types.map((type) => {
<button slot="trigger" class="pf-c-dropdown__menu-item">${gettext("OAuth2/OpenID Provider")}<br> return html`<li>
<small> <ak-modal-button href="${type.link}">
${gettext("OAuth2 Provider for generic OAuth and OpenID Connect Applications.")} <button slot="trigger" class="pf-c-dropdown__menu-item">${type.name}<br>
</small> <small>${type.description}</small>
</button> </button>
<div slot="modal"></div> <div slot="modal"></div>
</ak-modal-button> </ak-modal-button>
</li> </li>`;
<li> })
<ak-modal-button href="${Provider.adminUrl("/create/?type=ProxyProvider")}"> }), html`<ak-spinner></ak-spinner>`)}
<button slot="trigger" class="pf-c-dropdown__menu-item">${gettext("Proxy Provider")}<br>
<small>
${gettext("Protect applications that don't support any of the other Protocols by using a Reverse-Proxy.")}
</small>
</button>
<div slot="modal"></div>
</ak-modal-button>
</li>
<li>
<ak-modal-button href="${Provider.adminUrl("/create/?type=SAMLProvider")}">
<button slot="trigger" class="pf-c-dropdown__menu-item">${gettext("SAML Provider")}<br>
<small>
${gettext("SAML 2.0 Endpoint for applications which support SAML.")}
</small>
</button>
<div slot="modal"></div>
</ak-modal-button>
</li>
<li>
<ak-modal-button href="${Provider.adminUrl("/create/saml/from-metadata/")}">
<button slot="trigger" class="pf-c-dropdown__menu-item">${gettext("SAML Provider from Metadata")}<br>
<small>
${gettext("Create a SAML Provider by importing its Metadata.")}
</small>
</button>
<div slot="modal"></div>
</ak-modal-button>
</li>
</ul> </ul>
</ak-dropdown> </ak-dropdown>
${super.renderToolbar()}`; ${super.renderToolbar()}`;