20862: Add 'sdk/ruby-google-api-client/' from commit '2f4be67955e48bb65d008ecd9ff6da9...
[arvados.git] / services / api / lib / 20200501150153_permission_table_constants.rb
1 # Copyright (C) The Arvados Authors. All rights reserved.
2 #
3 # SPDX-License-Identifier: AGPL-3.0
4
5 # These constants are used in both
6 # db/migrate/20200501150153_permission_table and update_permissions
7 #
8 # This file allows them to be easily imported by both to avoid duplication.
9 #
10 # Don't mess with this!  Any changes will affect both the current
11 # update_permissions and the past migration.  If you are tinkering
12 # with the permission system and need to change how
13 # PERM_QUERY_TEMPLATE, refresh_trashed or refresh_permissions works,
14 # you should make a new file with your modified functions and have
15 # update_permissions reference that file instead.
16
17 PERMISSION_VIEW = "materialized_permissions"
18 TRASHED_GROUPS = "trashed_groups"
19 FROZEN_GROUPS = "frozen_groups"
20
21 # We need to use this parameterized query in a few different places,
22 # including as a subquery in a larger query.
23 #
24 # There's basically two options, the way I did this originally was to
25 # put this in a postgres function and do a lateral join over it.
26 # However, postgres functions impose an optimization barrier, and
27 # possibly have other overhead with temporary tables, so I ended up
28 # going with the brute force approach of inlining the whole thing.
29 #
30 # The two substitutions are "base_case" which determines the initial
31 # set of permission origins and "edge_perm" which is used to ensure
32 # that the new permission takes precedence over the one in the edges
33 # table (but some queries don't need that.)
34 #
35 PERM_QUERY_TEMPLATE = %{
36 WITH RECURSIVE
37         traverse_graph(origin_uuid, target_uuid, val, traverse_owned, starting_set) as (
38             %{base_case}
39           union
40             (select traverse_graph.origin_uuid,
41                     edges.head_uuid,
42                       least(%{edge_perm},
43                             traverse_graph.val),
44                     should_traverse_owned(edges.head_uuid, edges.val),
45                     false
46              from permission_graph_edges as edges, traverse_graph
47              where traverse_graph.target_uuid = edges.tail_uuid
48              and (edges.tail_uuid like '_____-j7d0g-_______________' or
49                   traverse_graph.starting_set)))
50         select traverse_graph.origin_uuid, target_uuid, max(val) as val, bool_or(traverse_owned) as traverse_owned from traverse_graph
51         group by (traverse_graph.origin_uuid, target_uuid)
52 }
53
54 def refresh_trashed
55   ActiveRecord::Base.transaction do
56     ActiveRecord::Base.connection.execute("LOCK TABLE #{TRASHED_GROUPS}")
57     ActiveRecord::Base.connection.execute("DELETE FROM #{TRASHED_GROUPS}")
58
59     # Helper populate trashed_groups table. This starts with
60     #   each group owned by a user and computes the subtree under that
61     #   group to find any groups that are trashed.
62     ActiveRecord::Base.connection.execute(%{
63 INSERT INTO #{TRASHED_GROUPS}
64 select ps.target_uuid as group_uuid, ps.trash_at from groups,
65   lateral project_subtree_with_trash_at(groups.uuid, groups.trash_at) ps
66   where groups.owner_uuid like '_____-tpzed-_______________' and ps.trash_at is not NULL
67 })
68   end
69 end
70
71 def refresh_permissions
72   ActiveRecord::Base.transaction do
73     ActiveRecord::Base.connection.execute("LOCK TABLE #{PERMISSION_VIEW}")
74     ActiveRecord::Base.connection.execute("DELETE FROM #{PERMISSION_VIEW}")
75
76     ActiveRecord::Base.connection.execute %{
77 INSERT INTO materialized_permissions
78     #{PERM_QUERY_TEMPLATE % {:base_case => %{
79         select uuid, uuid, 3, true, true from users
80 },
81 :edge_perm => 'edges.val'
82 } }
83 }, "refresh_permission_view.do"
84   end
85 end
86
87 def refresh_frozen
88   ActiveRecord::Base.transaction do
89     ActiveRecord::Base.connection.execute("LOCK TABLE #{FROZEN_GROUPS}")
90     ActiveRecord::Base.connection.execute("DELETE FROM #{FROZEN_GROUPS}")
91
92     # Compute entire frozen_groups table, starting with top-level
93     # projects (i.e., project groups owned by a user).
94     ActiveRecord::Base.connection.execute(%{
95 INSERT INTO #{FROZEN_GROUPS}
96 select ps.uuid from groups,
97   lateral project_subtree_with_is_frozen(groups.uuid, groups.frozen_by_uuid is not null) ps
98   where groups.owner_uuid like '_____-tpzed-_______________'
99     and group_class = 'project'
100     and ps.is_frozen
101 })
102   end
103 end