1 CREATE or REPLACE FUNCTION legacy_privilege_to_bits( TEXT ) RETURNS BIT(24) AS $$
6 out_bits := 0::BIT(24);
12 -- The CALDAV:read-free-busy privilege MUST be aggregated in the DAV:read privilege.
14 -- 512 CalDAV:read-free-busy
15 -- 4096 CALDAV:schedule-query-freebusy
16 IF in_priv ~* 'R' THEN
17 out_bits := out_bits | 4609::BIT(24);
20 -- DAV:write => DAV:write MUST contain DAV:bind, DAV:unbind, DAV:write-properties and DAV:write-content
21 -- 2 DAV:write-properties
22 -- 4 DAV:write-content
25 IF in_priv ~* 'W' THEN
26 out_bits := out_bits | 198::BIT(24);
30 IF in_priv ~* 'B' THEN
31 out_bits := out_bits | 64::BIT(24);
35 IF in_priv ~* 'U' THEN
36 out_bits := out_bits | 128::BIT(24);
39 -- 512 CalDAV:read-free-busy
40 -- 4096 CALDAV:schedule-query-freebusy
41 IF in_priv ~* 'F' THEN
42 out_bits := out_bits | 4608::BIT(24);
48 LANGUAGE 'plpgsql' IMMUTABLE STRICT;
50 -- This legacy conversion function will eventually be removed, once all logic
51 -- has been converted to use bitmaps, or to use the bits_to_priv() output.
53 -- NOTE: Round-trip through this and then back through legacy_privilege_to_bits
54 -- function is lossy! Through legacy_privilege_to_bits() and back through
57 CREATE or REPLACE FUNCTION bits_to_legacy_privilege( BIT(24) ) RETURNS TEXT AS $$
63 IF in_bits = (~ 0::BIT(24)) THEN
68 -- The CALDAV:read-free-busy privilege MUST be aggregated in the DAV:read privilege.
70 -- 512 CalDAV:read-free-busy
71 -- 4096 CALDAV:schedule-query-freebusy
72 IF (in_bits & 4609::BIT(24)) != 0::BIT(24) THEN
73 IF (in_bits & 1::BIT(24)) != 0::BIT(24) THEN
80 -- DAV:write => DAV:write MUST contain DAV:bind, DAV:unbind, DAV:write-properties and DAV:write-content
81 -- 2 DAV:write-properties
82 -- 4 DAV:write-content
85 IF (in_bits & 198::BIT(24)) != 0::BIT(24) THEN
86 IF (in_bits & 6::BIT(24)) != 0::BIT(24) THEN
87 out_priv := out_priv || 'W';
89 IF (in_bits & 64::BIT(24)) != 0::BIT(24) THEN
90 out_priv := out_priv || 'B';
92 IF (in_bits & 128::BIT(24)) != 0::BIT(24) THEN
93 out_priv := out_priv || 'U';
101 LANGUAGE 'plpgsql' IMMUTABLE STRICT;
103 CREATE or REPLACE FUNCTION get_permissions( INT, INT ) RETURNS TEXT AS $$
105 in_from ALIAS FOR $1;
115 -- Self can always have full access
116 IF in_from = in_to THEN
121 SELECT bits_to_legacy_privilege(r1.confers) INTO out_confers FROM relationship r1
122 WHERE r1.from_user = in_from AND r1.to_user = in_to AND NOT usr_is_role(r1.to_user,'Group');
124 RETURN dbg || out_confers;
126 -- RAISE NOTICE 'No simple relationships between % and %', in_from, in_to;
128 SELECT bit_or(r1.confers & r2.confers) INTO bit_confers
130 JOIN relationship r2 ON r1.to_user=r2.from_user
131 WHERE r1.from_user=in_from AND r2.to_user=in_to
132 AND r2.from_user IN (SELECT user_no FROM roles LEFT JOIN role_member USING(role_no) WHERE role_name='Group');
133 IF bit_confers != 0::BIT(24) THEN
134 RETURN dbg || bits_to_legacy_privilege(bit_confers);
138 -- RAISE NOTICE 'No complex relationships between % and %', in_from, in_to;
140 SELECT bits_to_legacy_privilege(r1.confers) INTO out_confers FROM relationship r1 LEFT OUTER JOIN relationship r2 ON(r1.to_user = r2.to_user)
141 WHERE r1.from_user = in_from AND r2.from_user = in_to AND r1.from_user != r2.from_user
142 AND NOT EXISTS( SELECT 1 FROM relationship r3 WHERE r3.from_user = r1.to_user ) ;
146 -- RAISE NOTICE 'Permissions to shared group % ', out_confers;
147 RETURN dbg || out_confers;
150 -- RAISE NOTICE 'No common group relationships between % and %', in_from, in_to;
154 $$ LANGUAGE 'plpgsql' IMMUTABLE STRICT;
157 CREATE or REPLACE FUNCTION get_group_role_no() RETURNS INT AS $$
158 SELECT role_no FROM roles WHERE role_name = 'Group'
159 $$ LANGUAGE 'sql' IMMUTABLE;
161 CREATE or REPLACE FUNCTION has_legacy_privilege( INT, TEXT, INT ) RETURNS BOOLEAN AS $$
163 in_from ALIAS FOR $1;
164 in_legacy_privilege ALIAS FOR $2;
169 -- Self can always have full access
170 IF in_from = in_to THEN
174 SELECT get_group_role_no() INTO group_role_no;
175 SELECT legacy_privilege_to_bits(in_legacy_privilege) INTO in_confers;
177 IF EXISTS(SELECT 1 FROM relationship WHERE from_user = in_from AND to_user = in_to
178 AND (in_confers & confers) = in_confers
179 AND NOT EXISTS(SELECT 1 FROM role_member WHERE to_user = user_no AND role_no = group_role_no) ) THEN
180 -- A direct relationship from A to B that grants sufficient
181 -- RAISE NOTICE 'Permissions directly granted';
185 IF EXISTS( SELECT 1 FROM relationship r1 JOIN relationship r2 ON r1.to_user=r2.from_user
186 WHERE (in_confers & r1.confers & r2.confers) = in_confers
187 AND r1.from_user=in_from AND r2.to_user=in_to
188 AND r2.from_user IN (SELECT user_no FROM role_member WHERE role_no=group_role_no) ) THEN
189 -- An indirect relationship from A to B via group G that grants sufficient
190 -- RAISE NOTICE 'Permissions mediated via group';
194 IF EXISTS( SELECT 1 FROM relationship r1 JOIN relationship r2 ON r1.to_user=r2.to_user
195 WHERE (in_confers & r1.confers & r2.confers) = in_confers
196 AND r1.from_user=in_from AND r2.from_user=in_to
197 AND r2.to_user IN (SELECT user_no FROM role_member WHERE role_no=group_role_no)
198 AND NOT EXISTS(SELECT 1 FROM relationship WHERE from_user=r2.to_user) ) THEN
199 -- An indirect reflexive relationship from both A & B to group G which grants sufficient
200 -- RAISE NOTICE 'Permissions to shared group';
204 -- RAISE NOTICE 'No common group relationships between % and %', in_from, in_to;
208 $$ LANGUAGE 'plpgsql' IMMUTABLE STRICT;
211 -- Given a verbose DAV: or CalDAV: privilege name return the bitmask
212 CREATE or REPLACE FUNCTION privilege_to_bits( TEXT ) RETURNS BIT(24) AS $$
214 raw_priv ALIAS FOR $1;
217 in_priv := trim(lower(regexp_replace(raw_priv, '^.*:', '')));
218 IF in_priv = 'all' THEN
223 WHEN in_priv = 'read' THEN 4609 -- 1 + 512 + 4096
224 WHEN in_priv = 'write' THEN 198 -- 2 + 4 + 64 + 128
225 WHEN in_priv = 'write-properties' THEN 2
226 WHEN in_priv = 'write-content' THEN 4
227 WHEN in_priv = 'unlock' THEN 8
228 WHEN in_priv = 'read-acl' THEN 16
229 WHEN in_priv = 'read-current-user-privilege-set' THEN 32
230 WHEN in_priv = 'bind' THEN 64
231 WHEN in_priv = 'unbind' THEN 128
232 WHEN in_priv = 'write-acl' THEN 256
233 WHEN in_priv = 'read-free-busy' THEN 4608 -- 512 + 4096
234 WHEN in_priv = 'schedule-deliver' THEN 7168 -- 1024 + 2048 + 4096
235 WHEN in_priv = 'schedule-deliver-invite' THEN 1024
236 WHEN in_priv = 'schedule-deliver-reply' THEN 2048
237 WHEN in_priv = 'schedule-query-freebusy' THEN 4096
238 WHEN in_priv = 'schedule-send' THEN 57344 -- 8192 + 16384 + 32768
239 WHEN in_priv = 'schedule-send-invite' THEN 8192
240 WHEN in_priv = 'schedule-send-reply' THEN 16384
241 WHEN in_priv = 'schedule-send-freebusy' THEN 32768
242 ELSE 0 END)::BIT(24);
245 LANGUAGE 'plpgsql' IMMUTABLE STRICT;
248 -- Given an array of verbose DAV: or CalDAV: privilege names return the bitmask
249 CREATE or REPLACE FUNCTION privilege_to_bits( TEXT[] ) RETURNS BIT(24) AS $$
251 raw_privs ALIAS FOR $1;
259 out_bits := 0::BIT(24);
261 SELECT array_lower(raw_privs,1) INTO start;
262 SELECT array_upper(raw_privs,1) INTO finish;
263 FOR i IN start .. finish LOOP
264 SELECT out_bits | privilege_to_bits(raw_privs[i]) INTO out_bits;
265 IF out_bits = all THEN
272 LANGUAGE 'plpgsql' IMMUTABLE STRICT;
275 -- This legacy conversion function will eventually be removed, once all logic
276 -- has been converted to use bitmaps, or to use the bits_to_priv() output.
278 -- NOTE: Round-trip through this and then back through privilege_to_bits
279 -- function is lossy! Through privilege_to_bits() and back through
282 CREATE or REPLACE FUNCTION bits_to_privilege( BIT(24) ) RETURNS TEXT[] AS $$
284 in_bits ALIAS FOR $1;
287 out_priv := ARRAY[]::text[];
288 IF in_bits = (~ 0::BIT(24)) THEN
289 out_priv := out_priv || ARRAY['DAV:all'];
292 IF (in_bits & 513::BIT(24)) != 0::BIT(24) THEN
293 IF (in_bits & 1::BIT(24)) != 0::BIT(24) THEN
294 out_priv := out_priv || ARRAY['DAV:read'];
296 IF (in_bits & 512::BIT(24)) != 0::BIT(24) THEN
297 out_priv := out_priv || ARRAY['caldav:read-free-busy'];
301 IF (in_bits & 198::BIT(24)) != 0::BIT(24) THEN
302 IF (in_bits & 198::BIT(24)) = 198::BIT(24) THEN
303 out_priv := out_priv || ARRAY['DAV:write'];
305 IF (in_bits & 2::BIT(24)) != 0::BIT(24) THEN
306 out_priv := out_priv || ARRAY['DAV:write-properties'];
308 IF (in_bits & 4::BIT(24)) != 0::BIT(24) THEN
309 out_priv := out_priv || ARRAY['DAV:write-content'];
311 IF (in_bits & 64::BIT(24)) != 0::BIT(24) THEN
312 out_priv := out_priv || ARRAY['DAV:bind'];
314 IF (in_bits & 128::BIT(24)) != 0::BIT(24) THEN
315 out_priv := out_priv || ARRAY['DAV:unbind'];
320 IF (in_bits & 8::BIT(24)) != 0::BIT(24) THEN
321 out_priv := out_priv || ARRAY['DAV:unlock'];
324 IF (in_bits & 16::BIT(24)) != 0::BIT(24) THEN
325 out_priv := out_priv || ARRAY['DAV:read-acl'];
328 IF (in_bits & 32::BIT(24)) != 0::BIT(24) THEN
329 out_priv := out_priv || ARRAY['DAV:read-current-user-privilege-set'];
332 IF (in_bits & 256::BIT(24)) != 0::BIT(24) THEN
333 out_priv := out_priv || ARRAY['DAV:write-acl'];
336 IF (in_bits & 7168::BIT(24)) != 0::BIT(24) THEN
337 IF (in_bits & 7168::BIT(24)) = 7168::BIT(24) THEN
338 out_priv := out_priv || ARRAY['caldav:schedule-deliver'];
340 IF (in_bits & 1024::BIT(24)) != 0::BIT(24) THEN
341 out_priv := out_priv || ARRAY['caldav:schedule-deliver-invite'];
343 IF (in_bits & 2048::BIT(24)) != 0::BIT(24) THEN
344 out_priv := out_priv || ARRAY['caldav:schedule-deliver-reply'];
346 IF (in_bits & 4096::BIT(24)) != 0::BIT(24) THEN
347 out_priv := out_priv || ARRAY['caldav:schedule-query-freebusy'];
352 IF (in_bits & 57344::BIT(24)) != 0::BIT(24) THEN
353 IF (in_bits & 57344::BIT(24)) = 57344::BIT(24) THEN
354 out_priv := out_priv || ARRAY['caldav:schedule-send'];
356 IF (in_bits & 8192::BIT(24)) != 0::BIT(24) THEN
357 out_priv := out_priv || ARRAY['caldav:schedule-send-invite'];
359 IF (in_bits & 16384::BIT(24)) != 0::BIT(24) THEN
360 out_priv := out_priv || ARRAY['caldav:schedule-send-reply'];
362 IF (in_bits & 32768::BIT(24)) != 0::BIT(24) THEN
363 out_priv := out_priv || ARRAY['caldav:schedule-send-freebusy'];
371 LANGUAGE 'plpgsql' IMMUTABLE STRICT;