Project

General

Profile

Bug #16007 » select_subtree.sql

Peter Amstutz, 05/01/2020 02:55 PM

 
1
WITH RECURSIVE
2
	project_subtree(uuid) as (
3
--	values ('x1u39-j7d0g-rufsjii9txtu2xg')
4
--	values ('x1u39-tpzed-fr97h9t4m5jffxs')
5
	values ('x1u39-tpzed-3kz0nwtjehhl0u4')
6
	union
7
	select groups.uuid from groups join project_subtree on (groups.owner_uuid = project_subtree.uuid)
8
	)
9
	select uuid from project_subtree;
10

    
11
WITH RECURSIVE edges(tail_uuid, head_uuid) as (
12
	  select groups.owner_uuid, groups.uuid from groups
13
	  union
14
	  select links.tail_uuid, links.head_uuid from links
15
	   where links.link_class='permission'
16
	),
17
	project_subtree(uuid) as (
18
--	values ('x1u39-j7d0g-rufsjii9txtu2xg')
19
--	values ('x1u39-tpzed-fr97h9t4m5jffxs')
20
	values ('x1u39-tpzed-3kz0nwtjehhl0u4')
21
	union
22
	(select edges.head_uuid from edges join project_subtree on (edges.tail_uuid = project_subtree.uuid)
23
	))
24
	select uuid from project_subtree;
25

    
26

    
27
WITH RECURSIVE perm_value(name, val) AS (
28
         VALUES ('can_read'::text,(1)::smallint), ('can_login'::text,1), ('can_write'::text,2), ('can_manage'::text,3)
29
        ),
30
        edges(tail_uuid, head_uuid, val) as (
31
	  select groups.owner_uuid, groups.uuid, (3) from groups
32
	  union
33
	  select links.tail_uuid, links.head_uuid, pv.val from links
34
	  JOIN perm_value pv ON ((pv.name = (links.name)::text))
35
	   where links.link_class='permission'
36
	),
37
	project_subtree(target_uuid, val) as (
38
--	values ('x1u39-j7d0g-rufsjii9txtu2xg')
39
--	values ('x1u39-tpzed-fr97h9t4m5jffxs')
40
	values ('x1u39-tpzed-3kz0nwtjehhl0u4', 3)
41
	union
42
	(select edges.head_uuid, least(edges.val, project_subtree.val) from edges join project_subtree
43
	on (edges.tail_uuid = project_subtree.target_uuid)
44
	))
45
	select target_uuid, val from project_subtree;
46

    
47
create table computed_permissions (
48
user_uuid character varying (27), target_uuid character varying (27), perm_level smallint);
49
create unique index computed_permissions_index on computed_permissions (user_uuid, target_uuid);
50

    
51
-- unique index on (user, target_uuid)
52

    
53
-- Permission in model:
54
-- tail (owner_uuid)
55
-- permission level
56
-- head (or uuid of new project or group)
57

    
58
-- Adding a permission
59
-- with recursive search from head, get affected nodes
60
-- next, select (users, permission) with target_uuid = tail (or owner_uuid)
61
-- insert \forall users \forall nodes (user, node, least(user perm, node perm))
62
--   on conflict update set greatest(existing perm, new perm)
63

    
64
-- Removing a permission
65
-- with recursive search from head, get affected nodes
66
-- delete all permissions with target_uuid in affected nodes
67
-- select permission links where
68
--   head is in the affected nodes
69
--   tail is in computed permissions
70
-- re-add each link (via adding a permission, above)
71

    
72

    
73
begin;
74
create TEMPORARY table affected_perms on commit drop
75
as WITH RECURSIVE perm_value(name, val)  AS (
76
         VALUES ('can_read'::text,(1)::smallint), ('can_login'::text,1), ('can_write'::text,2), ('can_manage'::text,3)
77
        ),
78
        edges(tail_uuid, head_uuid, val) as (
79
	  select groups.owner_uuid, groups.uuid, (3) from groups
80
	  union
81
	  select links.tail_uuid, links.head_uuid, pv.val from links
82
	  JOIN perm_value pv ON ((pv.name = (links.name)::text))
83
	   where links.link_class='permission' and links.head_uuid not like '%-tpzed-%'
84
	),
85
	project_subtree(target_uuid, val) as (
86
--	values ('x1u39-j7d0g-rufsjii9txtu2xg')
87
--	values ('x1u39-tpzed-fr97h9t4m5jffxs')
88
--	values ('x1u39-j7d0g-qjt4uv6kq0ntm3y', 3)
89
	values ('x1u39-tpzed-3kz0nwtjehhl0u4', 3)
90
	union
91
	(select edges.head_uuid, least(edges.val, project_subtree.val) from edges join project_subtree
92
	on (edges.tail_uuid = project_subtree.target_uuid)))
