core: add API for user source settings

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
This commit is contained in:
Jens Langhammer 2021-03-18 00:32:40 +01:00
parent 9a27bc8627
commit 07142cab8b
4 changed files with 78 additions and 4 deletions

View File

@ -1,4 +1,6 @@
"""Source API Views"""
from typing import Iterable
from django.urls import reverse
from drf_yasg2.utils import swagger_auto_schema
from rest_framework.decorators import action
@ -6,11 +8,16 @@ from rest_framework.request import Request
from rest_framework.response import Response
from rest_framework.serializers import ModelSerializer, SerializerMethodField
from rest_framework.viewsets import ReadOnlyModelViewSet
from structlog.stdlib import get_logger
from authentik.core.api.utils import MetaNameSerializer, TypeCreateSerializer
from authentik.core.models import Source
from authentik.flows.challenge import Challenge
from authentik.lib.templatetags.authentik_utils import verbose_name
from authentik.lib.utils.reflection import all_subclasses
from authentik.policies.engine import PolicyEngine
LOGGER = get_logger()
class SourceSerializer(ModelSerializer, MetaNameSerializer):
@ -63,3 +70,25 @@ class SourceViewSet(ReadOnlyModelViewSet):
}
)
return Response(TypeCreateSerializer(data, many=True).data)
@swagger_auto_schema(responses={200: Challenge(many=True)})
@action(detail=False)
def user_settings(self, request: Request) -> Response:
"""Get all sources the user can configure"""
_all_sources: Iterable[Source] = Source.objects.filter(
enabled=True
).select_subclasses()
matching_sources: list[Challenge] = []
for source in _all_sources:
user_settings = source.ui_user_settings
if not user_settings:
continue
policy_engine = PolicyEngine(source, request.user, request)
policy_engine.build()
if not policy_engine.passing:
continue
source_settings = source.ui_user_settings
if not source_settings.is_valid():
LOGGER.warning(source_settings.errors)
matching_sources.append(source_settings.validated_data)
return Response(matching_sources)

View File

@ -25,6 +25,7 @@ from structlog.stdlib import get_logger
from authentik.core.exceptions import PropertyMappingExpressionException
from authentik.core.signals import password_changed
from authentik.core.types import UILoginButton
from authentik.flows.challenge import Challenge
from authentik.flows.models import Flow
from authentik.lib.config import CONFIG
from authentik.lib.models import CreatedUpdatedModel, SerializerModel
@ -286,9 +287,9 @@ class Source(SerializerModel, PolicyBindingModel):
return None
@property
def ui_user_settings(self) -> Optional[str]:
def ui_user_settings(self) -> Optional[Challenge]:
"""Entrypoint to integrate with User settings. Can either return None if no
user settings are available, or a string with the URL to fetch."""
user settings are available, or a challenge."""
return None
def __str__(self):

View File

@ -10,6 +10,7 @@ from rest_framework.serializers import Serializer
from authentik.core.models import Source, UserSourceConnection
from authentik.core.types import UILoginButton
from authentik.flows.challenge import Challenge, ChallengeTypes
class OAuthSource(Source):
@ -66,9 +67,15 @@ class OAuthSource(Source):
)
@property
def ui_user_settings(self) -> Optional[str]:
def ui_user_settings(self) -> Optional[Challenge]:
view_name = "authentik_sources_oauth:oauth-client-user"
return reverse(view_name, kwargs={"source_slug": self.slug})
return Challenge(
data={
"type": ChallengeTypes.shell.value,
"title": self.name,
"component": reverse(view_name, kwargs={"source_slug": self.slug}),
}
)
def __str__(self) -> str:
return f"OAuth Source {self.name}"

View File

@ -6688,6 +6688,43 @@ paths:
tags:
- sources
parameters: []
/sources/all/user_settings/:
get:
operationId: sources_all_user_settings
description: Get all sources the user can configure
parameters:
- 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: Challenge that gets sent to the client based on which stage
is currently active
schema:
description: ''
type: array
items:
$ref: '#/definitions/Challenge'
tags:
- sources
parameters: []
/sources/all/{slug}/:
get:
operationId: sources_all_read