From bb74178e414b86fbdfb8b80f982426f31defcfaa Mon Sep 17 00:00:00 2001 From: Abhishek Shroff Date: Sat, 15 Mar 2025 11:21:01 +0530 Subject: [PATCH] [server][core] store timestamp along with grants --- server/internal/core/db/fs.sql.go | 28 ------------ server/internal/core/db/fs_permissions.sql.go | 43 ++++++++++++++++++- .../core/db/migrations/data/012_access.sql | 22 +++------- server/internal/core/fs/update.go | 32 ++++++++++---- server/sql/queries/fs.sql | 16 ------- server/sql/queries/fs_permissions.sql | 20 ++++++++- 6 files changed, 90 insertions(+), 71 deletions(-) diff --git a/server/internal/core/db/fs.sql.go b/server/internal/core/db/fs.sql.go index 0ab8579d..652da863 100644 --- a/server/internal/core/db/fs.sql.go +++ b/server/internal/core/db/fs.sql.go @@ -440,31 +440,3 @@ func (q *Queries) UpdateResourceParent(ctx context.Context, arg UpdateResourcePa ) return i, err } - -const updateUserPermissionsForResource = `-- name: UpdateUserPermissionsForResource :exec -INSERT INTO permissions( - resource_id, - username, - modified, - value -) VALUES ( - $1::UUID, - $2::TEXT, - NOW(), - $3::INT -) ON CONFLICT(resource_id, username) DO UPDATE -SET - modified = CURRENT_TIMESTAMP, - value = $3::INT -` - -type UpdateUserPermissionsForResourceParams struct { - ResourceID uuid.UUID - Username string - Permission int32 -} - -func (q *Queries) UpdateUserPermissionsForResource(ctx context.Context, arg UpdateUserPermissionsForResourceParams) error { - _, err := q.db.Exec(ctx, updateUserPermissionsForResource, arg.ResourceID, arg.Username, arg.Permission) - return err -} diff --git a/server/internal/core/db/fs_permissions.sql.go b/server/internal/core/db/fs_permissions.sql.go index 37d7f0e3..d4ce1898 100644 --- a/server/internal/core/db/fs_permissions.sql.go +++ b/server/internal/core/db/fs_permissions.sql.go @@ -11,15 +11,35 @@ import ( "github.com/google/uuid" ) +const grantUserPermissionForResource = `-- name: GrantUserPermissionForResource :exec +UPDATE resources +SET + grants[$1::text] = jsonb_build_object('p', $2::INT, 't', EXTRACT(EPOCH FROM NOW())::INTEGER), + modified = NOW() +WHERE id = $3::uuid +RETURNING permissions +` + +type GrantUserPermissionForResourceParams struct { + Username string + Permission int32 + ResourceID uuid.UUID +} + +func (q *Queries) GrantUserPermissionForResource(ctx context.Context, arg GrantUserPermissionForResourceParams) error { + _, err := q.db.Exec(ctx, grantUserPermissionForResource, arg.Username, arg.Permission, arg.ResourceID) + return err +} + const recomputePermissions = `-- name: RecomputePermissions :exec WITH RECURSIVE nodes(id, parent, permissions) AS ( - SELECT r.id, r.parent, CASE WHEN r.parent IS NULL THEN r.grants ELSE jsonb_bitwise_or(p.permissions, r.grants) END + SELECT r.id, r.parent, phylum_merge_permission_grants(COALESCE(p.permissions, '{}'::JSONB), r.grants) END FROM resources r LEFT JOIN resources p ON r.parent = p.id WHERE r.id = $1::uuid UNION ALL - SELECT r.id, r.parent, jsonb_bitwise_or(n.permissions, r.grants) + SELECT r.id, r.parent, phylum_merge_permission_grants(n.permissions, r.grants) FROM resources r JOIN nodes n on r.parent = n.id ) UPDATE resources @@ -32,3 +52,22 @@ func (q *Queries) RecomputePermissions(ctx context.Context, resourceID uuid.UUID _, err := q.db.Exec(ctx, recomputePermissions, resourceID) return err } + +const revokeUserPermissionForResource = `-- name: RevokeUserPermissionForResource :exec +UPDATE resources +SET + grants = grants - $1::TEXT, + modified = NOW() +WHERE id = $2::uuid +RETURNING permissions +` + +type RevokeUserPermissionForResourceParams struct { + Username string + ResourceID uuid.UUID +} + +func (q *Queries) RevokeUserPermissionForResource(ctx context.Context, arg RevokeUserPermissionForResourceParams) error { + _, err := q.db.Exec(ctx, revokeUserPermissionForResource, arg.Username, arg.ResourceID) + return err +} diff --git a/server/internal/core/db/migrations/data/012_access.sql b/server/internal/core/db/migrations/data/012_access.sql index 3320dc52..c85763fa 100644 --- a/server/internal/core/db/migrations/data/012_access.sql +++ b/server/internal/core/db/migrations/data/012_access.sql @@ -2,31 +2,23 @@ ALTER TABLE resources ADD COLUMN permissions JSONB NOT NULL DEFAULT '{}'; ALTER TABLE resources ADD COLUMN grants JSONB NOT NULL DEFAULT '{}'; -CREATE OR REPLACE FUNCTION jsonb_bitwise_or(a JSONB, b JSONB) +CREATE OR REPLACE FUNCTION phylum_merge_permission_grants(a JSONB, b JSONB) RETURNS JSONB LANGUAGE SQL AS $$ - SELECT + SELECT COALESCE(jsonb_object_agg( - COALESCE(ka, kb), + COALESCE(ka, kb), CASE - WHEN va ISNULL THEN vb::integer - WHEN vb ISNULL THEN va::integer - ELSE va::integer | vb::integer + WHEN vb ISNULL THEN va::integer + WHEN va ISNULL THEN (vb -> 'p')::integer + ELSE va::integer | (vb -> 'p')::integer END ), '{}'::JSONB) FROM jsonb_each(a) e1(ka, va) FULL JOIN jsonb_each(b) e2(kb, vb) ON ka = kb $$; -CREATE AGGREGATE agg_jsonb_bitwise_or(JSONB) ( - SFUNC = jsonb_bitwise_or, - STYPE = JSONB, - INITCOND = '{}' -); - ---- create above / drop below ---- -DROP AGGREGATE agg_jsonb_bitwise_or(JSONB); - -DROP FUNCTION jsonb_bitwise_or; +DROP FUNCTION phylum_merge_permission_grants; ALTER TABLE resources DROP COLUMN permissions; diff --git a/server/internal/core/fs/update.go b/server/internal/core/fs/update.go index c7d624d3..50b76957 100644 --- a/server/internal/core/fs/update.go +++ b/server/internal/core/fs/update.go @@ -13,15 +13,31 @@ func (r Resource) UpdatePermissions(username string, permission Permission) (Res // Do not grant more than what you have permission = permission & r.userPermission - var err error - err = r.f.db.UpdateUserPermissionsForResource(r.f.ctx, db.UpdateUserPermissionsForResourceParams{ - ResourceID: r.ID(), - Username: username, - Permission: permission, + ctx := r.f.ctx + err := r.f.db.WithTx(ctx, func(dbh *db.DbHandler) error { + if permission == PermissionNone { + if err := dbh.RevokeUserPermissionForResource(ctx, db.RevokeUserPermissionForResourceParams{ + ResourceID: r.ID(), + Username: username, + }); err != nil { + return err + } + } else { + if err := dbh.GrantUserPermissionForResource(ctx, db.GrantUserPermissionForResourceParams{ + ResourceID: r.ID(), + Username: username, + Permission: permission, + }); err != nil { + return err + } + } + + if err := dbh.RecomputePermissions(ctx, r.ID()); err != nil { + return err + } + + return nil }) - if err != nil { - return Resource{}, err - } p := make(map[string]Permission) if r.Info.permissions != nil { json.Unmarshal(r.Info.permissions, &p) diff --git a/server/sql/queries/fs.sql b/server/sql/queries/fs.sql index f37757c1..01ba6291 100644 --- a/server/sql/queries/fs.sql +++ b/server/sql/queries/fs.sql @@ -58,22 +58,6 @@ SET WHERE id = @id::uuid RETURNING name, parent, modified; --- name: UpdateUserPermissionsForResource :exec -INSERT INTO permissions( - resource_id, - username, - modified, - value -) VALUES ( - @resource_id::UUID, - @username::TEXT, - NOW(), - @permission::INT -) ON CONFLICT(resource_id, username) DO UPDATE -SET - modified = CURRENT_TIMESTAMP, - value = @permission::INT; - -- name: ChildResourceByName :one SELECT * FROM resources WHERE parent = @parent::uuid AND name = @name::text AND deleted IS NULL; diff --git a/server/sql/queries/fs_permissions.sql b/server/sql/queries/fs_permissions.sql index 5650c481..a7ec727f 100644 --- a/server/sql/queries/fs_permissions.sql +++ b/server/sql/queries/fs_permissions.sql @@ -1,12 +1,28 @@ +-- name: GrantUserPermissionForResource :exec +UPDATE resources +SET + grants[@username::text] = jsonb_build_object('p', @permission::INT, 't', EXTRACT(EPOCH FROM NOW())::INTEGER), + modified = NOW() +WHERE id = @resource_id::uuid +RETURNING permissions; + +-- name: RevokeUserPermissionForResource :exec +UPDATE resources +SET + grants = grants - @username::TEXT, + modified = NOW() +WHERE id = @resource_id::uuid +RETURNING permissions; + -- name: RecomputePermissions :exec WITH RECURSIVE nodes(id, parent, permissions) AS ( - SELECT r.id, r.parent, CASE WHEN r.parent IS NULL THEN r.grants ELSE jsonb_bitwise_or(p.permissions, r.grants) END + SELECT r.id, r.parent, phylum_merge_permission_grants(COALESCE(p.permissions, '{}'::JSONB), r.grants) END FROM resources r LEFT JOIN resources p ON r.parent = p.id WHERE r.id = @resource_id::uuid UNION ALL - SELECT r.id, r.parent, jsonb_bitwise_or(n.permissions, r.grants) + SELECT r.id, r.parent, phylum_merge_permission_grants(n.permissions, r.grants) FROM resources r JOIN nodes n on r.parent = n.id ) UPDATE resources