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:
@ -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(
|
||||||
|
@ -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(
|
||||||
|
@ -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)
|
||||||
|
@ -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()
|
||||||
|
@ -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),
|
||||||
|
),
|
||||||
|
]
|
@ -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]:
|
||||||
|
@ -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
|
||||||
|
@ -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 {
|
||||||
|
@ -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 {
|
||||||
|
Reference in New Issue
Block a user