12032: Use permission_view in subquery to filter objects readable by user.
[arvados.git] / services / api / lib / create_permission_view.sql
1 -- Copyright (C) The Arvados Authors. All rights reserved.
2 --
3 -- SPDX-License-Identifier: AGPL-3.0
4
5 -- constructing perm_edges
6 --   1. get the list of all permission links,
7 --   2. any can_manage link or permission link to a group means permission should "follow through"
8 --      (as a special case, can_manage links to a user grant access to everything owned by the user,
9 --       unlike can_read or can_write which only grant access to the user record)
10 --   3. add all owner->owned relationships between groups as can_manage edges
11 --
12 -- constructing permissions
13 --   1. base case: start with set of all users as the working set
14 --   2. recursive case:
15 --      join with edges where the tail is in the working set and "follow" is true
16 --      produce a new working set with the head (target) of each edge
17 --      set permission to the least permission encountered on the path
18 --      propagate trashed flag down
19
20 CREATE TEMPORARY VIEW permission_view AS
21 WITH RECURSIVE
22 perm_value (name, val) AS (
23      VALUES
24      ('can_read',   1::smallint),
25      ('can_login',  1),
26      ('can_write',  2),
27      ('can_manage', 3)
28      ),
29 perm_edges (tail_uuid, head_uuid, val, follow, trashed) AS (
30        SELECT links.tail_uuid,
31               links.head_uuid,
32               pv.val,
33               (pv.val = 3 OR groups.uuid IS NOT NULL) AS follow,
34               0::smallint AS trashed
35               FROM links
36               LEFT JOIN perm_value pv ON pv.name = links.name
37               LEFT JOIN groups ON pv.val<3 AND groups.uuid = links.head_uuid
38               WHERE links.link_class = 'permission'
39        UNION ALL
40        SELECT owner_uuid, uuid, 3, true, 0::smallint FROM groups
41        ),
42 perm (val, follow, user_uuid, target_uuid, trashed, startnode) AS (
43      SELECT 3::smallint             AS val,
44             false                   AS follow,
45             users.uuid::varchar(32) AS user_uuid,
46             users.uuid::varchar(32) AS target_uuid,
47             0::smallint             AS trashed,
48             true                    AS startnode
49             FROM users
50      UNION
51      SELECT LEAST(perm.val, edges.val)::smallint  AS val,
52             edges.follow                          AS follow,
53             perm.user_uuid::varchar(32)           AS user_uuid,
54             edges.head_uuid::varchar(32)          AS target_uuid,
55             GREATEST(perm.trashed, edges.trashed)::smallint AS trashed,
56             false                                 AS startnode
57             FROM perm
58             INNER JOIN perm_edges edges
59             ON (perm.startnode or perm.follow) AND edges.tail_uuid = perm.target_uuid
60 )
61 SELECT user_uuid,
62        target_uuid,
63        val AS perm_level,
64        CASE follow WHEN true THEN target_uuid ELSE NULL END AS target_owner_uuid,
65        trashed
66        FROM perm;