[server][core] store timestamp along with grants

This commit is contained in:
Abhishek Shroff
2025-03-15 11:21:01 +05:30
parent d9bf45b916
commit bb74178e41
6 changed files with 90 additions and 71 deletions

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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;

View File

@@ -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)

View File

@@ -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;

View File

@@ -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