diff --git a/authentik/core/api/transactional_applications.py b/authentik/core/api/transactional_applications.py deleted file mode 100644 index fa48d0523..000000000 --- a/authentik/core/api/transactional_applications.py +++ /dev/null @@ -1,127 +0,0 @@ -"""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, - 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 - - -class TransactionApplicationResponseSerializer(PassiveSerializer): - """Transactional creation response""" - - valid = BooleanField() - applied = BooleanField() - logs = ListField(child=CharField()) - - -class TransactionalApplicationView(APIView): - """Create provider and application and attach them in a single transaction""" - - 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) - print(data.validated_data) - - blueprint = Blueprint() - blueprint.entries.append( - BlueprintEntry( - model=data.validated_data["provider_model"], - state=BlueprintEntryDesiredState.MUST_CREATED, - identifiers={ - "name": data.validated_data["provider"]["name"], - }, - id="provider", - attrs=data.validated_data["provider"], - ) - ) - app_data = data.validated_data["app"] - app_data["provider"] = KeyOf(None, ScalarNode(tag="", value="provider")) - blueprint.entries.append( - BlueprintEntry( - model="authentik_core.application", - state=BlueprintEntryDesiredState.MUST_CREATED, - identifiers={ - "slug": data.validated_data["app"]["slug"], - }, - attrs=app_data, - ) - ) - importer = Importer(blueprint, {}) - response = {"valid": False, "applied": False, "logs": []} - valid, logs = importer.validate() - response["logs"] = [x["event"] for x in logs] - response["valid"] = valid - if valid: - applied = importer.apply() - response["applied"] = applied - return Response(response, status=200) diff --git a/authentik/core/tests/test_transactional_applications_api.py b/authentik/core/tests/test_transactional_applications_api.py deleted file mode 100644 index beea6b5ee..000000000 --- a/authentik/core/tests/test_transactional_applications_api.py +++ /dev/null @@ -1,45 +0,0 @@ -"""Test Transactional API""" -from json import loads - -from django.urls import reverse -from rest_framework.test import APITestCase - -from authentik.core.models import Application -from authentik.core.tests.utils import create_test_admin_user, create_test_flow -from authentik.lib.generators import generate_id -from authentik.providers.oauth2.models import OAuth2Provider - - -class TestTransactionalApplicationsAPI(APITestCase): - """Test Transactional API""" - - def setUp(self) -> None: - self.user = create_test_admin_user() - - def test_create_transactional(self): - """Test transactional Application + provider creation""" - self.client.force_login(self.user) - uid = generate_id() - authorization_flow = create_test_flow() - response = self.client.put( - reverse("authentik_api:core-transactional-application"), - data={ - "app": { - "name": uid, - "slug": uid, - }, - "provider_model": "authentik_providers_oauth2.oauth2provider", - "provider": { - "name": uid, - "authorization_flow": str(authorization_flow.pk), - }, - }, - ) - response_body = loads(response.content.decode()) - self.assertTrue(response_body["valid"]) - self.assertTrue(response_body["applied"]) - provider = OAuth2Provider.objects.filter(name=uid).first() - self.assertIsNotNone(provider) - app = Application.objects.filter(slug=uid).first() - self.assertIsNotNone(app) - self.assertEqual(app.provider.pk, provider.pk) diff --git a/authentik/core/urls.py b/authentik/core/urls.py index 0914d4e88..c9aa748c5 100644 --- a/authentik/core/urls.py +++ b/authentik/core/urls.py @@ -15,7 +15,6 @@ from authentik.core.api.propertymappings import PropertyMappingViewSet from authentik.core.api.providers import ProviderViewSet from authentik.core.api.sources import SourceViewSet, UserSourceConnectionViewSet from authentik.core.api.tokens import TokenViewSet -from authentik.core.api.transactional_applications import TransactionalApplicationView from authentik.core.api.users import UserViewSet from authentik.core.views import apps from authentik.core.views.debug import AccessDeniedView @@ -71,11 +70,6 @@ urlpatterns = [ api_urlpatterns = [ ("core/authenticated_sessions", AuthenticatedSessionViewSet), ("core/applications", ApplicationViewSet), - path( - "core/transactional/applications/", - TransactionalApplicationView.as_view(), - name="core-transactional-application", - ), ("core/groups", GroupViewSet), ("core/users", UserViewSet), ("core/tokens", TokenViewSet), diff --git a/schema.yml b/schema.yml index 412aeb02b..b1c26397e 100644 --- a/schema.yml +++ b/schema.yml @@ -4349,39 +4349,6 @@ paths: schema: $ref: '#/components/schemas/GenericError' description: '' - /core/transactional/applications/: - put: - operationId: core_transactional_applications_update - description: Convert data into a blueprint, validate it and apply it - tags: - - core - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/TransactionApplicationRequest' - required: true - security: - - authentik: [] - responses: - '200': - content: - application/json: - schema: - $ref: '#/components/schemas/TransactionApplicationResponse' - description: '' - '400': - content: - application/json: - schema: - $ref: '#/components/schemas/ValidationError' - description: '' - '403': - content: - application/json: - schema: - $ref: '#/components/schemas/GenericError' - description: '' /core/user_consent/: get: operationId: core_user_consent_list @@ -37593,22 +37560,6 @@ components: description: |- * `twilio` - Twilio * `generic` - Generic - ProviderModelEnum: - enum: - - authentik_providers_ldap.ldapprovider - - authentik_providers_oauth2.oauth2provider - - authentik_providers_proxy.proxyprovider - - authentik_providers_radius.radiusprovider - - authentik_providers_saml.samlprovider - - authentik_providers_scim.scimprovider - type: string - description: |- - * `authentik_providers_ldap.ldapprovider` - authentik_providers_ldap.ldapprovider - * `authentik_providers_oauth2.oauth2provider` - authentik_providers_oauth2.oauth2provider - * `authentik_providers_proxy.proxyprovider` - authentik_providers_proxy.proxyprovider - * `authentik_providers_radius.radiusprovider` - authentik_providers_radius.radiusprovider - * `authentik_providers_saml.samlprovider` - authentik_providers_saml.samlprovider - * `authentik_providers_scim.scimprovider` - authentik_providers_scim.scimprovider ProviderRequest: type: object description: Provider Serializer @@ -40020,36 +39971,6 @@ components: readOnly: true required: - key - TransactionApplicationRequest: - type: object - description: Serializer for creating a provider and an application in one transaction - properties: - app: - $ref: '#/components/schemas/ApplicationRequest' - provider_model: - $ref: '#/components/schemas/ProviderModelEnum' - provider: - $ref: '#/components/schemas/modelRequest' - required: - - app - - provider - - provider_model - TransactionApplicationResponse: - type: object - description: Transactional creation response - properties: - valid: - type: boolean - applied: - type: boolean - logs: - type: array - items: - type: string - required: - - applied - - logs - - valid TypeCreate: type: object description: Types of an object that can be created @@ -40977,23 +40898,6 @@ components: type: integer required: - count - modelRequest: - oneOf: - - $ref: '#/components/schemas/LDAPProviderRequest' - - $ref: '#/components/schemas/OAuth2ProviderRequest' - - $ref: '#/components/schemas/ProxyProviderRequest' - - $ref: '#/components/schemas/RadiusProviderRequest' - - $ref: '#/components/schemas/SAMLProviderRequest' - - $ref: '#/components/schemas/SCIMProviderRequest' - discriminator: - propertyName: provider_model - mapping: - authentik_providers_ldap.ldapprovider: '#/components/schemas/LDAPProviderRequest' - authentik_providers_oauth2.oauth2provider: '#/components/schemas/OAuth2ProviderRequest' - authentik_providers_proxy.proxyprovider: '#/components/schemas/ProxyProviderRequest' - authentik_providers_radius.radiusprovider: '#/components/schemas/RadiusProviderRequest' - authentik_providers_saml.samlprovider: '#/components/schemas/SAMLProviderRequest' - authentik_providers_scim.scimprovider: '#/components/schemas/SCIMProviderRequest' securitySchemes: authentik: type: apiKey