]> git.evergreen-ils.org Git - working/Evergreen.git/blob - Open-ILS/src/sql/Pg/upgrade/0175.schema.permission.usr_has_perm_at_nd-EVERYTHING.sql
LP#1178377: Make bib source optional element from unapi.bre
[working/Evergreen.git] / Open-ILS / src / sql / Pg / upgrade / 0175.schema.permission.usr_has_perm_at_nd-EVERYTHING.sql
1
2 BEGIN;
3
4 INSERT INTO config.upgrade_log (version) VALUES ('0175'); -- From patch by Jason Stephenson (applied by miker)
5
6 CREATE OR REPLACE FUNCTION permission.usr_has_perm_at_nd(
7     user_id    IN INTEGER,
8     perm_code  IN TEXT
9 )
10 RETURNS SETOF INTEGER AS $$
11 --
12 -- Return a set of all the org units for which a given user has a given
13 -- permission, granted directly (not through inheritance from a parent
14 -- org unit).
15 --
16 -- The permissions apply to a minimum depth of the org unit hierarchy,
17 -- for the org unit(s) to which the user is assigned.  (They also apply
18 -- to the subordinates of those org units, but we don't report the
19 -- subordinates here.)
20 --
21 -- For purposes of this function, the permission.usr_work_ou_map table
22 -- defines which users belong to which org units.  I.e. we ignore the
23 -- home_ou column of actor.usr.
24 --
25 -- The result set may contain duplicates, which should be eliminated
26 -- by a DISTINCT clause.
27 --
28 DECLARE
29     b_super       BOOLEAN;
30     n_perm        INTEGER;
31     n_min_depth   INTEGER;
32     n_work_ou     INTEGER;
33     n_curr_ou     INTEGER;
34     n_depth       INTEGER;
35     n_curr_depth  INTEGER;
36 BEGIN
37     --
38     -- Check for superuser
39     --
40     SELECT INTO b_super
41         super_user
42     FROM
43         actor.usr
44     WHERE
45         id = user_id;
46     --
47     IF NOT FOUND THEN
48         return;             -- No user?  No permissions.
49     ELSIF b_super THEN
50         --
51         -- Super user has all permissions everywhere
52         --
53         FOR n_work_ou IN
54             SELECT
55                 id
56             FROM
57                 actor.org_unit
58             WHERE
59                 parent_ou IS NULL
60         LOOP
61             RETURN NEXT n_work_ou;
62         END LOOP;
63         RETURN;
64     END IF;
65     --
66     -- Translate the permission name
67     -- to a numeric permission id
68     --
69     SELECT INTO n_perm
70         id
71     FROM
72         permission.perm_list
73     WHERE
74         code = perm_code;
75     --
76     IF NOT FOUND THEN
77         RETURN;               -- No such permission
78     END IF;
79     --
80     -- Find the highest-level org unit (i.e. the minimum depth)
81     -- to which the permission is applied for this user
82     --
83     -- This query is modified from the one in permission.usr_perms().
84     --
85     SELECT INTO n_min_depth
86         min( depth )
87     FROM    (
88         SELECT depth
89           FROM permission.usr_perm_map upm
90          WHERE upm.usr = user_id
91            AND (upm.perm = n_perm OR upm.perm = -1)
92                     UNION
93         SELECT  gpm.depth
94           FROM  permission.grp_perm_map gpm
95           WHERE (gpm.perm = n_perm OR gpm.perm = -1)
96             AND gpm.grp IN (
97                SELECT   (permission.grp_ancestors(
98                     (SELECT profile FROM actor.usr WHERE id = user_id)
99                 )).id
100             )
101                     UNION
102         SELECT  p.depth
103           FROM  permission.grp_perm_map p
104           WHERE (p.perm = n_perm OR p.perm = -1)
105             AND p.grp IN (
106                 SELECT (permission.grp_ancestors(m.grp)).id
107                 FROM   permission.usr_grp_map m
108                 WHERE  m.usr = user_id
109             )
110     ) AS x;
111     --
112     IF NOT FOUND THEN
113         RETURN;                -- No such permission for this user
114     END IF;
115     --
116     -- Identify the org units to which the user is assigned.  Note that
117     -- we pay no attention to the home_ou column in actor.usr.
118     --
119     FOR n_work_ou IN
120         SELECT
121             work_ou
122         FROM
123             permission.usr_work_ou_map
124         WHERE
125             usr = user_id
126     LOOP            -- For each org unit to which the user is assigned
127         --
128         -- Determine the level of the org unit by a lookup in actor.org_unit_type.
129         -- We take it on faith that this depth agrees with the actual hierarchy
130         -- defined in actor.org_unit.
131         --
132         SELECT INTO n_depth
133             type.depth
134         FROM
135             actor.org_unit_type type
136                 INNER JOIN actor.org_unit ou
137                     ON ( ou.ou_type = type.id )
138         WHERE
139             ou.id = n_work_ou;
140         --
141         IF NOT FOUND THEN
142             CONTINUE;        -- Maybe raise exception?
143         END IF;
144         --
145         -- Compare the depth of the work org unit to the
146         -- minimum depth, and branch accordingly
147         --
148         IF n_depth = n_min_depth THEN
149             --
150             -- The org unit is at the right depth, so return it.
151             --
152             RETURN NEXT n_work_ou;
153         ELSIF n_depth > n_min_depth THEN
154             --
155             -- Traverse the org unit tree toward the root,
156             -- until you reach the minimum depth determined above
157             --
158             n_curr_depth := n_depth;
159             n_curr_ou := n_work_ou;
160             WHILE n_curr_depth > n_min_depth LOOP
161                 SELECT INTO n_curr_ou
162                     parent_ou
163                 FROM
164                     actor.org_unit
165                 WHERE
166                     id = n_curr_ou;
167                 --
168                 IF FOUND THEN
169                     n_curr_depth := n_curr_depth - 1;
170                 ELSE
171                     --
172                     -- This can happen only if the hierarchy defined in
173                     -- actor.org_unit is corrupted, or out of sync with
174                     -- the depths defined in actor.org_unit_type.
175                     -- Maybe we should raise an exception here, instead
176                     -- of silently ignoring the problem.
177                     --
178                     n_curr_ou = NULL;
179                     EXIT;
180                 END IF;
181             END LOOP;
182             --
183             IF n_curr_ou IS NOT NULL THEN
184                 RETURN NEXT n_curr_ou;
185             END IF;
186         ELSE
187             --
188             -- The permission applies only at a depth greater than the work org unit.
189             -- Use connectby() to find all dependent org units at the specified depth.
190             --
191             FOR n_curr_ou IN
192                 SELECT ou::INTEGER
193                 FROM connectby(
194                         'actor.org_unit',         -- table name
195                         'id',                     -- key column
196                         'parent_ou',              -- recursive foreign key
197                         n_work_ou::TEXT,          -- id of starting point
198                         (n_min_depth - n_depth)   -- max depth to search, relative
199                     )                             --   to starting point
200                     AS t(
201                         ou text,            -- dependent org unit
202                         parent_ou text,     -- (ignore)
203                         level int           -- depth relative to starting point
204                     )
205                 WHERE
206                     level = n_min_depth - n_depth
207             LOOP
208                 RETURN NEXT n_curr_ou;
209             END LOOP;
210         END IF;
211         --
212     END LOOP;
213     --
214     RETURN;
215     --
216 END;
217 $$ LANGUAGE 'plpgsql';
218
219 COMMIT;
220