diff --git a/authentik/providers/scim/api/providers.py b/authentik/providers/scim/api/providers.py index 197550ded6..350473d646 100644 --- a/authentik/providers/scim/api/providers.py +++ b/authentik/providers/scim/api/providers.py @@ -28,6 +28,7 @@ class SCIMProviderSerializer(ProviderSerializer): "url", "verify_certificates", "token", + "compatibility_mode", "exclude_users_service_account", "filter_group", "dry_run", diff --git a/authentik/providers/scim/clients/base.py b/authentik/providers/scim/clients/base.py index ab7726abd0..32ffb8a951 100644 --- a/authentik/providers/scim/clients/base.py +++ b/authentik/providers/scim/clients/base.py @@ -22,7 +22,7 @@ from authentik.lib.sync.outgoing.exceptions import ( from authentik.lib.utils.http import get_http_session from authentik.providers.scim.clients.exceptions import SCIMRequestException from authentik.providers.scim.clients.schema import ServiceProviderConfiguration -from authentik.providers.scim.models import SCIMProvider +from authentik.providers.scim.models import SCIMCompatibilityMode, SCIMProvider if TYPE_CHECKING: from django.db.models import Model @@ -90,9 +90,14 @@ class SCIMClient[TModel: "Model", TConnection: "Model", TSchema: "BaseModel"]( """Get Service provider config""" default_config = ServiceProviderConfiguration.default() try: - return ServiceProviderConfiguration.model_validate( + config = ServiceProviderConfiguration.model_validate( self._request("GET", "/ServiceProviderConfig") ) + if self.provider.compatibility_mode == SCIMCompatibilityMode.AWS: + config.patch.supported = False + if self.provider.compatibility_mode == SCIMCompatibilityMode.SLACK: + config.filter.supported = True + return config except (ValidationError, SCIMRequestException, NotFoundSyncException) as exc: self.logger.warning("failed to get ServiceProviderConfig", exc=exc) return default_config diff --git a/authentik/providers/scim/migrations/0012_scimprovider_compatibility_mode.py b/authentik/providers/scim/migrations/0012_scimprovider_compatibility_mode.py new file mode 100644 index 0000000000..abc58ecc1f --- /dev/null +++ b/authentik/providers/scim/migrations/0012_scimprovider_compatibility_mode.py @@ -0,0 +1,24 @@ +# Generated by Django 5.0.12 on 2025-03-07 23:35 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("authentik_providers_scim", "0011_scimprovider_dry_run"), + ] + + operations = [ + migrations.AddField( + model_name="scimprovider", + name="compatibility_mode", + field=models.CharField( + choices=[("default", "Default"), ("aws", "AWS"), ("slack", "Slack")], + default="default", + help_text="Alter authentik behavior for vendor-specific SCIM implementations.", + max_length=30, + verbose_name="SCIM Compatibility Mode", + ), + ), + ] diff --git a/authentik/providers/scim/models.py b/authentik/providers/scim/models.py index db6e2086b2..8c85499a4a 100644 --- a/authentik/providers/scim/models.py +++ b/authentik/providers/scim/models.py @@ -57,6 +57,14 @@ class SCIMProviderGroup(SerializerModel): return f"SCIM Provider Group {self.group_id} to {self.provider_id}" +class SCIMCompatibilityMode(models.TextChoices): + """SCIM compatibility mode""" + + DEFAULT = "default", _("Default") + AWS = "aws", _("AWS") + SLACK = "slack", _("Slack") + + class SCIMProvider(OutgoingSyncProvider, BackchannelProvider): """SCIM 2.0 provider to create users and groups in external applications""" @@ -77,6 +85,14 @@ class SCIMProvider(OutgoingSyncProvider, BackchannelProvider): help_text=_("Property mappings used for group creation/updating."), ) + compatibility_mode = models.CharField( + max_length=30, + choices=SCIMCompatibilityMode.choices, + default=SCIMCompatibilityMode.DEFAULT, + verbose_name=_("SCIM Compatibility Mode"), + help_text=_("Alter authentik behavior for vendor-specific SCIM implementations."), + ) + @property def icon_url(self) -> str | None: return static("authentik/sources/scim.png") diff --git a/blueprints/schema.json b/blueprints/schema.json index 0e229a84dd..b945abf48e 100644 --- a/blueprints/schema.json +++ b/blueprints/schema.json @@ -6661,6 +6661,16 @@ "title": "Token", "description": "Authentication token" }, + "compatibility_mode": { + "type": "string", + "enum": [ + "default", + "aws", + "slack" + ], + "title": "SCIM Compatibility Mode", + "description": "Alter authentik behavior for vendor-specific SCIM implementations." + }, "exclude_users_service_account": { "type": "boolean", "title": "Exclude users service account" diff --git a/schema.yml b/schema.yml index 4e08ef1041..92d18fab57 100644 --- a/schema.yml +++ b/schema.yml @@ -41582,6 +41582,12 @@ components: - confidential - public type: string + CompatibilityModeEnum: + enum: + - default + - aws + - slack + type: string Config: type: object description: Serialize authentik Config into DRF Object @@ -52441,6 +52447,11 @@ components: type: string minLength: 1 description: Authentication token + compatibility_mode: + allOf: + - $ref: '#/components/schemas/CompatibilityModeEnum' + title: SCIM Compatibility Mode + description: Alter authentik behavior for vendor-specific SCIM implementations. exclude_users_service_account: type: boolean filter_group: @@ -55841,6 +55852,11 @@ components: token: type: string description: Authentication token + compatibility_mode: + allOf: + - $ref: '#/components/schemas/CompatibilityModeEnum' + title: SCIM Compatibility Mode + description: Alter authentik behavior for vendor-specific SCIM implementations. exclude_users_service_account: type: boolean filter_group: @@ -55931,6 +55947,11 @@ components: type: string minLength: 1 description: Authentication token + compatibility_mode: + allOf: + - $ref: '#/components/schemas/CompatibilityModeEnum' + title: SCIM Compatibility Mode + description: Alter authentik behavior for vendor-specific SCIM implementations. exclude_users_service_account: type: boolean filter_group: diff --git a/web/src/admin/providers/scim/SCIMProviderFormForm.ts b/web/src/admin/providers/scim/SCIMProviderFormForm.ts index b23c109657..2b09e45c3b 100644 --- a/web/src/admin/providers/scim/SCIMProviderFormForm.ts +++ b/web/src/admin/providers/scim/SCIMProviderFormForm.ts @@ -11,6 +11,7 @@ import { html } from "lit"; import { ifDefined } from "lit/directives/if-defined.js"; import { + CompatibilityModeEnum, CoreApi, CoreGroupsListRequest, Group, @@ -61,6 +62,35 @@ export function renderForm(provider?: Partial, errors: ValidationE )} inputHint="code" > +