1 # Copyright (C) The Arvados Authors. All rights reserved.
3 # SPDX-License-Identifier: AGPL-3.0
5 class FixTrashFlagFollow < ActiveRecord::Migration
7 ActiveRecord::Base.connection.execute("DROP MATERIALIZED VIEW materialized_permission_view")
8 ActiveRecord::Base.connection.execute(
9 "-- constructing perm_edges
10 -- 1. get the list of all permission links,
11 -- 2. any can_manage link or permission link to a group means permission should 'follow through'
12 -- (as a special case, can_manage links to a user grant access to everything owned by the user,
13 -- unlike can_read or can_write which only grant access to the user record)
14 -- 3. add all owner->owned relationships between groups as can_manage edges
16 -- constructing permissions
17 -- 1. base case: start with set of all users as the working set
19 -- join with edges where the tail is in the working set and 'follow' is true
20 -- produce a new working set with the head (target) of each edge
21 -- set permission to the least permission encountered on the path
22 -- propagate trashed flag down
24 CREATE MATERIALIZED VIEW materialized_permission_view AS
26 perm_value (name, val) AS (
28 ('can_read', 1::smallint),
33 perm_edges (tail_uuid, head_uuid, val, follow, trashed) AS (
34 SELECT links.tail_uuid,
37 (pv.val = 3 OR groups.uuid IS NOT NULL) AS follow,
38 0::smallint AS trashed,
39 0::smallint AS followtrash
41 LEFT JOIN perm_value pv ON pv.name = links.name
42 LEFT JOIN groups ON pv.val<3 AND groups.uuid = links.head_uuid
43 WHERE links.link_class = 'permission'
45 SELECT owner_uuid, uuid, 3, true,
46 CASE WHEN trash_at IS NOT NULL and trash_at < clock_timestamp() THEN 1 ELSE 0 END,
50 perm (val, follow, user_uuid, target_uuid, trashed) AS (
51 SELECT 3::smallint AS val,
53 users.uuid::varchar(32) AS user_uuid,
54 users.uuid::varchar(32) AS target_uuid,
55 0::smallint AS trashed
58 SELECT LEAST(perm.val, edges.val)::smallint AS val,
59 edges.follow AS follow,
60 perm.user_uuid::varchar(32) AS user_uuid,
61 edges.head_uuid::varchar(32) AS target_uuid,
62 (GREATEST(perm.trashed, edges.trashed) * edges.followtrash)::smallint AS trashed
64 INNER JOIN perm_edges edges
65 ON perm.follow AND edges.tail_uuid = perm.target_uuid
69 MAX(val) AS perm_level,
70 CASE follow WHEN true THEN target_uuid ELSE NULL END AS target_owner_uuid,
71 MAX(trashed) AS trashed
73 GROUP BY user_uuid, target_uuid, target_owner_uuid;
75 add_index :materialized_permission_view, [:trashed, :target_uuid], name: 'permission_target_trashed'
76 add_index :materialized_permission_view, [:user_uuid, :trashed, :perm_level], name: 'permission_target_user_trashed_level'
77 ActiveRecord::Base.connection.execute("REFRESH MATERIALIZED VIEW materialized_permission_view")