providers/SCIM: fix object exists error for users, attempt to look up user ID in remote system (#13437)
* providers/scim: handle ObjectExistsSyncException when filtering is supported by remote system Signed-off-by: Jens Langhammer <jens@goauthentik.io> * unrelated: correctly check for backchannel application in SCIM view page Signed-off-by: Jens Langhammer <jens@goauthentik.io> * unrelated: fix missing ignore paths in codespell Signed-off-by: Jens Langhammer <jens@goauthentik.io> * format Signed-off-by: Jens Langhammer <jens@goauthentik.io> --------- Signed-off-by: Jens Langhammer <jens@goauthentik.io>
This commit is contained in:
@ -1,10 +1,12 @@
|
|||||||
"""User client"""
|
"""User client"""
|
||||||
|
|
||||||
|
from django.db import transaction
|
||||||
|
from django.utils.http import urlencode
|
||||||
from pydantic import ValidationError
|
from pydantic import ValidationError
|
||||||
|
|
||||||
from authentik.core.models import User
|
from authentik.core.models import User
|
||||||
from authentik.lib.sync.mapper import PropertyMappingManager
|
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.policies.utils import delete_none_values
|
||||||
from authentik.providers.scim.clients.base import SCIMClient
|
from authentik.providers.scim.clients.base import SCIMClient
|
||||||
from authentik.providers.scim.clients.schema import SCIM_USER_SCHEMA
|
from authentik.providers.scim.clients.schema import SCIM_USER_SCHEMA
|
||||||
@ -55,6 +57,8 @@ class SCIMUserClient(SCIMClient[User, SCIMProviderUser, SCIMUserSchema]):
|
|||||||
def create(self, user: User):
|
def create(self, user: User):
|
||||||
"""Create user from scratch and create a connection object"""
|
"""Create user from scratch and create a connection object"""
|
||||||
scim_user = self.to_schema(user, None)
|
scim_user = self.to_schema(user, None)
|
||||||
|
with transaction.atomic():
|
||||||
|
try:
|
||||||
response = self._request(
|
response = self._request(
|
||||||
"POST",
|
"POST",
|
||||||
"/Users",
|
"/Users",
|
||||||
@ -63,10 +67,25 @@ class SCIMUserClient(SCIMClient[User, SCIMProviderUser, SCIMUserSchema]):
|
|||||||
exclude_unset=True,
|
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")
|
scim_id = response.get("id")
|
||||||
if not scim_id or scim_id == "":
|
if not scim_id or scim_id == "":
|
||||||
raise StopSync("SCIM Response with missing or invalid `id`")
|
raise StopSync("SCIM Response with missing or invalid `id`")
|
||||||
return SCIMProviderUser.objects.create(provider=self.provider, user=user, scim_id=scim_id)
|
return SCIMProviderUser.objects.create(
|
||||||
|
provider=self.provider, user=user, scim_id=scim_id
|
||||||
|
)
|
||||||
|
|
||||||
def update(self, user: User, connection: SCIMProviderUser):
|
def update(self, user: User, connection: SCIMProviderUser):
|
||||||
"""Update existing user"""
|
"""Update existing user"""
|
||||||
|
|||||||
@ -17,11 +17,15 @@ skip = [
|
|||||||
"go.sum",
|
"go.sum",
|
||||||
"locale",
|
"locale",
|
||||||
"**/dist",
|
"**/dist",
|
||||||
|
"**/storybook-static",
|
||||||
"**/web/src/locales",
|
"**/web/src/locales",
|
||||||
"**/web/xliff",
|
"**/web/xliff",
|
||||||
"./website/build",
|
"./website/build",
|
||||||
"./gen-ts-api",
|
"./gen-ts-api",
|
||||||
|
"./gen-py-api",
|
||||||
|
"./gen-go-api",
|
||||||
"*.api.mdx",
|
"*.api.mdx",
|
||||||
|
"./htmlcov",
|
||||||
]
|
]
|
||||||
dictionary = ".github/codespell-dictionary.txt,-"
|
dictionary = ".github/codespell-dictionary.txt,-"
|
||||||
ignore-words = ".github/codespell-words.txt"
|
ignore-words = ".github/codespell-words.txt"
|
||||||
|
|||||||
@ -21,12 +21,22 @@ export class RelatedApplicationButton extends AKElement {
|
|||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
provider?: Provider;
|
provider?: Provider;
|
||||||
|
|
||||||
|
@property()
|
||||||
|
mode: "primary" | "backchannel" = "primary";
|
||||||
|
|
||||||
render(): TemplateResult {
|
render(): TemplateResult {
|
||||||
if (this.provider?.assignedApplicationSlug) {
|
if (this.mode === "primary" && this.provider?.assignedApplicationSlug) {
|
||||||
return html`<a href="#/core/applications/${this.provider.assignedApplicationSlug}">
|
return html`<a href="#/core/applications/${this.provider.assignedApplicationSlug}">
|
||||||
${this.provider.assignedApplicationName}
|
${this.provider.assignedApplicationName}
|
||||||
</a>`;
|
</a>`;
|
||||||
}
|
}
|
||||||
|
if (this.mode === "backchannel" && this.provider?.assignedBackchannelApplicationSlug) {
|
||||||
|
return html`<a
|
||||||
|
href="#/core/applications/${this.provider.assignedBackchannelApplicationSlug}"
|
||||||
|
>
|
||||||
|
${this.provider.assignedBackchannelApplicationName}
|
||||||
|
</a>`;
|
||||||
|
}
|
||||||
return html`<ak-forms-modal>
|
return html`<ak-forms-modal>
|
||||||
<span slot="submit"> ${msg("Create")} </span>
|
<span slot="submit"> ${msg("Create")} </span>
|
||||||
<span slot="header"> ${msg("Create Application")} </span>
|
<span slot="header"> ${msg("Create Application")} </span>
|
||||||
|
|||||||
@ -174,6 +174,7 @@ export class SCIMProviderViewPage extends AKElement {
|
|||||||
<dd class="pf-c-description-list__description">
|
<dd class="pf-c-description-list__description">
|
||||||
<div class="pf-c-description-list__text">
|
<div class="pf-c-description-list__text">
|
||||||
<ak-provider-related-application
|
<ak-provider-related-application
|
||||||
|
mode="backchannel"
|
||||||
.provider=${this.provider}
|
.provider=${this.provider}
|
||||||
></ak-provider-related-application>
|
></ak-provider-related-application>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user