93
	select target_uuid, val from project_subtree;
94

    
95
	select * from affected_perms;
96

    
97
	insert into computed_permissions select 'x1u39-tpzed-3kz0nwtjehhl0u4', target_uuid, val from affected_perms;
98

    
99
	delete from computed_permissions
100
	where exists
101
	(select 1 from affected_perms where
102
	 computed_permissions.user_uuid='x1u39-tpzed-3kz0nwtjehhl0u4'
103
	 and computed_permissions.target_uuid=affected_perms.target_uuid
104
	 and computed_permissions.val=affected_perms.val);
105

    
106
	create TEMPORARY table links_to_recompute on commit drop
107
	as WITH perm_value(name, val)  AS (
108
         VALUES ('can_read'::text,(1)::smallint), ('can_login'::text,1), ('can_write'::text,2), ('can_manage'::text,3)
109
        )
110
	select links.tail_uuid, links.head_uuid, pv.val from links
111
	  JOIN affected_perms on (links.head_uuid = affected_perms.target_uuid)
112
	  JOIN perm_value pv ON ((pv.name = (links.name)::text))
113
	  where links.link_class='permission';
114

    
115
	select * from links_to_recompute;
116

    
117
commit;
118

    
119
create or replace function project_subtree (starting_uuid varchar(27))
120
returns table (target_uuid varchar(27))
121
STABLE
122
language SQL
123
as $$
124
WITH RECURSIVE
125
	project_subtree(uuid) as (
126
	values (starting_uuid)
127
	union
128
	select groups.uuid from groups join project_subtree on (groups.owner_uuid = project_subtree.uuid)
129
	)
130
	select uuid from project_subtree;
131
$$;
132

    
133

    
134
create or replace function search_permission_graph (starting_uuid varchar(27), starting_perm smallint)
135
returns table (target_uuid varchar(27), val smallint)
136
STABLE
137
language SQL
138
as $$
139
WITH RECURSIVE perm_value(name, val)  AS (
140
         VALUES ('can_read'::text,(1)::smallint), ('can_login'::text,1), ('can_write'::text,2), ('can_manage'::text,3)
141
        ),
142
        edges(tail_uuid, head_uuid, val) as (
143
	  select groups.owner_uuid, groups.uuid, (3) from groups
144
	  union
145
	  select links.tail_uuid, links.head_uuid, pv.val from links
146
	  JOIN perm_value pv ON ((pv.name = (links.name)::text))
147
	   where links.link_class='permission'
148
	),
149
	project_subtree(target_uuid, val) as (
150
	values (starting_uuid, starting_perm)
151
	union
152
	(select edges.head_uuid, least(edges.val, project_subtree.val)::smallint from edges join project_subtree
153
	on (edges.tail_uuid = project_subtree.target_uuid)
154
	where edges.tail_uuid like '_____-j7d0g-_______________'
155
	     or edges.tail_uuid = starting_uuid))
156
	select target_uuid, max(val) from project_subtree
157
	group by (target_uuid) ;
158
$$;
159

    
160
create or replace function add_permission (tail_uuid varchar(27), head_uuid varchar(27), perm_value smallint)
161
returns table(user_uuid character varying (27), target_uuid character varying (27), val smallint)
162
VOLATILE
163
language SQL
164
as $$
165
with
166
  affected_nodes(target_uuid, val)
167
    as (select target_uuid, val from search_permission_graph(head_uuid, perm_value)),
168
  affected_users(user_uuid, val)
169
    as (select user_uuid, perm_level from computed_permissions where target_uuid = tail_uuid),
170
  new_perms(user_uuid, target_uuid, val)
171
    as (select affected_users.user_uuid, affected_nodes.target_uuid, least(affected_nodes.val, affected_users.val)
172
      from affected_nodes cross join affected_users)
173
  insert into computed_permissions (user_uuid, target_uuid, perm_level)
174
    (select user_uuid, target_uuid, val from new_perms)
175
    on conflict (user_uuid, target_uuid) do update set perm_level=greatest(computed_permissions.perm_level, excluded.perm_level)
176
    returning *;
177
$$;
178

    
179
create or replace function clear_permission (head_uuid varchar(27))
180
returns table(user_uuid character varying (27), target_uuid character varying (27), val smallint)
181
VOLATILE
182
language SQL
183
as $$
184
with
185
  affected_nodes(target_uuid)
186
    as (select target_uuid from search_permission_graph(head_uuid, 0::smallint))
187
  delete from computed_permissions where
188
    target_uuid in (select target_uuid from affected_nodes where target_uuid <> user_uuid)
189
    returning *;
