
* rename consent permission Signed-off-by: Jens Langhammer <jens@goauthentik.io> * the user version Signed-off-by: Jens Langhammer <jens@goauthentik.io> t Signed-off-by: Jens Langhammer <jens@goauthentik.io> * initial role Signed-off-by: Jens Langhammer <jens@goauthentik.io> * start form Signed-off-by: Jens Langhammer <jens@goauthentik.io> * some minor table refactoring Signed-off-by: Jens Langhammer <jens@goauthentik.io> * fix user, add assign Signed-off-by: Jens Langhammer <jens@goauthentik.io> * add roles ui Signed-off-by: Jens Langhammer <jens@goauthentik.io> * fix backend Signed-off-by: Jens Langhammer <jens@goauthentik.io> * add assign API for roles Signed-off-by: Jens Langhammer <jens@goauthentik.io> * start adding toggle buttons Signed-off-by: Jens Langhammer <jens@goauthentik.io> * start view page Signed-off-by: Jens Langhammer <jens@goauthentik.io> * exclude add_ permission for per-object perms Signed-off-by: Jens Langhammer <jens@goauthentik.io> * small cleanup Signed-off-by: Jens Langhammer <jens@goauthentik.io> * add permission list for roles Signed-off-by: Jens Langhammer <jens@goauthentik.io> * make sidebar update Signed-off-by: Jens Langhammer <jens@goauthentik.io> * fix page header not re-rendering? Signed-off-by: Jens Langhammer <jens@goauthentik.io> * fixup Signed-off-by: Jens Langhammer <jens@goauthentik.io> * add search Signed-off-by: Jens Langhammer <jens@goauthentik.io> * show first category in table groupBy except when its empty Signed-off-by: Jens Langhammer <jens@goauthentik.io> * make model and object PK optional but required together Signed-off-by: Jens Langhammer <jens@goauthentik.io> * allow for setting global perms Signed-off-by: Jens Langhammer <jens@goauthentik.io> * exclude non-authentik permissions Signed-off-by: Jens Langhammer <jens@goauthentik.io> * exclude models which aren't allowed (base models etc) Signed-off-by: Jens Langhammer <jens@goauthentik.io> * ensure all models have verbose_name set, exclude some more internal objects Signed-off-by: Jens Langhammer <jens@goauthentik.io> * lint fix Signed-off-by: Jens Langhammer <jens@goauthentik.io> * fix role perm assign Signed-off-by: Jens Langhammer <jens@goauthentik.io> * add unasign for global perms Signed-off-by: Jens Langhammer <jens@goauthentik.io> * add meta changes Signed-off-by: Jens Langhammer <jens@goauthentik.io> * clear modal state after submit Signed-off-by: Jens Langhammer <jens@goauthentik.io> * add roles to our group Signed-off-by: Jens Langhammer <jens@goauthentik.io> * fix duplicate url names Signed-off-by: Jens Langhammer <jens@goauthentik.io> * make recursive group query more usable Signed-off-by: Jens Langhammer <jens@goauthentik.io> * add name field to role itself and move group creation to signal Signed-off-by: Jens Langhammer <jens@goauthentik.io> * start sync Signed-off-by: Jens Langhammer <jens@goauthentik.io> * move rbac stuff to separate django app Signed-off-by: Jens Langhammer <jens@goauthentik.io> * fix lint and such Signed-off-by: Jens Langhammer <jens@goauthentik.io> * fix go Signed-off-by: Jens Langhammer <jens@goauthentik.io> * update Signed-off-by: Jens Langhammer <jens@goauthentik.io> * start API changes Signed-off-by: Jens Langhammer <jens@goauthentik.io> * add more API tests Signed-off-by: Jens Langhammer <jens@goauthentik.io> * make admin interface not require superuser for now, improve error handling Signed-off-by: Jens Langhammer <jens@goauthentik.io> * replace some IsAdminUser where applicable Signed-off-by: Jens Langhammer <jens@goauthentik.io> * migrate flow inspector perms to actual permission Signed-off-by: Jens Langhammer <jens@goauthentik.io> * fix license not being a serializermodel Signed-off-by: Jens Langhammer <jens@goauthentik.io> * add permission modal to models without view page Signed-off-by: Jens Langhammer <jens@goauthentik.io> * add additional permissions to assign/unassign permissions Signed-off-by: Jens Langhammer <jens@goauthentik.io> * add action to unassign user permissions Signed-off-by: Jens Langhammer <jens@goauthentik.io> * add permissions tab to remaining view pages Signed-off-by: Jens Langhammer <jens@goauthentik.io> * fix Signed-off-by: Jens Langhammer <jens@goauthentik.io> * fix flow inspector permission check Signed-off-by: Jens Langhammer <jens@goauthentik.io> * fix codecov config? Signed-off-by: Jens Langhammer <jens@goauthentik.io> * add more API tests Signed-off-by: Jens Langhammer <jens@goauthentik.io> * ensure viewsets have an order set Signed-off-by: Jens Langhammer <jens@goauthentik.io> * hopefully the last api name change Signed-off-by: Jens Langhammer <jens@goauthentik.io> * make perm modal less confusing Signed-off-by: Jens Langhammer <jens@goauthentik.io> * start user view permission page Signed-off-by: Jens Langhammer <jens@goauthentik.io> * only make delete bulk form expandable if usedBy is set Signed-off-by: Jens Langhammer <jens@goauthentik.io> * expand permission tables Signed-off-by: Jens Langhammer <jens@goauthentik.io> * add more things Signed-off-by: Jens Langhammer <jens@goauthentik.io> * add user global permission table Signed-off-by: Jens Langhammer <jens@goauthentik.io> * fix lint Signed-off-by: Jens Langhammer <jens@goauthentik.io> * fix tests' url names Signed-off-by: Jens Langhammer <jens@goauthentik.io> * add tests for assign perms Signed-off-by: Jens Langhammer <jens@goauthentik.io> * add unassign tests Signed-off-by: Jens Langhammer <jens@goauthentik.io> * rebuild permissions Signed-off-by: Jens Langhammer <jens@goauthentik.io> * prevent assigning/unassigning permissions to internal service accounts Signed-off-by: Jens Langhammer <jens@goauthentik.io> * only enable default api browser in debug Signed-off-by: Jens Langhammer <jens@goauthentik.io> * fix role object permissions showing duplicate Signed-off-by: Jens Langhammer <jens@goauthentik.io> * fix role link on role object permissions table Signed-off-by: Jens Langhammer <jens@goauthentik.io> * fix object permission modal having duplicate close buttons Signed-off-by: Jens Langhammer <jens@goauthentik.io> * return error if user has no global perm and no object perms also improve error display on table Signed-off-by: Jens Langhammer <jens@goauthentik.io> * small optimisation Signed-off-by: Jens Langhammer <jens@goauthentik.io> * optimise even more Signed-off-by: Jens Langhammer <jens@goauthentik.io> * update locale Signed-off-by: Jens Langhammer <jens@goauthentik.io> * add system permission for non-object permissions Signed-off-by: Jens Langhammer <jens@goauthentik.io> * allow access to admin interface based on perm Signed-off-by: Jens Langhammer <jens@goauthentik.io> * clean Signed-off-by: Jens Langhammer <jens@goauthentik.io> * don't exclude base models Signed-off-by: Jens Langhammer <jens@goauthentik.io> --------- Signed-off-by: Jens Langhammer <jens@goauthentik.io>
141 lines
5.0 KiB
Python
141 lines
5.0 KiB
Python
"""transactional application and provider creation"""
|
|
from django.apps import apps
|
|
from drf_spectacular.utils import PolymorphicProxySerializer, extend_schema, extend_schema_field
|
|
from rest_framework.exceptions import ValidationError
|
|
from rest_framework.fields import BooleanField, CharField, ChoiceField, DictField, ListField
|
|
from rest_framework.permissions import IsAdminUser
|
|
from rest_framework.request import Request
|
|
from rest_framework.response import Response
|
|
from rest_framework.views import APIView
|
|
from yaml import ScalarNode
|
|
|
|
from authentik.blueprints.v1.common import (
|
|
Blueprint,
|
|
BlueprintEntry,
|
|
BlueprintEntryDesiredState,
|
|
EntryInvalidError,
|
|
KeyOf,
|
|
)
|
|
from authentik.blueprints.v1.importer import Importer
|
|
from authentik.core.api.applications import ApplicationSerializer
|
|
from authentik.core.api.utils import PassiveSerializer
|
|
from authentik.core.models import Provider
|
|
from authentik.lib.utils.reflection import all_subclasses
|
|
|
|
|
|
def get_provider_serializer_mapping():
|
|
"""Get a mapping of all providers' model names and their serializers"""
|
|
mapping = {}
|
|
for model in all_subclasses(Provider):
|
|
if model._meta.abstract:
|
|
continue
|
|
mapping[f"{model._meta.app_label}.{model._meta.model_name}"] = model().serializer
|
|
return mapping
|
|
|
|
|
|
@extend_schema_field(
|
|
PolymorphicProxySerializer(
|
|
component_name="model",
|
|
serializers=get_provider_serializer_mapping,
|
|
resource_type_field_name="provider_model",
|
|
)
|
|
)
|
|
class TransactionProviderField(DictField):
|
|
"""Dictionary field which can hold provider creation data"""
|
|
|
|
|
|
class TransactionApplicationSerializer(PassiveSerializer):
|
|
"""Serializer for creating a provider and an application in one transaction"""
|
|
|
|
app = ApplicationSerializer()
|
|
provider_model = ChoiceField(choices=list(get_provider_serializer_mapping().keys()))
|
|
provider = TransactionProviderField()
|
|
|
|
_provider_model: type[Provider] = None
|
|
|
|
def validate_provider_model(self, fq_model_name: str) -> str:
|
|
"""Validate that the model exists and is a provider"""
|
|
if "." not in fq_model_name:
|
|
raise ValidationError("Invalid provider model")
|
|
try:
|
|
app, _, model_name = fq_model_name.partition(".")
|
|
model = apps.get_model(app, model_name)
|
|
if not issubclass(model, Provider):
|
|
raise ValidationError("Invalid provider model")
|
|
self._provider_model = model
|
|
except LookupError:
|
|
raise ValidationError("Invalid provider model")
|
|
return fq_model_name
|
|
|
|
def validate(self, attrs: dict) -> dict:
|
|
blueprint = Blueprint()
|
|
blueprint.entries.append(
|
|
BlueprintEntry(
|
|
model=attrs["provider_model"],
|
|
state=BlueprintEntryDesiredState.MUST_CREATED,
|
|
identifiers={
|
|
"name": attrs["provider"]["name"],
|
|
},
|
|
# Must match the name of the field on `self`
|
|
id="provider",
|
|
attrs=attrs["provider"],
|
|
)
|
|
)
|
|
app_data = attrs["app"]
|
|
app_data["provider"] = KeyOf(None, ScalarNode(tag="", value="provider"))
|
|
blueprint.entries.append(
|
|
BlueprintEntry(
|
|
model="authentik_core.application",
|
|
state=BlueprintEntryDesiredState.MUST_CREATED,
|
|
identifiers={
|
|
"slug": attrs["app"]["slug"],
|
|
},
|
|
attrs=app_data,
|
|
# Must match the name of the field on `self`
|
|
id="app",
|
|
)
|
|
)
|
|
importer = Importer(blueprint, {})
|
|
try:
|
|
valid, _ = importer.validate(raise_validation_errors=True)
|
|
if not valid:
|
|
raise ValidationError("Invalid blueprint")
|
|
except EntryInvalidError as exc:
|
|
raise ValidationError(
|
|
{
|
|
exc.entry_id: exc.validation_error.detail,
|
|
}
|
|
)
|
|
return blueprint
|
|
|
|
|
|
class TransactionApplicationResponseSerializer(PassiveSerializer):
|
|
"""Transactional creation response"""
|
|
|
|
applied = BooleanField()
|
|
logs = ListField(child=CharField())
|
|
|
|
|
|
class TransactionalApplicationView(APIView):
|
|
"""Create provider and application and attach them in a single transaction"""
|
|
|
|
# TODO: Migrate to a more specific permission
|
|
permission_classes = [IsAdminUser]
|
|
|
|
@extend_schema(
|
|
request=TransactionApplicationSerializer(),
|
|
responses={
|
|
200: TransactionApplicationResponseSerializer(),
|
|
},
|
|
)
|
|
def put(self, request: Request) -> Response:
|
|
"""Convert data into a blueprint, validate it and apply it"""
|
|
data = TransactionApplicationSerializer(data=request.data)
|
|
data.is_valid(raise_exception=True)
|
|
|
|
importer = Importer(data.validated_data, {})
|
|
applied = importer.apply()
|
|
response = {"applied": False, "logs": []}
|
|
response["applied"] = applied
|
|
return Response(response, status=200)
|