1 # Copyright (C) The Arvados Authors. All rights reserved.
3 # SPDX-License-Identifier: AGPL-3.0
5 PERMISSION_VIEW = "materialized_permissions"
6 TRASHED_GROUPS = "trashed_groups"
8 def refresh_permission_view
9 ActiveRecord::Base.transaction do
10 ActiveRecord::Base.connection.execute("LOCK TABLE #{PERMISSION_VIEW}")
11 ActiveRecord::Base.connection.execute("DELETE FROM #{PERMISSION_VIEW}")
12 ActiveRecord::Base.connection.execute %{
13 INSERT INTO #{PERMISSION_VIEW}
14 select users.uuid, g.target_uuid, g.val, g.traverse_owned
15 from users, lateral search_permission_graph(users.uuid, 3) as g where g.val > 0
17 "refresh_permission_view.do"
22 ActiveRecord::Base.connection.execute("DELETE FROM #{TRASHED_GROUPS}")
23 ActiveRecord::Base.connection.execute("INSERT INTO #{TRASHED_GROUPS} select * from compute_trashed()")
26 def update_permissions perm_origin_uuid, starting_uuid, perm_level, check=false
27 # Update a subset of the permission graph
28 # perm_level is the inherited permission
29 # perm_level is a number from 0-3
33 # call with perm_level=0 to revoke permissions
35 # 1. Compute set (group, permission) implied by traversing
36 # graph starting at this group
37 # 2. Find links from outside the graph that point inside
38 # 3. For each starting uuid, get the set of permissions from the
39 # materialized permission table
40 # 3. Delete permissions from table not in our computed subset.
41 # 4. Upsert each permission in our subset (user, group, val)
43 ActiveRecord::Base.connection.execute "LOCK TABLE #{PERMISSION_VIEW} in SHARE MODE"
45 ActiveRecord::Base.connection.exec_query "SET LOCAL enable_mergejoin to false;"
47 temptable_perms = "temp_perms_#{rand(2**64).to_s(10)}"
48 ActiveRecord::Base.connection.exec_query %{
49 create temporary table #{temptable_perms} on commit drop
50 as select * from compute_permission_subgraph($1, $2, $3)
52 'update_permissions.select',
53 [[nil, perm_origin_uuid],
57 ActiveRecord::Base.connection.exec_query "SET LOCAL enable_mergejoin to true;"
59 ActiveRecord::Base.connection.exec_delete %{
60 delete from #{PERMISSION_VIEW} where
61 target_uuid in (select target_uuid from #{temptable_perms}) and
62 not exists (select 1 from #{temptable_perms}
63 where target_uuid=#{PERMISSION_VIEW}.target_uuid and
64 user_uuid=#{PERMISSION_VIEW}.user_uuid and
67 "update_permissions.delete"
69 ActiveRecord::Base.connection.exec_query %{
70 insert into #{PERMISSION_VIEW} (user_uuid, target_uuid, perm_level, traverse_owned)
71 select user_uuid, target_uuid, val as perm_level, traverse_owned from #{temptable_perms} where val>0
72 on conflict (user_uuid, target_uuid) do update set perm_level=EXCLUDED.perm_level, traverse_owned=EXCLUDED.traverse_owned;
74 "update_permissions.insert"
76 if check and perm_level>0
77 check_permissions_against_full_refresh
82 def check_permissions_against_full_refresh
84 # For debugging, this checks contents of the
85 # incrementally-updated 'materialized_permission' against a
86 # from-scratch permission refresh.
89 q1 = ActiveRecord::Base.connection.exec_query %{
90 select user_uuid, target_uuid, perm_level, traverse_owned from #{PERMISSION_VIEW}
91 order by user_uuid, target_uuid
92 }, "check_permissions_against_full_refresh.permission_table"
94 q2 = ActiveRecord::Base.connection.exec_query %{
95 select users.uuid as user_uuid, g.target_uuid, g.val as perm_level, g.traverse_owned
96 from users, lateral search_permission_graph(users.uuid, 3) as g where g.val > 0
97 order by users.uuid, target_uuid
98 }, "check_permissions_against_full_refresh.full_recompute"
100 if q1.count != q2.count
101 puts "Didn't match incremental+: #{q1.count} != full refresh-: #{q2.count}"
104 if q1.count > q2.count
105 q1.each_with_index do |r, i|
107 puts "+#{r}\n-#{q2[i]}"
112 q2.each_with_index do |r, i|
114 puts "+#{q1[i]}\n-#{r}"