providers/scim: save attributes returned from remote system like google workspace and entra ID (#13459)

providers/scim: save attributes returned from remote system like google workspace and entra

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
This commit is contained in:
Jens L.
2025-03-18 13:35:56 +00:00
committed by GitHub
parent 5f315bddbd
commit bb20576d84
9 changed files with 60 additions and 4 deletions

View File

@ -24,7 +24,9 @@ class SCIMProviderGroupSerializer(ModelSerializer):
"group", "group",
"group_obj", "group_obj",
"provider", "provider",
"attributes",
] ]
extra_kwargs = {"attributes": {"read_only": True}}
class SCIMProviderGroupViewSet( class SCIMProviderGroupViewSet(

View File

@ -24,7 +24,9 @@ class SCIMProviderUserSerializer(ModelSerializer):
"user", "user",
"user_obj", "user_obj",
"provider", "provider",
"attributes",
] ]
extra_kwargs = {"attributes": {"read_only": True}}
class SCIMProviderUserViewSet( class SCIMProviderUserViewSet(

View File

@ -102,7 +102,7 @@ class SCIMGroupClient(SCIMClient[Group, SCIMProviderGroup, SCIMGroupSchema]):
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`")
connection = SCIMProviderGroup.objects.create( connection = SCIMProviderGroup.objects.create(
provider=self.provider, group=group, scim_id=scim_id provider=self.provider, group=group, scim_id=scim_id, attributes=response
) )
users = list(group.users.order_by("id").values_list("id", flat=True)) users = list(group.users.order_by("id").values_list("id", flat=True))
self._patch_add_users(connection, users) self._patch_add_users(connection, users)

View File

@ -77,21 +77,24 @@ class SCIMUserClient(SCIMClient[User, SCIMProviderUser, SCIMUserSchema]):
if len(users_res) < 1: if len(users_res) < 1:
raise exc raise exc
return SCIMProviderUser.objects.create( return SCIMProviderUser.objects.create(
provider=self.provider, user=user, scim_id=users_res[0]["id"] provider=self.provider,
user=user,
scim_id=users_res[0]["id"],
attributes=users_res[0],
) )
else: 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( return SCIMProviderUser.objects.create(
provider=self.provider, user=user, scim_id=scim_id provider=self.provider, user=user, scim_id=scim_id, attributes=response
) )
def update(self, user: User, connection: SCIMProviderUser): def update(self, user: User, connection: SCIMProviderUser):
"""Update existing user""" """Update existing user"""
scim_user = self.to_schema(user, connection) scim_user = self.to_schema(user, connection)
scim_user.id = connection.scim_id scim_user.id = connection.scim_id
self._request( response = self._request(
"PUT", "PUT",
f"/Users/{connection.scim_id}", f"/Users/{connection.scim_id}",
json=scim_user.model_dump( json=scim_user.model_dump(
@ -99,3 +102,5 @@ class SCIMUserClient(SCIMClient[User, SCIMProviderUser, SCIMUserSchema]):
exclude_unset=True, exclude_unset=True,
), ),
) )
connection.attributes = response
connection.save()

View File

@ -0,0 +1,23 @@
# Generated by Django 5.0.12 on 2025-03-11 13:51
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("authentik_providers_scim", "0011_scimprovider_dry_run"),
]
operations = [
migrations.AddField(
model_name="scimprovidergroup",
name="attributes",
field=models.JSONField(default=dict),
),
migrations.AddField(
model_name="scimprovideruser",
name="attributes",
field=models.JSONField(default=dict),
),
]

View File

@ -22,6 +22,7 @@ class SCIMProviderUser(SerializerModel):
scim_id = models.TextField() scim_id = models.TextField()
user = models.ForeignKey(User, on_delete=models.CASCADE) user = models.ForeignKey(User, on_delete=models.CASCADE)
provider = models.ForeignKey("SCIMProvider", on_delete=models.CASCADE) provider = models.ForeignKey("SCIMProvider", on_delete=models.CASCADE)
attributes = models.JSONField(default=dict)
@property @property
def serializer(self) -> type[Serializer]: def serializer(self) -> type[Serializer]:
@ -43,6 +44,7 @@ class SCIMProviderGroup(SerializerModel):
scim_id = models.TextField() scim_id = models.TextField()
group = models.ForeignKey(Group, on_delete=models.CASCADE) group = models.ForeignKey(Group, on_delete=models.CASCADE)
provider = models.ForeignKey("SCIMProvider", on_delete=models.CASCADE) provider = models.ForeignKey("SCIMProvider", on_delete=models.CASCADE)
attributes = models.JSONField(default=dict)
@property @property
def serializer(self) -> type[Serializer]: def serializer(self) -> type[Serializer]:

View File

@ -55897,7 +55897,10 @@ components:
readOnly: true readOnly: true
provider: provider:
type: integer type: integer
attributes:
readOnly: true
required: required:
- attributes
- group - group
- group_obj - group_obj
- id - id
@ -55984,7 +55987,10 @@ components:
readOnly: true readOnly: true
provider: provider:
type: integer type: integer
attributes:
readOnly: true
required: required:
- attributes
- id - id
- provider - provider
- scim_id - scim_id

View File

@ -24,6 +24,7 @@ export class SCIMProviderGroupList extends Table<SCIMProviderGroup> {
return true; return true;
} }
expandable = true;
checkbox = true; checkbox = true;
clearOnRefresh = true; clearOnRefresh = true;
@ -81,6 +82,13 @@ export class SCIMProviderGroupList extends Table<SCIMProviderGroup> {
html`${item.id}`, html`${item.id}`,
]; ];
} }
renderExpanded(item: SCIMProviderGroup): TemplateResult {
return html`<td role="cell" colspan="4">
<div class="pf-c-table__expandable-row-content">
<pre>${JSON.stringify(item.attributes, null, 4)}</pre>
</div>
</td>`;
}
} }
declare global { declare global {

View File

@ -24,6 +24,7 @@ export class SCIMProviderUserList extends Table<SCIMProviderUser> {
return true; return true;
} }
expandable = true;
checkbox = true; checkbox = true;
clearOnRefresh = true; clearOnRefresh = true;
@ -82,6 +83,13 @@ export class SCIMProviderUserList extends Table<SCIMProviderUser> {
html`${item.id}`, html`${item.id}`,
]; ];
} }
renderExpanded(item: SCIMProviderUser): TemplateResult {
return html`<td role="cell" colspan="4">
<div class="pf-c-table__expandable-row-content">
<pre>${JSON.stringify(item.attributes, null, 4)}</pre>
</div>
</td>`;
}
} }
declare global { declare global {