diff --git a/authentik/providers/scim/clients/users.py b/authentik/providers/scim/clients/users.py index 90ffe76abb..f9bfe58b88 100644 --- a/authentik/providers/scim/clients/users.py +++ b/authentik/providers/scim/clients/users.py @@ -1,10 +1,12 @@ """User client""" +from django.db import transaction +from django.utils.http import urlencode from pydantic import ValidationError from authentik.core.models import User from authentik.lib.sync.mapper import PropertyMappingManager -from authentik.lib.sync.outgoing.exceptions import StopSync +from authentik.lib.sync.outgoing.exceptions import ObjectExistsSyncException, StopSync from authentik.policies.utils import delete_none_values from authentik.providers.scim.clients.base import SCIMClient from authentik.providers.scim.clients.schema import SCIM_USER_SCHEMA @@ -55,18 +57,35 @@ class SCIMUserClient(SCIMClient[User, SCIMProviderUser, SCIMUserSchema]): def create(self, user: User): """Create user from scratch and create a connection object""" scim_user = self.to_schema(user, None) - response = self._request( - "POST", - "/Users", - json=scim_user.model_dump( - mode="json", - exclude_unset=True, - ), - ) - scim_id = response.get("id") - if not scim_id or scim_id == "": - raise StopSync("SCIM Response with missing or invalid `id`") - return SCIMProviderUser.objects.create(provider=self.provider, user=user, scim_id=scim_id) + with transaction.atomic(): + try: + response = self._request( + "POST", + "/Users", + json=scim_user.model_dump( + mode="json", + exclude_unset=True, + ), + ) + except ObjectExistsSyncException as exc: + if not self._config.filter.supported: + raise exc + users = self._request( + "GET", f"/Users?{urlencode({'filter': f'userName eq {scim_user.userName}'})}" + ) + users_res = users.get("Resources", []) + if len(users_res) < 1: + raise exc + return SCIMProviderUser.objects.create( + provider=self.provider, user=user, scim_id=users_res[0]["id"] + ) + else: + scim_id = response.get("id") + if not scim_id or scim_id == "": + raise StopSync("SCIM Response with missing or invalid `id`") + return SCIMProviderUser.objects.create( + provider=self.provider, user=user, scim_id=scim_id + ) def update(self, user: User, connection: SCIMProviderUser): """Update existing user""" diff --git a/web/src/admin/providers/RelatedApplicationButton.ts b/web/src/admin/providers/RelatedApplicationButton.ts index 3ada21bae9..c8ae28b5b8 100644 --- a/web/src/admin/providers/RelatedApplicationButton.ts +++ b/web/src/admin/providers/RelatedApplicationButton.ts @@ -21,12 +21,22 @@ export class RelatedApplicationButton extends AKElement { @property({ attribute: false }) provider?: Provider; + @property() + mode: "primary" | "backchannel" = "primary"; + render(): TemplateResult { - if (this.provider?.assignedApplicationSlug) { + if (this.mode === "primary" && this.provider?.assignedApplicationSlug) { return html` ${this.provider.assignedApplicationName} `; } + if (this.mode === "backchannel" && this.provider?.assignedBackchannelApplicationSlug) { + return html` + ${this.provider.assignedBackchannelApplicationName} + `; + } return html` ${msg("Create")} ${msg("Create Application")} diff --git a/web/src/admin/providers/scim/SCIMProviderViewPage.ts b/web/src/admin/providers/scim/SCIMProviderViewPage.ts index 808d4d8534..d0b6f11210 100644 --- a/web/src/admin/providers/scim/SCIMProviderViewPage.ts +++ b/web/src/admin/providers/scim/SCIMProviderViewPage.ts @@ -173,6 +173,7 @@ export class SCIMProviderViewPage extends AKElement {