190
$$;
191

    
192

    
193
create or replace function remove_permission_helper (starting_uuid varchar(27))
194
returns table(link_uuid character varying (27), tail_uuid character varying (27), head_uuid character varying (27), val smallint)
195
VOLATILE
196
language SQL
197
as $$
198
with perm_value(name, val)  AS (
199
    VALUES ('can_read'::text,(1)::smallint), ('can_login'::text,1), ('can_write'::text,2), ('can_manage'::text,3)),
200
  removed_rows(user_uuid, target_uuid, val) as (
201
    select user_uuid, target_uuid, val from clear_permission(starting_uuid))
202
  select links.uuid, links.tail_uuid, links.head_uuid, pv.val::smallint from links
203
      JOIN perm_value pv ON ((pv.name = (links.name)::text))
204
      where links.link_class='permission' and
205
        exists (select 1 from computed_permissions where
206
	  links.tail_uuid=computed_permissions.target_uuid) and
207
        exists (select 1 from removed_rows where links.head_uuid=removed_rows.target_uuid)
208
$$;
209

    
210
create or replace function delete_permission_link (link_uuid varchar(27), link_head_uuid varchar(27))
211
returns table(tail_uuid character varying (27), head_uuid character varying (27), val smallint)
212
VOLATILE
213
language SQL
214
as $$
215
with links_to_recompute(link_uuid, tail_uuid, head_uuid, val) as
216
    (select * from remove_permission_helper(link_head_uuid)),
217
  _delete_link as (delete from links where links.uuid=link_uuid)
218
  select ap.user_uuid, ap.target_uuid, ap.val
219
    from links_to_recompute,
220
      lateral add_permission(links_to_recompute.tail_uuid, links_to_recompute.head_uuid, links_to_recompute.val::smallint) ap
221
    where links_to_recompute.link_uuid <> link_uuid
222
$$;
223

    
224
create or replace function delete_group (group_uuid varchar(27))
225
returns table(tail_uuid character varying (27), head_uuid character varying (27), val smallint)
226
VOLATILE
227
language SQL
228
as $$
229
with links_to_recompute(link_uuid, tail_uuid, head_uuid, val) as
230
    (select * from remove_permission_helper(group_uuid)),
231
  _delete_group as (delete from groups where groups.uuid=group_uuid)
232
  select ap.user_uuid, ap.target_uuid, ap.val
233
    from links_to_recompute,
234
      lateral add_permission(links_to_recompute.tail_uuid, links_to_recompute.head_uuid, links_to_recompute.val::smallint) ap
235
$$;
236

    
237
create or replace function move_group (group_uuid varchar(27), new_owner_uuid varchar(27))
238
returns table(tail_uuid character varying (27), head_uuid character varying (27), val smallint)
239
VOLATILE
240
language SQL
241
as $$
242
with links_to_recompute(link_uuid, tail_uuid, head_uuid, val) as
243
    (values ('', new_owner_uuid, group_uuid, 3::smallint)
244
    union
245
    select link_uuid, tail_uuid, head_uuid, val from remove_permission_helper(group_uuid)),
246
  update_group as (update groups set owner_uuid=new_owner_uuid where groups.uuid=group_uuid)
247
  select ap.user_uuid, ap.target_uuid, ap.val
248
    from links_to_recompute,
249
      lateral add_permission(links_to_recompute.tail_uuid, links_to_recompute.head_uuid, links_to_recompute.val::smallint) ap
250
$$;
251

    
252

    
253

    
254
insert into computed_permissions (user_uuid, target_uuid, perm_level) values ('x1u39-tpzed-3kz0nwtjehhl0u4', 'x1u39-tpzed-3kz0nwtjehhl0u4', 3);
255

    
256
select * from add_permission('x1u39-tpzed-3kz0nwtjehhl0u4', 'x1u39-tpzed-3kz0nwtjehhl0u4', 3::smallint);
257

    
258
-- x1u39-j7d0g-sh5mwu9t8b71al6 -> Project under old user (owned by x1u39-j7d0g-7mj64tj4fe3snmo)
259
-- x1u39-j7d0g-qjt4uv6kq0ntm3y -> Foghord (can_write x1u39-j7d0g-7mj64tj4fe3snmo) (can_read x1u39-tpzed-3kz0nwtjehhl0u4)
260
-- x1u39-j7d0g-sij00wivb8v3hkt -> Thinger holder (owned by x1u39-tpzed-3kz0nwtjehhl0u4)
261
-- x1u39-j7d0g-7mj64tj4fe3snmo -> Data from old user
262
-- x1u39-tpzed-3kz0nwtjehhl0u4 -> c c (can_manage x1u39-j7d0g-qjt4uv6kq0ntm3y)
(1-1/2)