feat(guardian): Downstream permissions targets to groups - #84
Conversation
| pk_field = "object_pk" | ||
|
|
||
| # Compute codenames, app_label, ctype | ||
| for perm in perms: |
There was a problem hiding this comment.
For my personal taste this method is too long.
It mixes setup (parsing the permissions) with normalization and then execution.
There was a problem hiding this comment.
Agreed, however kept it 1:1 possible with the upstream code cuz the customization comes at the veeery end of the code rather than the beginning, and as usual nothing is encapsulated into overridable code chunks :c
There was a problem hiding this comment.
So upstream having too long methods actually bricks inheritance and the ability to override small parts....
You see that's another reason for short methods....
We should have a preprocessor that just inlines upstream code 😸
| # query _and_ matching the parents. | ||
| values_list = perms_queryset.values_list(normalized_pk_field, flat=True) | ||
| perms_queryset = ( | ||
| queryset.model.objects.annotate( |
There was a problem hiding this comment.
Ready to read cursed SQL?
Baseline (upcasting to target type [uuid, int, varchar]):
SELECT
"authentik_core_user"."id", "authentik_core_user"."password", "authentik_core_user"."last_login",
"authentik_core_user"."username", "authentik_core_user"."first_name", "authentik_core_user"."last_name",
"authentik_core_user"."email", "authentik_core_user"."is_active", "authentik_core_user"."date_joined",
"authentik_core_user"."attributes", "authentik_core_user"."uuid", "authentik_core_user"."name",
"authentik_core_user"."path", "authentik_core_user"."type", "authentik_core_user"."password_change_date",
"authentik_core_user"."last_updated"
FROM "authentik_core_user"
WHERE (
NOT ("authentik_core_user"."username" = %s)
AND "authentik_core_user"."id" IN (
SELECT ("permission_subquery"."object_pk")::bigint AS "object_pk"
FROM (
SELECT "guardian_roleobjectpermission"."object_pk" AS "object_pk"
FROM "guardian_roleobjectpermission"
inner join "auth_permission"
ON ("guardian_roleobjectpermission"."permission_id" = "auth_permission"."id")
WHERE (
"guardian_roleobjectpermission"."role_id" IN
(
SELECT DISTINCT w0."uuid"
FROM "authentik_rbac_role" w0
left outer join "authentik_core_user_roles" w1
ON (w0."uuid" = w1."role_id")
left outer join "authentik_core_group_roles" w3
ON (w0."uuid" = w3."role_id")
WHERE (w1."user_id" = %s OR w3."group_id" IN
(
SELECT DISTINCT v0."group_uuid"
FROM "authentik_core_group" v0
left outer join "authentik_core_groupancestry" v1
ON ( v0."group_uuid" = v1."ancestor_id")
WHERE (v0."group_uuid" IN
(
SELECT u0."group_uuid" AS "pk"
FROM "authentik_core_group" u0
inner join "authentik_core_user_ak_groups" u1
ON (u0."group_uuid" = u1."group_id")
WHERE u1."user_id" = %s
)
OR v1."descendant_id" IN
(
SELECT u0."group_uuid" AS "pk"
FROM "authentik_core_group" u0
inner join "authentik_core_user_ak_groups" u1
ON (u0."group_uuid" = u1."group_id")
WHERE u1."user_id" = %s
)
)
)
)
)
AND "auth_permission"."content_type_id" = %s
AND "auth_permission"."codename" IN (%s))
) "permission_subquery" offset 0)
)This change (downcasting from target type [all varchars]):
SELECT
"authentik_core_user"."id", "authentik_core_user"."password", "authentik_core_user"."last_login",
"authentik_core_user"."username", "authentik_core_user"."first_name", "authentik_core_user"."last_name",
"authentik_core_user"."email", "authentik_core_user"."is_active", "authentik_core_user"."date_joined",
"authentik_core_user"."attributes", "authentik_core_user"."uuid", "authentik_core_user"."name",
"authentik_core_user"."path", "authentik_core_user"."type", "authentik_core_user"."password_change_date",
"authentik_core_user"."last_updated",( "authentik_core_user"."id" ) :: VARCHAR AS "t__normalized_id"
FROM "authentik_core_user"
WHERE (
NOT ( "authentik_core_user"."username" = %s )
AND ( "authentik_core_user"."id" ) :: VARCHAR IN (
SELECT ( X0."object_pk" ) :: VARCHAR AS "t__normalized_id"
FROM "guardian_roleobjectpermission" X0
inner join "auth_permission" X2
ON ( X0."permission_id" = X2."id" )
WHERE (
X0."role_id" IN (
SELECT DISTINCT W0."uuid"
FROM "authentik_rbac_role" W0
left outer join "authentik_core_user_roles" W1
ON ( W0."uuid" = W1."role_id" )
left outer join "authentik_core_group_roles" W3
ON ( W0."uuid" = W3."role_id" )
WHERE
( W1."user_id" = %s OR W3."group_id" IN (
SELECT DISTINCT V0."group_uuid"
FROM "authentik_core_group" V0
left outer join "authentik_core_groupancestry" V1
ON (V0."group_uuid" = V1."ancestor_id")
WHERE ( V0."group_uuid" IN (
SELECT U0."group_uuid" AS "pk"
FROM "authentik_core_group" U0
inner join "authentik_core_user_ak_groups" U1
ON ( U0."group_uuid" = U1."group_id" )
WHERE U1."user_id" = %s
)
OR V1."descendant_id" IN (
SELECT U0."group_uuid" AS "pk"
FROM "authentik_core_group" U0
inner join "authentik_core_user_ak_groups" U1
ON ( U0."group_uuid" = U1."group_id" )
WHERE U1."user_id" = %s
)
)
)
)
)
AND X2."content_type_id" = %s AND X2."codename" IN ( %s )
)
)
) `get_objects_for_user` relayed on a subquery for "duck-typing" heterogeneus primary key types and fencing the query planner optimizations. Now it's implemented with downcasting (casting from * -> varchar) using django orm `.annotate` calls. No need for RawSQL nor OFFSET 0 This behavior is toggled by the suse.use_custom_guardian bool flag
Allow granting permission towards a parent model (z.B: Group) and downstream it to all children. TL;DR: Grant view_group to a parent, and it grants view_group to all children.
5e6dae4 to
8140c7b
Compare
feat(guardian): Extend permission targets with parent relationships
Allow granting permission towards a parent model (z.B: Group) and
downstream it to all children.
TL;DR: Grant view_group to a parent, and it grants view_group to all
children.
Additionally took the chance to rewrite a RawSQL statement into native django annotations