2 * Copyright (C) 2004-2008 Georgia Public Library Service
3 * Copyright (C) 2008 Equinox Software, Inc.
4 * Mike Rylander <miker@esilibrary.com>
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
19 DROP SCHEMA IF EXISTS permission CASCADE;
22 CREATE SCHEMA permission;
24 CREATE TABLE permission.perm_list (
25 id SERIAL PRIMARY KEY,
26 code TEXT NOT NULL UNIQUE,
29 CREATE INDEX perm_list_code_idx ON permission.perm_list (code);
30 CREATE TRIGGER maintain_perm_i18n_tgr
31 AFTER UPDATE ON permission.perm_list
32 FOR EACH ROW EXECUTE PROCEDURE oils_i18n_id_tracking('ppl');
34 CREATE TABLE permission.grp_tree (
35 id SERIAL PRIMARY KEY,
36 name TEXT NOT NULL UNIQUE,
37 parent INT REFERENCES permission.grp_tree (id) ON DELETE RESTRICT DEFERRABLE INITIALLY DEFERRED,
38 usergroup BOOL NOT NULL DEFAULT TRUE,
39 perm_interval INTERVAL DEFAULT '3 years'::interval NOT NULL,
43 CREATE INDEX grp_tree_parent_idx ON permission.grp_tree (parent);
45 CREATE TABLE permission.grp_penalty_threshold (
46 id SERIAL PRIMARY KEY,
47 grp INT NOT NULL REFERENCES permission.grp_tree (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
48 org_unit INT NOT NULL REFERENCES actor.org_unit (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
49 penalty INT NOT NULL REFERENCES config.standing_penalty (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
50 threshold NUMERIC(8,2) NOT NULL,
51 CONSTRAINT penalty_grp_once UNIQUE (grp,penalty,org_unit)
54 CREATE TABLE permission.grp_perm_map (
55 id SERIAL PRIMARY KEY,
56 grp INT NOT NULL REFERENCES permission.grp_tree (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
57 perm INT NOT NULL REFERENCES permission.perm_list (id) ON UPDATE CASCADE ON DELETE RESTRICT DEFERRABLE INITIALLY DEFERRED,
59 grantable BOOL NOT NULL DEFAULT FALSE,
60 CONSTRAINT perm_grp_once UNIQUE (grp,perm)
63 CREATE TABLE permission.usr_perm_map (
64 id SERIAL PRIMARY KEY,
65 usr INT NOT NULL REFERENCES actor.usr (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
66 perm INT NOT NULL REFERENCES permission.perm_list (id) ON UPDATE CASCADE ON DELETE RESTRICT DEFERRABLE INITIALLY DEFERRED,
68 grantable BOOL NOT NULL DEFAULT FALSE,
69 CONSTRAINT perm_usr_once UNIQUE (usr,perm)
72 CREATE TABLE permission.usr_object_perm_map (
73 id SERIAL PRIMARY KEY,
74 usr INT NOT NULL REFERENCES actor.usr (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
75 perm INT NOT NULL REFERENCES permission.perm_list (id) ON UPDATE CASCADE ON DELETE RESTRICT DEFERRABLE INITIALLY DEFERRED,
76 object_type TEXT NOT NULL,
77 object_id TEXT NOT NULL,
78 grantable BOOL NOT NULL DEFAULT FALSE,
79 CONSTRAINT perm_usr_obj_once UNIQUE (usr,perm,object_type,object_id)
82 CREATE INDEX uopm_usr_idx ON permission.usr_object_perm_map (usr);
84 CREATE TABLE permission.usr_grp_map (
85 id SERIAL PRIMARY KEY,
86 usr INT NOT NULL REFERENCES actor.usr (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
87 grp INT NOT NULL REFERENCES permission.grp_tree (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
88 CONSTRAINT usr_grp_once UNIQUE (usr,grp)
91 CREATE OR REPLACE FUNCTION permission.grp_ancestors ( INT ) RETURNS SETOF permission.grp_tree AS $$
93 FROM connectby('permission.grp_tree'::text,'parent'::text,'id'::text,'name'::text,$1::text,100,'.'::text)
94 AS t(keyid text, parent_keyid text, level int, branch text,pos int)
95 JOIN permission.grp_tree a ON a.id::text = t.keyid::text
97 CASE WHEN a.parent IS NULL
101 $$ LANGUAGE SQL STABLE;
103 CREATE OR REPLACE FUNCTION permission.usr_perms ( INT ) RETURNS SETOF permission.usr_perm_map AS $$
104 SELECT DISTINCT ON (usr,perm) *
106 (SELECT * FROM permission.usr_perm_map WHERE usr = $1)
108 (SELECT -p.id, $1 AS usr, p.perm, p.depth, p.grantable
109 FROM permission.grp_perm_map p
111 SELECT (permission.grp_ancestors(
112 (SELECT profile FROM actor.usr WHERE id = $1)
117 (SELECT -p.id, $1 AS usr, p.perm, p.depth, p.grantable
118 FROM permission.grp_perm_map p
119 WHERE p.grp IN (SELECT (permission.grp_ancestors(m.grp)).id FROM permission.usr_grp_map m WHERE usr = $1))
121 ORDER BY 2, 3, 1 DESC, 5 DESC ;
122 $$ LANGUAGE SQL STABLE;
124 CREATE TABLE permission.usr_work_ou_map (
125 id SERIAL PRIMARY KEY,
126 usr INT NOT NULL REFERENCES actor.usr (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
127 work_ou INT NOT NULL REFERENCES actor.org_unit (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
128 CONSTRAINT usr_work_ou_once UNIQUE (usr,work_ou)
131 CREATE OR REPLACE FUNCTION permission.usr_can_grant_perm ( iuser INT, tperm TEXT, target_ou INT ) RETURNS BOOL AS $$
133 r_usr actor.usr%ROWTYPE;
134 r_perm permission.usr_perm_map%ROWTYPE;
137 SELECT * INTO r_usr FROM actor.usr WHERE id = iuser;
139 IF r_usr.active = FALSE THEN
143 IF r_usr.super_user = TRUE THEN
147 FOR r_perm IN SELECT *
148 FROM permission.usr_perms(iuser) p
149 JOIN permission.perm_list l
151 WHERE (l.code = tperm AND p.grantable IS TRUE)
155 FROM actor.org_unit_descendants(target_ou,r_perm.depth)
156 WHERE id = r_usr.home_ou;
169 CREATE OR REPLACE FUNCTION permission.usr_has_home_perm ( iuser INT, tperm TEXT, target_ou INT ) RETURNS BOOL AS $$
171 r_usr actor.usr%ROWTYPE;
172 r_perm permission.usr_perm_map%ROWTYPE;
175 SELECT * INTO r_usr FROM actor.usr WHERE id = iuser;
177 IF r_usr.active = FALSE THEN
181 IF r_usr.super_user = TRUE THEN
185 FOR r_perm IN SELECT *
186 FROM permission.usr_perms(iuser) p
187 JOIN permission.perm_list l
193 FROM actor.org_unit_descendants(target_ou,r_perm.depth)
194 WHERE id = r_usr.home_ou;
207 CREATE OR REPLACE FUNCTION permission.usr_has_work_perm ( iuser INT, tperm TEXT, target_ou INT ) RETURNS BOOL AS $$
209 r_woum permission.usr_work_ou_map%ROWTYPE;
210 r_usr actor.usr%ROWTYPE;
211 r_perm permission.usr_perm_map%ROWTYPE;
214 SELECT * INTO r_usr FROM actor.usr WHERE id = iuser;
216 IF r_usr.active = FALSE THEN
220 IF r_usr.super_user = TRUE THEN
224 FOR r_perm IN SELECT *
225 FROM permission.usr_perms(iuser) p
226 JOIN permission.perm_list l
232 FOR r_woum IN SELECT *
233 FROM permission.usr_work_ou_map
238 FROM actor.org_unit_descendants(target_ou,r_perm.depth)
239 WHERE id = r_woum.work_ou;
253 CREATE OR REPLACE FUNCTION permission.usr_has_object_perm ( iuser INT, tperm TEXT, obj_type TEXT, obj_id TEXT, target_ou INT ) RETURNS BOOL AS $$
255 r_usr actor.usr%ROWTYPE;
259 SELECT * INTO r_usr FROM actor.usr WHERE id = iuser;
261 IF r_usr.active = FALSE THEN
265 IF r_usr.super_user = TRUE THEN
269 SELECT TRUE INTO res FROM permission.usr_object_perm_map WHERE usr = r_usr.id AND object_type = obj_type AND object_id = obj_id;
275 IF target_ou > -1 THEN
276 RETURN permission.usr_has_perm( iuser, tperm, target_ou);
284 CREATE OR REPLACE FUNCTION permission.usr_has_object_perm ( INT, TEXT, TEXT, TEXT ) RETURNS BOOL AS $$
285 SELECT permission.usr_has_object_perm( $1, $2, $3, $4, -1 );
288 CREATE OR REPLACE FUNCTION permission.usr_has_perm ( INT, TEXT, INT ) RETURNS BOOL AS $$
290 WHEN permission.usr_has_home_perm( $1, $2, $3 ) THEN TRUE
291 WHEN permission.usr_has_work_perm( $1, $2, $3 ) THEN TRUE
297 CREATE OR REPLACE FUNCTION permission.usr_has_perm_at_nd(
301 RETURNS SETOF INTEGER AS $$
303 -- Return a set of all the org units for which a given user has a given
304 -- permission, granted directly (not through inheritance from a parent
307 -- The permissions apply to a minimum depth of the org unit hierarchy,
308 -- for the org unit(s) to which the user is assigned. (They also apply
309 -- to the subordinates of those org units, but we don't report the
310 -- subordinates here.)
312 -- For purposes of this function, the permission.usr_work_ou_map table
313 -- defines which users belong to which org units. I.e. we ignore the
314 -- home_ou column of actor.usr.
316 -- The result set may contain duplicates, which should be eliminated
317 -- by a DISTINCT clause.
326 n_curr_depth INTEGER;
329 -- Check for superuser
339 return; -- No user? No permissions.
342 -- Super user has all permissions everywhere
352 RETURN NEXT n_work_ou;
357 -- Translate the permission name
358 -- to a numeric permission id
368 RETURN; -- No such permission
371 -- Find the highest-level org unit (i.e. the minimum depth)
372 -- to which the permission is applied for this user
374 -- This query is modified from the one in permission.usr_perms().
376 SELECT INTO n_min_depth
380 FROM permission.usr_perm_map upm
381 WHERE upm.usr = user_id
382 AND (upm.perm = n_perm OR upm.perm = -1)
385 FROM permission.grp_perm_map gpm
386 WHERE (gpm.perm = n_perm OR gpm.perm = -1)
388 SELECT (permission.grp_ancestors(
389 (SELECT profile FROM actor.usr WHERE id = user_id)
394 FROM permission.grp_perm_map p
395 WHERE (p.perm = n_perm OR p.perm = -1)
397 SELECT (permission.grp_ancestors(m.grp)).id
398 FROM permission.usr_grp_map m
399 WHERE m.usr = user_id
404 RETURN; -- No such permission for this user
407 -- Identify the org units to which the user is assigned. Note that
408 -- we pay no attention to the home_ou column in actor.usr.
414 permission.usr_work_ou_map
417 LOOP -- For each org unit to which the user is assigned
419 -- Determine the level of the org unit by a lookup in actor.org_unit_type.
420 -- We take it on faith that this depth agrees with the actual hierarchy
421 -- defined in actor.org_unit.
426 actor.org_unit_type type
427 INNER JOIN actor.org_unit ou
428 ON ( ou.ou_type = type.id )
433 CONTINUE; -- Maybe raise exception?
436 -- Compare the depth of the work org unit to the
437 -- minimum depth, and branch accordingly
439 IF n_depth = n_min_depth THEN
441 -- The org unit is at the right depth, so return it.
443 RETURN NEXT n_work_ou;
444 ELSIF n_depth > n_min_depth THEN
446 -- Traverse the org unit tree toward the root,
447 -- until you reach the minimum depth determined above
449 n_curr_depth := n_depth;
450 n_curr_ou := n_work_ou;
451 WHILE n_curr_depth > n_min_depth LOOP
452 SELECT INTO n_curr_ou
460 n_curr_depth := n_curr_depth - 1;
463 -- This can happen only if the hierarchy defined in
464 -- actor.org_unit is corrupted, or out of sync with
465 -- the depths defined in actor.org_unit_type.
466 -- Maybe we should raise an exception here, instead
467 -- of silently ignoring the problem.
474 IF n_curr_ou IS NOT NULL THEN
475 RETURN NEXT n_curr_ou;
479 -- The permission applies only at a depth greater than the work org unit.
480 -- Use connectby() to find all dependent org units at the specified depth.
485 'actor.org_unit', -- table name
487 'parent_ou', -- recursive foreign key
488 n_work_ou::TEXT, -- id of starting point
489 (n_min_depth - n_depth) -- max depth to search, relative
490 ) -- to starting point
492 ou text, -- dependent org unit
493 parent_ou text, -- (ignore)
494 level int -- depth relative to starting point
497 level = n_min_depth - n_depth
499 RETURN NEXT n_curr_ou;
508 $$ LANGUAGE 'plpgsql';
511 CREATE OR REPLACE FUNCTION permission.usr_has_perm_at_all_nd(
515 RETURNS SETOF INTEGER AS $$
517 -- Return a set of all the org units for which a given user has a given
518 -- permission, granted either directly or through inheritance from a parent
521 -- The permissions apply to a minimum depth of the org unit hierarchy, and
522 -- to the subordinates of those org units, for the org unit(s) to which the
525 -- For purposes of this function, the permission.usr_work_ou_map table
526 -- assigns users to org units. I.e. we ignore the home_ou column of actor.usr.
528 -- The result set may contain duplicates, which should be eliminated
529 -- by a DISTINCT clause.
536 SELECT DISTINCT * FROM permission.usr_has_perm_at_nd( user_id, perm_code )
539 -- The permission applies only at a depth greater than the work org unit.
540 -- Use connectby() to find all dependent org units at the specified depth.
545 'actor.org_unit', -- table name
547 'parent_ou', -- recursive foreign key
548 n_head_ou::TEXT, -- id of starting point
549 0 -- no limit on search depth
552 ou text, -- dependent org unit
553 parent_ou text, -- (ignore)
554 level int -- (ignore)
557 RETURN NEXT n_child_ou;
564 $$ LANGUAGE 'plpgsql';
567 CREATE OR REPLACE FUNCTION permission.usr_has_perm_at(
571 RETURNS SETOF INTEGER AS $$
572 SELECT DISTINCT * FROM permission.usr_has_perm_at_nd( $1, $2 );
576 CREATE OR REPLACE FUNCTION permission.usr_has_perm_at_all(
580 RETURNS SETOF INTEGER AS $$
581 SELECT DISTINCT * FROM permission.usr_has_perm_at_all_nd( $1, $2 );