* add basic guacamole Signed-off-by: Jens Langhammer <jens@goauthentik.io> * make everything mostly work Signed-off-by: Jens Langhammer <jens@goauthentik.io> * add rac build to CI Signed-off-by: Jens Langhammer <jens@goauthentik.io> * fix resize, fix web lint, sendSize correctly Signed-off-by: Jens Langhammer <jens@goauthentik.io> * pre-send connection from client, format Signed-off-by: Jens Langhammer <jens@goauthentik.io> * improve throughput Signed-off-by: Jens Langhammer <jens@goauthentik.io> * cleanup Signed-off-by: Jens Langhammer <jens@goauthentik.io> * rework TokenOutpostConsumer into middleware Signed-off-by: Jens Langhammer <jens@goauthentik.io> * fix some layout issues Signed-off-by: Jens Langhammer <jens@goauthentik.io> * add outpost controllers Signed-off-by: Jens Langhammer <jens@goauthentik.io> * start testing audio things Signed-off-by: Jens Langhammer <jens@goauthentik.io> * fix a bunch of things Signed-off-by: Jens Langhammer <jens@goauthentik.io> * add deps Signed-off-by: Jens Langhammer <jens@goauthentik.io> * fix to work with outpost group Signed-off-by: Jens Langhammer <jens@goauthentik.io> * add simple loadbalancing Signed-off-by: Jens Langhammer <jens@goauthentik.io> * add simple reconnect Signed-off-by: Jens Langhammer <jens@goauthentik.io> * show reconnecting text Signed-off-by: Jens Langhammer <jens@goauthentik.io> * fix error when checking ports Signed-off-by: Jens Langhammer <jens@goauthentik.io> * move to providers Signed-off-by: Jens Langhammer <jens@goauthentik.io> * add flow check to interface Signed-off-by: Jens Langhammer <jens@goauthentik.io> * fix go lint Signed-off-by: Jens Langhammer <jens@goauthentik.io> * fix rac app label Signed-off-by: Jens Langhammer <jens@goauthentik.io> * fix audio Signed-off-by: Jens Langhammer <jens@goauthentik.io> * add logging Signed-off-by: Jens Langhammer <jens@goauthentik.io> * cleanup Signed-off-by: Jens Langhammer <jens@goauthentik.io> * allow overriding all settings Signed-off-by: Jens Langhammer <jens@goauthentik.io> * fix duplicate keyboard, debug high DPI Signed-off-by: Jens Langhammer <jens@goauthentik.io> * re-add deps Signed-off-by: Jens Langhammer <jens@goauthentik.io> * fix lint Signed-off-by: Jens Langhammer <jens@goauthentik.io> * fix missing __init__.py breaking model loading I love python Signed-off-by: Jens Langhammer <jens@goauthentik.io> * fix tests Signed-off-by: Jens Langhammer <jens@goauthentik.io> * bump successful ws connection to info Signed-off-by: Jens Langhammer <jens@goauthentik.io> * hide cursor since guac draws that Signed-off-by: Jens Langhammer <jens@goauthentik.io> * add clipboard support (bidirectional) Signed-off-by: Jens Langhammer <jens@goauthentik.io> * make codespell not want to break the code Signed-off-by: Jens Langhammer <jens@goauthentik.io> * run pr comment in separate task Signed-off-by: Jens Langhammer <jens@goauthentik.io> * start endpoint and property mapping stuff Signed-off-by: Jens Langhammer <jens@goauthentik.io> * more endpoint things Signed-off-by: Jens Langhammer <jens@goauthentik.io> * unrelated: fix event model_pk filtering with ints Signed-off-by: Jens Langhammer <jens@goauthentik.io> * unrelated: improve event display for changelog Signed-off-by: Jens Langhammer <jens@goauthentik.io> * rebuild endpoint stuff again Signed-off-by: Jens Langhammer <jens@goauthentik.io> * idk special url Signed-off-by: Jens Langhammer <jens@goauthentik.io> * more stuff, connect token with session Signed-off-by: Jens Langhammer <jens@goauthentik.io> * add disconnect Signed-off-by: Jens Langhammer <jens@goauthentik.io> * rework disconnect cleanly disconnect from guacd instead of just letting the connection timeout Signed-off-by: Jens Langhammer <jens@goauthentik.io> * clear cache when creating outpost Signed-off-by: Jens Langhammer <jens@goauthentik.io> * support host:port and fix protocol Signed-off-by: Jens Langhammer <jens@goauthentik.io> * center smaller viewport Signed-off-by: Jens Langhammer <jens@goauthentik.io> * rework connection to wait more and stop after some time Signed-off-by: Jens Langhammer <jens@goauthentik.io> * add policy control to endpoints Signed-off-by: Jens Langhammer <jens@goauthentik.io> * remove provider protocol Signed-off-by: Jens Langhammer <jens@goauthentik.io> * don't switch to different outpost connection when already chosen Signed-off-by: Jens Langhammer <jens@goauthentik.io> * start using property mappings, add static settings Signed-off-by: Jens Langhammer <jens@goauthentik.io> * add some RAC mapping settings Signed-off-by: Jens Langhammer <jens@goauthentik.io> * fix lint Signed-off-by: Jens Langhammer <jens@goauthentik.io> * start adding tests Signed-off-by: Jens Langhammer <jens@goauthentik.io> * add tests for event changes Signed-off-by: Jens Langhammer <jens@goauthentik.io> * add tests and fix issues found by said tests Signed-off-by: Jens Langhammer <jens@goauthentik.io> * add preview banner, move endpoints to main page Signed-off-by: Jens Langhammer <jens@goauthentik.io> * add locale Signed-off-by: Jens Langhammer <jens@goauthentik.io> * auto-select endpoint if only one is available Signed-off-by: Jens Langhammer <jens@goauthentik.io> * backport https://github.com/goauthentik/authentik/pull/7831 to rac Signed-off-by: Jens Langhammer <jens@goauthentik.io> * dont select property mappings on endpoints Signed-off-by: Jens Langhammer <jens@goauthentik.io> * make table modal only load when opened Signed-off-by: Jens Langhammer <jens@goauthentik.io> * only auto-redirect when open Signed-off-by: Jens Langhammer <jens@goauthentik.io> * fix web deps Signed-off-by: Jens Langhammer <jens@goauthentik.io> * check for token expiry and terminate session Signed-off-by: Jens Langhammer <jens@goauthentik.io> * re-add endpoint name to title Signed-off-by: Jens Langhammer <jens@goauthentik.io> * disconnect connection when token is manually deleted Signed-off-by: Jens Langhammer <jens@goauthentik.io> * add initial RAC docs Signed-off-by: Jens Langhammer <jens@goauthentik.io> * add connection expiry setting to provider Signed-off-by: Jens Langhammer <jens@goauthentik.io> * fix flaky tests Signed-off-by: Jens Langhammer <jens@goauthentik.io> --------- Signed-off-by: Jens Langhammer <jens@goauthentik.io>
187 lines
6.7 KiB
Python
187 lines
6.7 KiB
Python
"""Outpost API Views"""
|
|
from dacite.core import from_dict
|
|
from dacite.exceptions import DaciteError
|
|
from django_filters.filters import ModelMultipleChoiceFilter
|
|
from django_filters.filterset import FilterSet
|
|
from drf_spectacular.utils import extend_schema
|
|
from rest_framework.decorators import action
|
|
from rest_framework.fields import BooleanField, CharField, DateTimeField
|
|
from rest_framework.relations import PrimaryKeyRelatedField
|
|
from rest_framework.request import Request
|
|
from rest_framework.response import Response
|
|
from rest_framework.serializers import ModelSerializer, ValidationError
|
|
from rest_framework.viewsets import ModelViewSet
|
|
|
|
from authentik import get_build_hash
|
|
from authentik.core.api.providers import ProviderSerializer
|
|
from authentik.core.api.used_by import UsedByMixin
|
|
from authentik.core.api.utils import JSONDictField, PassiveSerializer
|
|
from authentik.core.models import Provider
|
|
from authentik.enterprise.providers.rac.models import RACProvider
|
|
from authentik.outposts.api.service_connections import ServiceConnectionSerializer
|
|
from authentik.outposts.apps import MANAGED_OUTPOST, MANAGED_OUTPOST_NAME
|
|
from authentik.outposts.models import (
|
|
Outpost,
|
|
OutpostConfig,
|
|
OutpostState,
|
|
OutpostType,
|
|
default_outpost_config,
|
|
)
|
|
from authentik.providers.ldap.models import LDAPProvider
|
|
from authentik.providers.proxy.models import ProxyProvider
|
|
from authentik.providers.radius.models import RadiusProvider
|
|
|
|
|
|
class OutpostSerializer(ModelSerializer):
|
|
"""Outpost Serializer"""
|
|
|
|
config = JSONDictField(source="_config")
|
|
# Need to set allow_empty=True for the embedded outpost with no providers
|
|
# is checked for other providers in the API Viewset
|
|
providers = PrimaryKeyRelatedField(
|
|
allow_empty=True,
|
|
many=True,
|
|
queryset=Provider.objects.select_subclasses().all(),
|
|
)
|
|
providers_obj = ProviderSerializer(source="providers", many=True, read_only=True)
|
|
service_connection_obj = ServiceConnectionSerializer(
|
|
source="service_connection", read_only=True
|
|
)
|
|
|
|
def validate_name(self, name: str) -> str:
|
|
"""Validate name (especially for embedded outpost)"""
|
|
if not self.instance:
|
|
return name
|
|
if self.instance.managed == MANAGED_OUTPOST and name != MANAGED_OUTPOST_NAME:
|
|
raise ValidationError("Embedded outpost's name cannot be changed")
|
|
if self.instance.name == MANAGED_OUTPOST_NAME:
|
|
self.instance.managed = MANAGED_OUTPOST
|
|
return name
|
|
|
|
def validate_providers(self, providers: list[Provider]) -> list[Provider]:
|
|
"""Check that all providers match the type of the outpost"""
|
|
type_map = {
|
|
OutpostType.LDAP: LDAPProvider,
|
|
OutpostType.PROXY: ProxyProvider,
|
|
OutpostType.RADIUS: RadiusProvider,
|
|
OutpostType.RAC: RACProvider,
|
|
None: Provider,
|
|
}
|
|
for provider in providers:
|
|
if not isinstance(provider, type_map[self.initial_data.get("type")]):
|
|
raise ValidationError(
|
|
f"Outpost type {self.initial_data['type']} can't be used with "
|
|
f"{provider.__class__.__name__} providers."
|
|
)
|
|
if self.instance and self.instance.managed == MANAGED_OUTPOST:
|
|
return providers
|
|
if len(providers) < 1:
|
|
raise ValidationError("This list may not be empty.")
|
|
return providers
|
|
|
|
def validate_config(self, config) -> dict:
|
|
"""Check that the config has all required fields"""
|
|
try:
|
|
from_dict(OutpostConfig, config)
|
|
except DaciteError as exc:
|
|
raise ValidationError(f"Failed to validate config: {str(exc)}") from exc
|
|
return config
|
|
|
|
class Meta:
|
|
model = Outpost
|
|
fields = [
|
|
"pk",
|
|
"name",
|
|
"type",
|
|
"providers",
|
|
"providers_obj",
|
|
"service_connection",
|
|
"service_connection_obj",
|
|
"token_identifier",
|
|
"config",
|
|
"managed",
|
|
]
|
|
extra_kwargs = {"type": {"required": True}}
|
|
|
|
|
|
class OutpostDefaultConfigSerializer(PassiveSerializer):
|
|
"""Global default outpost config"""
|
|
|
|
config = JSONDictField(read_only=True)
|
|
|
|
|
|
class OutpostHealthSerializer(PassiveSerializer):
|
|
"""Outpost health status"""
|
|
|
|
uid = CharField(read_only=True)
|
|
last_seen = DateTimeField(read_only=True)
|
|
version = CharField(read_only=True)
|
|
version_should = CharField(read_only=True)
|
|
|
|
version_outdated = BooleanField(read_only=True)
|
|
|
|
build_hash = CharField(read_only=True, required=False)
|
|
build_hash_should = CharField(read_only=True, required=False)
|
|
|
|
hostname = CharField(read_only=True, required=False)
|
|
|
|
|
|
class OutpostFilter(FilterSet):
|
|
"""Filter for Outposts"""
|
|
|
|
providers_by_pk = ModelMultipleChoiceFilter(
|
|
field_name="providers",
|
|
queryset=Provider.objects.all(),
|
|
)
|
|
|
|
class Meta:
|
|
model = Outpost
|
|
fields = {
|
|
"providers": ["isnull"],
|
|
"name": ["iexact", "icontains"],
|
|
"service_connection__name": ["iexact", "icontains"],
|
|
"managed": ["iexact", "icontains"],
|
|
}
|
|
|
|
|
|
class OutpostViewSet(UsedByMixin, ModelViewSet):
|
|
"""Outpost Viewset"""
|
|
|
|
queryset = Outpost.objects.all()
|
|
serializer_class = OutpostSerializer
|
|
filterset_class = OutpostFilter
|
|
search_fields = [
|
|
"name",
|
|
"providers__name",
|
|
]
|
|
ordering = ["name", "service_connection__name"]
|
|
|
|
@extend_schema(responses={200: OutpostHealthSerializer(many=True)})
|
|
@action(methods=["GET"], detail=True, pagination_class=None)
|
|
def health(self, request: Request, pk: int) -> Response:
|
|
"""Get outposts current health"""
|
|
outpost: Outpost = self.get_object()
|
|
states = []
|
|
for state in outpost.state:
|
|
state: OutpostState
|
|
states.append(
|
|
{
|
|
"uid": state.uid,
|
|
"last_seen": state.last_seen,
|
|
"version": state.version,
|
|
"version_should": state.version_should,
|
|
"version_outdated": state.version_outdated,
|
|
"build_hash": state.build_hash,
|
|
"hostname": state.hostname,
|
|
"build_hash_should": get_build_hash(),
|
|
}
|
|
)
|
|
return Response(OutpostHealthSerializer(states, many=True).data)
|
|
|
|
@extend_schema(responses={200: OutpostDefaultConfigSerializer(many=False)})
|
|
@action(detail=False, methods=["GET"])
|
|
def default_settings(self, request: Request) -> Response:
|
|
"""Global default outpost config"""
|
|
host = self.request.build_absolute_uri("/")
|
|
return Response({"config": default_outpost_config(host)})
|