1 /* Copyright (C) 2013-2021 Greenbone Networks GmbH
2 *
3 * SPDX-License-Identifier: AGPL-3.0-or-later
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Affero General Public License as
7 * published by the Free Software Foundation, either version 3 of the
8 * License, or (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU Affero General Public License for more details.
14 *
15 * You should have received a copy of the GNU Affero General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 /**
20 * @file manage_acl.c
21 * @brief The Greenbone Vulnerability Manager management library (Access
22 * Control Layer).
23 *
24 * This file isolates the access control portions of the GVM
25 * management library.
26 */
27
28 #include "manage_acl.h"
29 #include "manage_sql.h"
30 #include "sql.h"
31
32 #include <assert.h>
33 #include <stdlib.h>
34 #include <string.h>
35
36 #undef G_LOG_DOMAIN
37 /**
38 * @brief GLib log domain.
39 */
40 #define G_LOG_DOMAIN "md manage"
41
42 /**
43 * @brief Test whether the current user may perform an operation.
44 *
45 * Does not check if the user is special.
46 *
47 * @param[in] operation Name of operation.
48 *
49 * @return 1 if user has permission, else 0.
50 */
51 static int
user_may_internal(const char * operation)52 user_may_internal (const char *operation)
53 {
54 int ret;
55 gchar *quoted_operation;
56
57 assert (operation);
58
59 quoted_operation = sql_quote (operation);
60
61 ret = sql_int (ACL_USER_MAY ("0"),
62 current_credentials.uuid,
63 current_credentials.uuid,
64 current_credentials.uuid,
65 quoted_operation,
66 quoted_operation,
67 quoted_operation,
68 quoted_operation);
69
70 g_free (quoted_operation);
71
72 return ret;
73 }
74
75 /**
76 * @brief Check if a string array contains a string, ignoring case.
77 *
78 * @param[in] strv String array.
79 * @param[in] string String.
80 *
81 * @return 1 if strv contains string, else 0.
82 */
83 static int
strv_case_eq(gchar ** strv,const gchar * string)84 strv_case_eq (gchar **strv, const gchar *string)
85 {
86 if (string == NULL)
87 return 0;
88
89 while (*strv)
90 if (strcasecmp (*strv, string) == 0)
91 return 1;
92 else
93 strv++;
94
95 return 0;
96 }
97
98 /**
99 * @brief Get commands that the current user may run.
100 *
101 * @param[in] disabled_commands All disabled commands.
102 *
103 * @return Freshly allocated list of commands. Free with g_free.
104 */
105 command_t *
acl_commands(gchar ** disabled_commands)106 acl_commands (gchar **disabled_commands)
107 {
108 command_t *all, *commands;
109 int index, length, special_user;
110
111 /* Count maximum number of commands. */
112
113 length = 1;
114 all = gmp_commands;
115 while ((*all).name)
116 {
117 length++;
118 all++;
119 }
120
121 /* Fill return array with allowed commands. */
122
123 special_user = ((current_credentials.uuid == NULL)
124 || (strlen (current_credentials.uuid) == 0));
125
126 if (special_user == 0)
127 special_user = sql_int ("SELECT user_can_everything ('%s');",
128 current_credentials.uuid);
129
130 commands = g_malloc0 (length * sizeof (*commands));
131 all = gmp_commands;
132 index = 0;
133 while ((*all).name)
134 {
135 if ((disabled_commands == NULL
136 || strv_case_eq (disabled_commands, (*all).name)
137 == 0)
138 && (special_user
139 || user_may_internal ((*all).name)))
140 {
141 commands[index].name = (*all).name;
142 commands[index].summary = (*all).summary;
143 index++;
144 }
145 all++;
146 }
147
148 return commands;
149 }
150
151 /**
152 * @brief Test whether a user may perform an operation.
153 *
154 * @param[in] operation Name of operation.
155 *
156 * @return 1 if user has permission, else 0.
157 */
158 int
acl_user_may(const char * operation)159 acl_user_may (const char *operation)
160 {
161 if (strlen (current_credentials.uuid) == 0)
162 /* Allow the dummy user in init_manage to do anything. */
163 return 1;
164
165 if (sql_int ("SELECT user_can_everything ('%s');",
166 current_credentials.uuid))
167 return 1;
168
169 return user_may_internal (operation);
170 }
171
172 /**
173 * @brief Check whether a role has Super Admin capability.
174 *
175 * @param[in] role_id ID of role.
176 *
177 * @return 1 if role can Super Admin, else 0.
178 */
179 int
acl_role_can_super_everyone(const char * role_id)180 acl_role_can_super_everyone (const char *role_id)
181 {
182 gchar *quoted_role_id;
183 quoted_role_id = sql_quote (role_id);
184 if (sql_int (" SELECT EXISTS (SELECT * FROM permissions"
185 " WHERE name = 'Super'"
186 /* Super on everyone. */
187 " AND (resource = 0)"
188 " AND subject_location"
189 " = " G_STRINGIFY (LOCATION_TABLE)
190 " AND (subject_type = 'role'"
191 " AND subject"
192 " = (SELECT id"
193 " FROM roles"
194 " WHERE uuid = '%s')));",
195 role_id))
196 {
197 g_free (quoted_role_id);
198 return 1;
199 }
200 g_free (quoted_role_id);
201 return 0;
202 }
203
204 /**
205 * @brief Check whether a user is a Super Admin.
206 *
207 * @param[in] uuid Uuid of user.
208 *
209 * @return 1 if user is a Super Admin, else 0.
210 */
211 int
acl_user_can_super_everyone(const char * uuid)212 acl_user_can_super_everyone (const char *uuid)
213 {
214 gchar *quoted_uuid;
215
216 quoted_uuid = sql_quote (uuid);
217 if (sql_int (" SELECT EXISTS (SELECT * FROM permissions"
218 " WHERE name = 'Super'"
219 /* Super on everyone. */
220 " AND (resource = 0)"
221 " AND subject_location"
222 " = " G_STRINGIFY (LOCATION_TABLE)
223 " AND ((subject_type = 'user'"
224 " AND subject"
225 " = (SELECT id FROM users"
226 " WHERE users.uuid = '%s'))"
227 " OR (subject_type = 'group'"
228 " AND subject"
229 " IN (SELECT DISTINCT \"group\""
230 " FROM group_users"
231 " WHERE \"user\""
232 " = (SELECT id"
233 " FROM users"
234 " WHERE users.uuid"
235 " = '%s')))"
236 " OR (subject_type = 'role'"
237 " AND subject"
238 " IN (SELECT DISTINCT role"
239 " FROM role_users"
240 " WHERE \"user\""
241 " = (SELECT id"
242 " FROM users"
243 " WHERE users.uuid"
244 " = '%s')))));",
245 quoted_uuid,
246 quoted_uuid,
247 quoted_uuid))
248 {
249 g_free (quoted_uuid);
250 return 1;
251 }
252 g_free (quoted_uuid);
253 return 0;
254 }
255
256 /**
257 * @brief Test whether a user may perform any operation.
258 *
259 * @param[in] user_id UUID of user.
260 *
261 * @return 1 if user has permission, else 0.
262 */
263 int
acl_user_can_everything(const char * user_id)264 acl_user_can_everything (const char *user_id)
265 {
266 gchar *quoted_user_id;
267 int ret;
268
269 quoted_user_id = sql_quote (user_id);
270 ret = sql_int ("SELECT count(*) > 0 FROM permissions"
271 " WHERE resource = 0"
272 " AND subject_location"
273 " = " G_STRINGIFY (LOCATION_TABLE)
274 " AND ((subject_type = 'user'"
275 " AND subject"
276 " = (SELECT id FROM users"
277 " WHERE users.uuid = '%s'))"
278 " OR (subject_type = 'group'"
279 " AND subject"
280 " IN (SELECT DISTINCT \"group\""
281 " FROM group_users"
282 " WHERE \"user\" = (SELECT id"
283 " FROM users"
284 " WHERE users.uuid"
285 " = '%s')))"
286 " OR (subject_type = 'role'"
287 " AND subject"
288 " IN (SELECT DISTINCT role"
289 " FROM role_users"
290 " WHERE \"user\" = (SELECT id"
291 " FROM users"
292 " WHERE users.uuid"
293 " = '%s'))))"
294 " AND name = 'Everything';",
295 quoted_user_id,
296 quoted_user_id,
297 quoted_user_id);
298 g_free (quoted_user_id);
299 return ret;
300 }
301
302 /**
303 * @brief Test whether a user has super permission on another user.
304 *
305 * @param[in] super_user_id UUID of user who may have super permission.
306 * @param[in] other_user Other user.
307 *
308 * @return 1 if user has permission, else 0.
309 */
310 int
acl_user_has_super(const char * super_user_id,user_t other_user)311 acl_user_has_super (const char *super_user_id, user_t other_user)
312 {
313 gchar *quoted_super_user_id;
314
315 quoted_super_user_id = sql_quote (super_user_id);
316 if (sql_int (" SELECT EXISTS (SELECT * FROM permissions"
317 " WHERE name = 'Super'"
318 /* Super on everyone. */
319 " AND ((resource = 0)"
320 /* Super on other_user. */
321 " OR ((resource_type = 'user')"
322 " AND (resource = %llu))"
323 /* Super on other_user's role. */
324 " OR ((resource_type = 'role')"
325 " AND (resource"
326 " IN (SELECT DISTINCT role"
327 " FROM role_users"
328 " WHERE \"user\" = %llu)))"
329 /* Super on other_user's group. */
330 " OR ((resource_type = 'group')"
331 " AND (resource"
332 " IN (SELECT DISTINCT \"group\""
333 " FROM group_users"
334 " WHERE \"user\" = %llu))))"
335 " AND subject_location"
336 " = " G_STRINGIFY (LOCATION_TABLE)
337 " AND ((subject_type = 'user'"
338 " AND subject"
339 " = (SELECT id FROM users"
340 " WHERE users.uuid = '%s'))"
341 " OR (subject_type = 'group'"
342 " AND subject"
343 " IN (SELECT DISTINCT \"group\""
344 " FROM group_users"
345 " WHERE \"user\""
346 " = (SELECT id"
347 " FROM users"
348 " WHERE users.uuid"
349 " = '%s')))"
350 " OR (subject_type = 'role'"
351 " AND subject"
352 " IN (SELECT DISTINCT role"
353 " FROM role_users"
354 " WHERE \"user\""
355 " = (SELECT id"
356 " FROM users"
357 " WHERE users.uuid"
358 " = '%s')))));",
359 other_user,
360 other_user,
361 other_user,
362 super_user_id,
363 super_user_id,
364 super_user_id))
365 {
366 g_free (quoted_super_user_id);
367 return 1;
368 }
369 g_free (quoted_super_user_id);
370 return 0;
371 }
372
373 /**
374 * @brief Check whether a user is an Admin.
375 *
376 * @param[in] uuid Uuid of user.
377 *
378 * @return 1 if user is an Admin, else 0.
379 */
380 int
acl_user_is_admin(const char * uuid)381 acl_user_is_admin (const char *uuid)
382 {
383 int ret;
384 gchar *quoted_uuid;
385
386 quoted_uuid = sql_quote (uuid);
387 ret = sql_int ("SELECT count (*) FROM role_users"
388 " WHERE role = (SELECT id FROM roles"
389 " WHERE uuid = '" ROLE_UUID_ADMIN "')"
390 " AND \"user\" = (SELECT id FROM users WHERE uuid = '%s');",
391 quoted_uuid);
392 g_free (quoted_uuid);
393 return ret;
394 }
395
396 /**
397 * @brief Check whether a user is an Observer.
398 *
399 * @param[in] uuid Uuid of user.
400 *
401 * @return 1 if user is an Observer, else 0.
402 */
403 int
acl_user_is_observer(const char * uuid)404 acl_user_is_observer (const char *uuid)
405 {
406 int ret;
407 gchar *quoted_uuid;
408
409 quoted_uuid = sql_quote (uuid);
410 ret = sql_int ("SELECT count (*) FROM role_users"
411 " WHERE role = (SELECT id FROM roles"
412 " WHERE uuid = '" ROLE_UUID_OBSERVER "')"
413 " AND \"user\" = (SELECT id FROM users WHERE uuid = '%s');",
414 quoted_uuid);
415 g_free (quoted_uuid);
416 return ret;
417 }
418
419 /**
420 * @brief Check whether a user is a Super Admin.
421 *
422 * @param[in] uuid Uuid of user.
423 *
424 * @return 1 if user is a Super Admin, else 0.
425 */
426 int
acl_user_is_super_admin(const char * uuid)427 acl_user_is_super_admin (const char *uuid)
428 {
429 int ret;
430 gchar *quoted_uuid;
431
432 quoted_uuid = sql_quote (uuid);
433 ret = sql_int ("SELECT count (*) FROM role_users"
434 " WHERE role = (SELECT id FROM roles"
435 " WHERE uuid = '" ROLE_UUID_SUPER_ADMIN "')"
436 " AND \"user\" = (SELECT id FROM users WHERE uuid = '%s');",
437 quoted_uuid);
438 g_free (quoted_uuid);
439 return ret;
440 }
441
442 /**
443 * @brief Check whether a user has the User role.
444 *
445 * @param[in] uuid Uuid of user.
446 *
447 * @return 1 if user has the User role, else 0.
448 */
449 int
acl_user_is_user(const char * uuid)450 acl_user_is_user (const char *uuid)
451 {
452 int ret;
453 gchar *quoted_uuid;
454
455 quoted_uuid = sql_quote (uuid);
456 ret = sql_int ("SELECT count (*) FROM role_users"
457 " WHERE role = (SELECT id FROM roles"
458 " WHERE uuid = '" ROLE_UUID_USER "')"
459 " AND \"user\" = (SELECT id FROM users WHERE uuid = '%s');",
460 quoted_uuid);
461 g_free (quoted_uuid);
462 return ret;
463 }
464
465 /* TODO This is only predicatable for unique fields like "id". If the field
466 * is "name" then "SELECT ... format" will choose arbitrarily between
467 * the resources that have the same name. */
468 /**
469 * @brief Super clause.
470 *
471 * @param[in] format Value format specifier.
472 */
473 #define ACL_SUPER_CLAUSE(format) \
474 " name = 'Super'" \
475 /* Super on everyone. */ \
476 " AND ((resource = 0)" \
477 /* Super on other_user. */ \
478 " OR ((resource_type = 'user')" \
479 " AND (resource = (SELECT %ss%s.owner" \
480 " FROM %ss%s" \
481 " WHERE %s = " format ")))" \
482 /* Super on other_user's role. */ \
483 " OR ((resource_type = 'role')" \
484 " AND (resource" \
485 " IN (SELECT DISTINCT role" \
486 " FROM role_users" \
487 " WHERE \"user\"" \
488 " = (SELECT %ss%s.owner" \
489 " FROM %ss%s" \
490 " WHERE %s" \
491 " = " format "))))" \
492 /* Super on other_user's group. */ \
493 " OR ((resource_type = 'group')" \
494 " AND (resource" \
495 " IN (SELECT DISTINCT \"group\"" \
496 " FROM group_users" \
497 " WHERE \"user\"" \
498 " = (SELECT %ss%s.owner" \
499 " FROM %ss%s" \
500 " WHERE %s = " format ")))))" \
501 " AND subject_location = " G_STRINGIFY (LOCATION_TABLE) \
502 " AND ((subject_type = 'user'" \
503 " AND subject" \
504 " = (SELECT id FROM users" \
505 " WHERE users.uuid = '%s'))" \
506 " OR (subject_type = 'group'" \
507 " AND subject" \
508 " IN (SELECT DISTINCT \"group\"" \
509 " FROM group_users" \
510 " WHERE \"user\"" \
511 " = (SELECT id" \
512 " FROM users" \
513 " WHERE users.uuid" \
514 " = '%s')))" \
515 " OR (subject_type = 'role'" \
516 " AND subject" \
517 " IN (SELECT DISTINCT role" \
518 " FROM role_users" \
519 " WHERE \"user\"" \
520 " = (SELECT id" \
521 " FROM users" \
522 " WHERE users.uuid" \
523 " = '%s'))))"
524
525 /**
526 * @brief Super clause arguments.
527 *
528 * @param[in] type Type of resource.
529 * @param[in] field Field to compare. Typically "uuid".
530 * @param[in] value Expected value of field.
531 * @param[in] user_id UUID of user.
532 * @param[in] trash Whether to search trash.
533 */
534 #define ACL_SUPER_CLAUSE_ARGS(type, field, value, user_id, trash) \
535 type, \
536 trash ? (strcasecmp (type, "task") ? "_trash" : "") : "", \
537 type, \
538 trash ? (strcasecmp (type, "task") ? "_trash" : "") : "", \
539 field, \
540 value, \
541 type, \
542 trash ? (strcasecmp (type, "task") ? "_trash" : "") : "", \
543 type, \
544 trash ? (strcasecmp (type, "task") ? "_trash" : "") : "", \
545 field, \
546 value, \
547 type, \
548 trash ? (strcasecmp (type, "task") ? "_trash" : "") : "", \
549 type, \
550 trash ? (strcasecmp (type, "task") ? "_trash" : "") : "", \
551 field, \
552 value, \
553 user_id, \
554 user_id, \
555 user_id
556
557 /**
558 * @brief Test whether a user has Super permission on a resource.
559 *
560 * @param[in] type Type of resource.
561 * @param[in] field Field to compare with value.
562 * @param[in] value Identifier value of resource.
563 * @param[in] trash Whether resource is in trash.
564 *
565 * @return 1 if user has Super, else 0.
566 */
567 static int
acl_user_has_super_on(const char * type,const char * field,const char * value,int trash)568 acl_user_has_super_on (const char *type, const char *field, const char *value,
569 int trash)
570 {
571 gchar *quoted_value;
572 quoted_value = sql_quote (value);
573 if (sql_int ("SELECT EXISTS (SELECT * FROM permissions"
574 " WHERE " ACL_SUPER_CLAUSE ("'%s'") ");",
575 ACL_SUPER_CLAUSE_ARGS (type, field, quoted_value,
576 current_credentials.uuid, trash)))
577 {
578 g_free (quoted_value);
579 return 1;
580 }
581 g_free (quoted_value);
582 return 0;
583 }
584
585 /**
586 * @brief Test whether a user has Super permission on a resource.
587 *
588 * @param[in] type Type of resource.
589 * @param[in] field Field to compare with resource.
590 * @param[in] resource Resource.
591 * @param[in] trash Whether resource is in trash.
592 *
593 * @return 1 if user has Super, else 0.
594 */
595 static int
acl_user_has_super_on_resource(const char * type,const char * field,resource_t resource,int trash)596 acl_user_has_super_on_resource (const char *type, const char *field,
597 resource_t resource, int trash)
598 {
599 if (sql_int ("SELECT EXISTS (SELECT * FROM permissions"
600 " WHERE " ACL_SUPER_CLAUSE ("%llu") ");",
601 ACL_SUPER_CLAUSE_ARGS (type, field, resource,
602 current_credentials.uuid, trash)))
603 return 1;
604 return 0;
605 }
606
607 /**
608 * @brief Test whether a user is the actual owner of a resource.
609 *
610 * @param[in] type Type of resource, for example "task".
611 * @param[in] uuid UUID of resource.
612 *
613 * @return 1 if user actually owns resource, else 0.
614 */
615 int
acl_user_is_owner(const char * type,const char * uuid)616 acl_user_is_owner (const char *type, const char *uuid)
617 {
618 int ret;
619 gchar *quoted_uuid;
620
621 assert (uuid && current_credentials.uuid);
622
623 quoted_uuid = g_strdup (uuid);
624 ret = sql_int ("SELECT count(*) FROM %ss"
625 " WHERE uuid = '%s'"
626 " AND owner = (SELECT users.id FROM users"
627 " WHERE users.uuid = '%s');",
628 type,
629 quoted_uuid,
630 current_credentials.uuid);
631 g_free (quoted_uuid);
632
633 return ret;
634 }
635
636 /**
637 * @brief Test whether a user effectively owns a resource.
638 *
639 * A Super permissions can give a user effective ownership of another
640 * user's resource.
641 *
642 * @param[in] type Type of resource, for example "task".
643 * @param[in] uuid UUID of resource.
644 * @param[in] trash Whether the resource is in the trash.
645 *
646 * @return 1 if user owns resource, else 0.
647 */
648 int
acl_user_owns_uuid(const char * type,const char * uuid,int trash)649 acl_user_owns_uuid (const char *type, const char *uuid, int trash)
650 {
651 int ret;
652 gchar *quoted_uuid;
653
654 assert (current_credentials.uuid);
655
656 if ((strcmp (type, "nvt") == 0)
657 || (strcmp (type, "cve") == 0)
658 || (strcmp (type, "cpe") == 0)
659 || (strcmp (type, "ovaldef") == 0)
660 || (strcmp (type, "cert_bund_adv") == 0)
661 || (strcmp (type, "dfn_cert_adv") == 0))
662 return 1;
663
664 if (acl_user_has_super_on (type, "uuid", uuid, 0))
665 return 1;
666
667 quoted_uuid = sql_quote (uuid);
668 if (strcmp (type, "result") == 0)
669 ret = sql_int ("SELECT count(*) FROM results, reports"
670 " WHERE results.uuid = '%s'"
671 " AND results.report = reports.id"
672 " AND (reports.owner = (SELECT users.id FROM users"
673 " WHERE users.uuid = '%s'));",
674 quoted_uuid,
675 current_credentials.uuid);
676 else if (strcmp (type, "report") == 0)
677 ret = sql_int ("SELECT count(*) FROM reports"
678 " WHERE uuid = '%s'"
679 " AND (owner = (SELECT users.id FROM users"
680 " WHERE users.uuid = '%s'));",
681 quoted_uuid,
682 current_credentials.uuid);
683 else if (strcmp (type, "permission") == 0)
684 ret = sql_int ("SELECT count(*) FROM permissions%s"
685 " WHERE uuid = '%s'"
686 " AND ((owner IS NULL)"
687 " OR (owner = (SELECT users.id FROM users"
688 " WHERE users.uuid = '%s')));",
689 trash ? "_trash" : "",
690 quoted_uuid,
691 current_credentials.uuid);
692 else
693 ret = sql_int ("SELECT count(*) FROM %ss%s"
694 " WHERE uuid = '%s'"
695 "%s"
696 " AND (owner = (SELECT users.id FROM users"
697 " WHERE users.uuid = '%s'));",
698 type,
699 (strcmp (type, "task") && trash) ? "_trash" : "",
700 quoted_uuid,
701 (strcmp (type, "task")
702 ? ""
703 : (trash ? " AND hidden = 2" : " AND hidden < 2")),
704 current_credentials.uuid);
705 g_free (quoted_uuid);
706
707 return ret;
708 }
709
710 /**
711 * @brief Test whether a user effectively owns a resource.
712 *
713 * A Super permissions can give a user effective ownership of another
714 * user's resource.
715 *
716 * @param[in] type Type of resource, for example "task".
717 * @param[in] resource Resource.
718 * @param[in] trash Whether the resource is in the trash.
719 *
720 * @return 1 if user owns resource, else 0.
721 */
722 int
acl_user_owns(const char * type,resource_t resource,int trash)723 acl_user_owns (const char *type, resource_t resource, int trash)
724 {
725 int ret;
726
727 assert (current_credentials.uuid);
728
729 if ((strcmp (type, "nvt") == 0)
730 || (strcmp (type, "cve") == 0)
731 || (strcmp (type, "cpe") == 0)
732 || (strcmp (type, "ovaldef") == 0)
733 || (strcmp (type, "cert_bund_adv") == 0)
734 || (strcmp (type, "dfn_cert_adv") == 0))
735 return 1;
736
737 if (acl_user_has_super_on_resource (type, "id", resource, trash))
738 return 1;
739
740 if (strcmp (type, "result") == 0)
741 ret = sql_int ("SELECT count(*) FROM results, reports"
742 " WHERE results.id = %llu"
743 " AND results.report = reports.id"
744 " AND (reports.owner = (SELECT users.id FROM users"
745 " WHERE users.uuid = '%s'));",
746 resource,
747 current_credentials.uuid);
748 else
749 ret = sql_int ("SELECT count(*) FROM %ss%s"
750 " WHERE id = %llu"
751 "%s"
752 " AND (owner = (SELECT users.id FROM users"
753 " WHERE users.uuid = '%s'));",
754 type,
755 (strcmp (type, "task") && trash) ? "_trash" : "",
756 resource,
757 (strcmp (type, "task")
758 ? ""
759 : (trash ? " AND hidden = 2" : " AND hidden < 2")),
760 current_credentials.uuid);
761
762 return ret;
763 }
764
765 /**
766 * @brief Test whether a user effectively owns a resource.
767 *
768 * A Super permissions can give a user effective ownership of another
769 * user's resource.
770 *
771 * @param[in] type Type of resource, for example "task".
772 * @param[in] uuid UUID of resource.
773 *
774 * @return 1 if user owns resource, else 0.
775 */
776 int
acl_user_owns_trash_uuid(const char * type,const char * uuid)777 acl_user_owns_trash_uuid (const char *type, const char *uuid)
778 {
779 int ret;
780 gchar *quoted_uuid;
781
782 assert (current_credentials.uuid);
783 assert (type && strcmp (type, "task"));
784
785 if (acl_user_has_super_on (type, "uuid", uuid, 1))
786 return 1;
787
788 quoted_uuid = sql_quote (uuid);
789 ret = sql_int ("SELECT count(*) FROM %ss_trash"
790 " WHERE uuid = '%s'"
791 " AND (owner = (SELECT users.id FROM users"
792 " WHERE users.uuid = '%s'));",
793 type,
794 quoted_uuid,
795 current_credentials.uuid);
796 g_free (quoted_uuid);
797
798 return ret;
799 }
800
801 /**
802 * @brief Test whether the user may access a resource.
803 *
804 * @param[in] type Type of resource, for example "task".
805 * @param[in] uuid UUID of resource.
806 * @param[in] permission Permission.
807 * @param[in] trash Whether the resource is in the trash.
808 *
809 * @return 1 if user may access resource, else 0.
810 */
811 int
acl_user_has_access_uuid(const char * type,const char * uuid,const char * permission,int trash)812 acl_user_has_access_uuid (const char *type, const char *uuid,
813 const char *permission, int trash)
814 {
815 int ret, get;
816 char *uuid_task;
817 gchar *quoted_permission, *quoted_uuid;
818
819 assert (current_credentials.uuid);
820
821 if (permission && (valid_gmp_command (permission) == 0))
822 return 0;
823
824 if (!strcmp (current_credentials.uuid, ""))
825 return 1;
826
827 /* The Super case is checked here. */
828 ret = acl_user_owns_uuid (type, uuid, trash);
829 if (ret)
830 return ret;
831
832 if (trash)
833 /* For simplicity, trashcan items are visible only to their owners. */
834 return 0;
835
836 quoted_uuid = sql_quote (uuid);
837 if (strcasecmp (type, "report") == 0)
838 {
839 task_t task;
840 report_t report;
841
842 switch (sql_int64 (&report,
843 "SELECT id FROM reports WHERE uuid = '%s';",
844 quoted_uuid))
845 {
846 case 0:
847 break;
848 case 1: /* Too few rows in result of query. */
849 g_free (quoted_uuid);
850 return 0;
851 break;
852 default: /* Programming error. */
853 assert (0);
854 case -1:
855 g_free (quoted_uuid);
856 return 0;
857 break;
858 }
859
860 report_task (report, &task);
861 if (task == 0)
862 {
863 g_free (quoted_uuid);
864 return 0;
865 }
866 task_uuid (task, &uuid_task);
867 }
868 else if (strcasecmp (type, "result") == 0)
869 {
870 task_t task;
871
872 switch (sql_int64 (&task,
873 "SELECT task FROM results WHERE uuid = '%s';",
874 uuid))
875 {
876 case 0:
877 break;
878 case 1: /* Too few rows in result of query. */
879 g_free (quoted_uuid);
880 return 0;
881 break;
882 default: /* Programming error. */
883 assert (0);
884 case -1:
885 g_free (quoted_uuid);
886 return 0;
887 break;
888 }
889
890 task_uuid (task, &uuid_task);
891 }
892 else
893 uuid_task = NULL;
894
895 if ((strcmp (type, "permission") == 0)
896 && ((permission == NULL)
897 || (strlen (permission) > 3 && strncmp (permission, "get", 3) == 0)))
898 {
899 ret = sql_int ("SELECT count(*) FROM permissions"
900 /* Any permission implies 'get'. */
901 " WHERE (resource_uuid = '%s'"
902 /* Users may view any permissions that affect them. */
903 " OR uuid = '%s')"
904 " AND subject_location = " G_STRINGIFY (LOCATION_TABLE)
905 " AND ((subject_type = 'user'"
906 " AND subject"
907 " = (SELECT id FROM users"
908 " WHERE users.uuid = '%s'))"
909 " OR (subject_type = 'group'"
910 " AND subject"
911 " IN (SELECT DISTINCT \"group\""
912 " FROM group_users"
913 " WHERE \"user\" = (SELECT id"
914 " FROM users"
915 " WHERE users.uuid"
916 " = '%s')))"
917 " OR (subject_type = 'role'"
918 " AND subject"
919 " IN (SELECT DISTINCT role"
920 " FROM role_users"
921 " WHERE \"user\" = (SELECT id"
922 " FROM users"
923 " WHERE users.uuid"
924 " = '%s'))));",
925 uuid_task ? uuid_task : quoted_uuid,
926 uuid_task ? uuid_task : quoted_uuid,
927 current_credentials.uuid,
928 current_credentials.uuid,
929 current_credentials.uuid);
930 free (uuid_task);
931 g_free (quoted_uuid);
932 return ret;
933 }
934 else if (strcmp (type, "permission") == 0)
935 {
936 /* There are no "permissions on permissions", so if a user does not
937 * effectively own a permission, there's no way for the user to access
938 * the permission. */
939 free (uuid_task);
940 g_free (quoted_uuid);
941 return 0;
942 }
943
944 get = (permission == NULL
945 || (strlen (permission) > 3 && strncmp (permission, "get", 3) == 0));
946 quoted_permission = sql_quote (permission ? permission : "");
947
948 ret = sql_int ("SELECT count(*) FROM permissions"
949 " WHERE resource_uuid = '%s'"
950 " AND subject_location = " G_STRINGIFY (LOCATION_TABLE)
951 " AND ((subject_type = 'user'"
952 " AND subject"
953 " = (SELECT id FROM users"
954 " WHERE users.uuid = '%s'))"
955 " OR (subject_type = 'group'"
956 " AND subject"
957 " IN (SELECT DISTINCT \"group\""
958 " FROM group_users"
959 " WHERE \"user\" = (SELECT id"
960 " FROM users"
961 " WHERE users.uuid"
962 " = '%s')))"
963 " OR (subject_type = 'role'"
964 " AND subject"
965 " IN (SELECT DISTINCT role"
966 " FROM role_users"
967 " WHERE \"user\" = (SELECT id"
968 " FROM users"
969 " WHERE users.uuid"
970 " = '%s'))))"
971 " %s%s%s;",
972 uuid_task ? uuid_task : quoted_uuid,
973 current_credentials.uuid,
974 current_credentials.uuid,
975 current_credentials.uuid,
976 (get ? "" : "AND name = '"),
977 (get ? "" : quoted_permission),
978 (get ? "" : "'"));
979
980 free (uuid_task);
981 g_free (quoted_permission);
982 g_free (quoted_uuid);
983 return ret;
984 }
985
986 /**
987 * @brief Generate the ownership part of an SQL WHERE clause for a given user.
988 *
989 * @param[in] user_id UUID of user. "" can be used to rely on
990 * user_sql alone, except when type is "permission".
991 * @param[in] user_sql SQL to get user.
992 * @param[in] type Type of resource.
993 * @param[in] get GET data.
994 * @param[in] owned Only get items accessible by the given user.
995 * @param[in] owner_filter Owner filter keyword.
996 * @param[in] resource Resource.
997 * @param[in] permissions Permissions.
998 * @param[in] with_optional Whether the WITH clause is optional.
999 * @param[in] with_prefix Optional prefix for WITH subqueries.
1000 * @param[out] with Address for WITH clause if allowed, else NULL.
1001 *
1002 * @return Newly allocated owned clause.
1003 */
1004 static gchar *
acl_where_owned_user(const char * user_id,const char * user_sql,const char * type,const get_data_t * get,int owned,const gchar * owner_filter,resource_t resource,array_t * permissions,int with_optional,const char * with_prefix,gchar ** with)1005 acl_where_owned_user (const char *user_id, const char *user_sql,
1006 const char *type, const get_data_t *get, int owned,
1007 const gchar *owner_filter, resource_t resource,
1008 array_t *permissions, int with_optional,
1009 const char *with_prefix, gchar **with)
1010 {
1011 gchar *owned_clause, *filter_owned_clause;
1012 GString *permission_or;
1013 int table_trash;
1014 guint index;
1015
1016 if (with)
1017 {
1018 *with = g_strdup_printf
1019 ("WITH %spermissions_subject"
1020 " AS (SELECT * FROM permissions"
1021 " WHERE subject_location"
1022 " = " G_STRINGIFY (LOCATION_TABLE)
1023 " AND ((subject_type = 'user'"
1024 " AND subject"
1025 " = (%s))"
1026 " OR (subject_type = 'group'"
1027 " AND subject"
1028 " IN (SELECT DISTINCT \"group\""
1029 " FROM group_users"
1030 " WHERE \"user\""
1031 " = (%s)))"
1032 " OR (subject_type = 'role'"
1033 " AND subject"
1034 " IN (SELECT DISTINCT role"
1035 " FROM role_users"
1036 " WHERE \"user\""
1037 " = (%s))))),"
1038 " %ssuper_on_users"
1039 " AS (SELECT DISTINCT *"
1040 " FROM (SELECT resource FROM %spermissions_subject"
1041 " WHERE name = 'Super'"
1042 " AND resource_type = 'user'"
1043 " UNION"
1044 " SELECT \"user\" FROM role_users"
1045 " WHERE role"
1046 " IN (SELECT resource"
1047 " FROM %spermissions_subject"
1048 " WHERE name = 'Super'"
1049 " AND resource_type = 'role')"
1050 " UNION"
1051 " SELECT \"user\" FROM group_users"
1052 " WHERE \"group\""
1053 " IN (SELECT resource"
1054 " FROM %spermissions_subject"
1055 " WHERE name = 'Super'"
1056 " AND resource_type = 'group'))"
1057 " AS all_users)",
1058 with_prefix ? with_prefix : "",
1059 user_sql,
1060 user_sql,
1061 user_sql,
1062 with_prefix ? with_prefix : "",
1063 with_prefix ? with_prefix : "",
1064 with_prefix ? with_prefix : "",
1065 with_prefix ? with_prefix : "");
1066 }
1067
1068 if (owned == 0)
1069 {
1070 if (with_optional && with)
1071 {
1072 g_free (*with);
1073 *with = NULL;
1074 }
1075 return g_strdup (" t ()");
1076 }
1077
1078 permission_or = g_string_new ("");
1079 index = 0;
1080 if (permissions == NULL || permissions->len == 0)
1081 {
1082 /* Treat filters with no permissions keyword as "any". */
1083 permission_or = g_string_new ("t ()");
1084 index = 1;
1085 }
1086 else if (permissions)
1087 for (; index < permissions->len; index++)
1088 {
1089 gchar *permission, *quoted;
1090 permission = (gchar*) g_ptr_array_index (permissions, index);
1091 if (strcasecmp (permission, "any") == 0)
1092 {
1093 g_string_free (permission_or, TRUE);
1094 permission_or = g_string_new ("t ()");
1095 index = 1;
1096 break;
1097 }
1098 quoted = sql_quote (permission);
1099 if (index == 0)
1100 g_string_append_printf (permission_or, "name = '%s'", quoted);
1101 else
1102 g_string_append_printf (permission_or, " OR name = '%s'",
1103 quoted);
1104 g_free (quoted);
1105 }
1106
1107 table_trash = get->trash && strcasecmp (type, "task");
1108 if (resource || (user_id == NULL))
1109 owned_clause
1110 = g_strdup (" (t ())");
1111 else if (with)
1112 {
1113 gchar *permission_clause;
1114
1115 /* Caller supports WITH clause. */
1116
1117 permission_clause = NULL;
1118 if (user_id && index)
1119 {
1120 gchar *clause;
1121 clause
1122 = g_strdup_printf ("OR EXISTS"
1123 " (SELECT id FROM %spermissions_subject"
1124 " WHERE resource = %ss%s.id"
1125 " AND resource_type = '%s'"
1126 " AND resource_location = %i"
1127 " AND (%s))",
1128 with_prefix ? with_prefix : "",
1129 type,
1130 get->trash && strcmp (type, "task") ? "_trash" : "",
1131 type,
1132 get->trash ? LOCATION_TRASH : LOCATION_TABLE,
1133 permission_or->str);
1134
1135 if (strcmp (type, "report") == 0)
1136 permission_clause
1137 = g_strdup_printf ("%s"
1138 " OR EXISTS"
1139 " (SELECT id FROM %spermissions_subject"
1140 " WHERE resource = reports%s.task"
1141 " AND resource_type = 'task'"
1142 " AND (%s))",
1143 clause,
1144 with_prefix ? with_prefix : "",
1145 get->trash ? "_trash" : "",
1146 permission_or->str);
1147 else if (strcmp (type, "result") == 0)
1148 permission_clause
1149 = g_strdup_printf ("%s"
1150 " OR EXISTS"
1151 " (SELECT id FROM %spermissions_subject"
1152 " WHERE resource = results%s.task"
1153 " AND resource_type = 'task'"
1154 " AND (%s))",
1155 clause,
1156 with_prefix ? with_prefix : "",
1157 get->trash ? "_trash" : "",
1158 permission_or->str);
1159
1160 if ((strcmp (type, "report") == 0)
1161 || (strcmp (type, "result") == 0))
1162 g_free (clause);
1163 else
1164 permission_clause = clause;
1165 }
1166
1167 if (strcmp (type, "permission") == 0)
1168 {
1169 int admin;
1170 assert (strcmp (user_id, ""));
1171 admin = acl_user_can_everything (user_id);
1172 /* A user sees permissions that involve the user. Admin users also
1173 * see all higher level permissions. */
1174 owned_clause
1175 = g_strdup_printf (/* Either the user is the owner. */
1176 " ((permissions%s.owner = (%s))"
1177 /* Or, for admins, it's a global permission. */
1178 " %s"
1179 /* Or the permission applies to the user. */
1180 " OR (%i = 0" /* Skip for trash. */
1181 " AND (permissions%s.subject_type = 'user'"
1182 " AND permissions%s.subject_location"
1183 " = " G_STRINGIFY (LOCATION_TABLE)
1184 " AND permissions%s.subject"
1185 " = (%s)))"
1186 /* Or the permission applies to the user's group. */
1187 " OR (%i = 0" /* Skip for trash. */
1188 " AND (permissions%s.subject_type = 'group'"
1189 " AND permissions%s.subject_location"
1190 " = " G_STRINGIFY (LOCATION_TABLE)
1191 " AND permissions%s.subject"
1192 " IN (SELECT DISTINCT \"group\""
1193 " FROM group_users"
1194 " WHERE \"user\" = (%s))))"
1195 /* Or the permission applies to the user's role. */
1196 " OR (%i = 0" /* Skip for trash. */
1197 " AND (permissions%s.subject_type = 'role'"
1198 " AND permissions%s.subject_location"
1199 " = " G_STRINGIFY (LOCATION_TABLE)
1200 " AND permissions%s.subject"
1201 " IN (SELECT DISTINCT role"
1202 " FROM role_users"
1203 " WHERE \"user\" = (%s))))"
1204 /* Or the user has super permission on all. */
1205 " OR EXISTS (SELECT * FROM %spermissions_subject"
1206 " WHERE name = 'Super'"
1207 " AND (resource = 0))"
1208 /* Or the user has super permission on the owner,
1209 * (directly, via the role, or via the group). */
1210 " OR permissions%s.owner IN"
1211 " (SELECT * FROM %ssuper_on_users)"
1212 " %s)",
1213 get->trash ? "_trash" : "",
1214 user_sql,
1215 admin
1216 ? (get->trash
1217 ? "OR (permissions_trash.owner IS NULL)"
1218 : "OR (permissions.owner IS NULL)")
1219 : "",
1220 get->trash,
1221 table_trash ? "_trash" : "",
1222 table_trash ? "_trash" : "",
1223 table_trash ? "_trash" : "",
1224 user_sql,
1225 get->trash,
1226 table_trash ? "_trash" : "",
1227 table_trash ? "_trash" : "",
1228 table_trash ? "_trash" : "",
1229 user_sql,
1230 get->trash,
1231 table_trash ? "_trash" : "",
1232 table_trash ? "_trash" : "",
1233 table_trash ? "_trash" : "",
1234 user_sql,
1235 with_prefix ? with_prefix : "",
1236 table_trash ? "_trash" : "",
1237 with_prefix ? with_prefix : "",
1238 permission_clause ? permission_clause : "");
1239 }
1240 else
1241 /* Any resource type other than Permissions. */
1242 owned_clause
1243 = g_strdup_printf (/* Either the user is the owner. */
1244 " ((%ss%s.owner"
1245 " = (%s))"
1246 /* Or the user has super permission on all. */
1247 " OR EXISTS (SELECT * FROM %spermissions_subject"
1248 " WHERE name = 'Super'"
1249 " AND (resource = 0))"
1250 /* Or the user has super permission on the owner,
1251 * (directly, via the role, or via the group). */
1252 " OR %ss%s.owner IN (SELECT *"
1253 " FROM %ssuper_on_users)"
1254 " %s)",
1255 type,
1256 table_trash ? "_trash" : "",
1257 user_sql,
1258 with_prefix ? with_prefix : "",
1259 type,
1260 table_trash ? "_trash" : "",
1261 with_prefix ? with_prefix : "",
1262 permission_clause ? permission_clause : "");
1263
1264 g_free (permission_clause);
1265 }
1266 else
1267 {
1268 gchar *permission_clause;
1269
1270 /* Caller does not support WITH.
1271 *
1272 * The result_overrides view is the last caller that requires this case,
1273 * because it cannot support this WITH mechanism. */
1274
1275 permission_clause = NULL;
1276
1277 /* Check on index is because default is owner and global, for backward
1278 * compatibility. */
1279 if (user_id && index)
1280 {
1281 gchar *clause;
1282 clause
1283 = g_strdup_printf ("OR EXISTS"
1284 " (SELECT id FROM permissions"
1285 " WHERE resource = %ss%s.id"
1286 " AND resource_type = '%s'"
1287 " AND resource_location = %i"
1288 " AND subject_location"
1289 " = " G_STRINGIFY (LOCATION_TABLE)
1290 " AND ((subject_type = 'user'"
1291 " AND subject"
1292 " = (%s))"
1293 " OR (subject_type = 'group'"
1294 " AND subject"
1295 " IN (SELECT DISTINCT \"group\""
1296 " FROM group_users"
1297 " WHERE \"user\""
1298 " = (%s)))"
1299 " OR (subject_type = 'role'"
1300 " AND subject"
1301 " IN (SELECT DISTINCT role"
1302 " FROM role_users"
1303 " WHERE \"user\""
1304 " = (%s))))"
1305 " AND (%s))",
1306 type,
1307 get->trash && strcmp (type, "task") ? "_trash" : "",
1308 type,
1309 get->trash ? LOCATION_TRASH : LOCATION_TABLE,
1310 user_sql,
1311 user_sql,
1312 user_sql,
1313 permission_or->str);
1314
1315 if (strcmp (type, "report") == 0)
1316 permission_clause
1317 = g_strdup_printf ("%s"
1318 " OR EXISTS"
1319 " (SELECT id FROM permissions"
1320 " WHERE resource = reports%s.task"
1321 " AND resource_type = 'task'"
1322 " AND subject_location"
1323 " = " G_STRINGIFY (LOCATION_TABLE)
1324 " AND ((subject_type = 'user'"
1325 " AND subject"
1326 " = (%s))"
1327 " OR (subject_type = 'group'"
1328 " AND subject"
1329 " IN (SELECT DISTINCT \"group\""
1330 " FROM group_users"
1331 " WHERE \"user\""
1332 " = (%s)))"
1333 " OR (subject_type = 'role'"
1334 " AND subject"
1335 " IN (SELECT DISTINCT role"
1336 " FROM role_users"
1337 " WHERE \"user\""
1338 " = (%s))))"
1339 " AND (%s))",
1340 clause,
1341 get->trash ? "_trash" : "",
1342 user_sql,
1343 user_sql,
1344 user_sql,
1345 permission_or->str);
1346 else if (strcmp (type, "result") == 0)
1347 permission_clause
1348 = g_strdup_printf ("%s"
1349 " OR EXISTS"
1350 " (SELECT id FROM permissions"
1351 " WHERE resource = results%s.task"
1352 " AND resource_type = 'task'"
1353 " AND subject_location"
1354 " = " G_STRINGIFY (LOCATION_TABLE)
1355 " AND ((subject_type = 'user'"
1356 " AND subject"
1357 " = (%s))"
1358 " OR (subject_type = 'group'"
1359 " AND subject"
1360 " IN (SELECT DISTINCT \"group\""
1361 " FROM group_users"
1362 " WHERE \"user\""
1363 " = (%s)))"
1364 " OR (subject_type = 'role'"
1365 " AND subject"
1366 " IN (SELECT DISTINCT role"
1367 " FROM role_users"
1368 " WHERE \"user\""
1369 " = (%s))))"
1370 " AND (%s))",
1371 clause,
1372 get->trash ? "_trash" : "",
1373 user_sql,
1374 user_sql,
1375 user_sql,
1376 permission_or->str);
1377
1378 if ((strcmp (type, "report") == 0)
1379 || (strcmp (type, "result") == 0))
1380 g_free (clause);
1381 else
1382 permission_clause = clause;
1383 }
1384
1385 owned_clause
1386 = g_strdup_printf (/* Either the user is the owner. */
1387 " ((%ss%s.owner"
1388 " = (%s))"
1389 /* Or the user has super permission. */
1390 " OR EXISTS (SELECT * FROM permissions"
1391 " WHERE name = 'Super'"
1392 /* Super on everyone. */
1393 " AND ((resource = 0)"
1394 /* Super on other_user. */
1395 " OR ((resource_type = 'user')"
1396 " AND (resource = %ss%s.owner))"
1397 /* Super on other_user's role. */
1398 " OR ((resource_type = 'role')"
1399 " AND (resource"
1400 " IN (SELECT DISTINCT role"
1401 " FROM role_users"
1402 " WHERE \"user\""
1403 " = %ss%s.owner)))"
1404 /* Super on other_user's group. */
1405 " OR ((resource_type = 'group')"
1406 " AND (resource"
1407 " IN (SELECT DISTINCT \"group\""
1408 " FROM group_users"
1409 " WHERE \"user\""
1410 " = %ss%s.owner))))"
1411 " AND subject_location"
1412 " = " G_STRINGIFY (LOCATION_TABLE)
1413 " AND ((subject_type = 'user'"
1414 " AND subject"
1415 " = (%s))"
1416 " OR (subject_type = 'group'"
1417 " AND subject"
1418 " IN (SELECT DISTINCT \"group\""
1419 " FROM group_users"
1420 " WHERE \"user\""
1421 " = (%s)))"
1422 " OR (subject_type = 'role'"
1423 " AND subject"
1424 " IN (SELECT DISTINCT role"
1425 " FROM role_users"
1426 " WHERE \"user\""
1427 " = (%s)))))"
1428 " %s)",
1429 type,
1430 table_trash ? "_trash" : "",
1431 user_sql,
1432 type,
1433 table_trash ? "_trash" : "",
1434 type,
1435 table_trash ? "_trash" : "",
1436 type,
1437 table_trash ? "_trash" : "",
1438 user_sql,
1439 user_sql,
1440 user_sql,
1441 permission_clause ? permission_clause : "");
1442
1443 g_free (permission_clause);
1444 }
1445
1446 g_string_free (permission_or, TRUE);
1447
1448 if (get->trash && (strcasecmp (type, "task") == 0))
1449 {
1450 gchar *new;
1451 new = g_strdup_printf (" (%ss.hidden = 2"
1452 " AND %s)",
1453 type,
1454 owned_clause);
1455 g_free (owned_clause);
1456 owned_clause = new;
1457 }
1458
1459 if (owner_filter == NULL
1460 || (owner_filter && (strcmp (owner_filter, "any") == 0)))
1461 filter_owned_clause = g_strdup (owned_clause);
1462 else if (owner_filter && strcmp (owner_filter, ""))
1463 {
1464 gchar *quoted;
1465 quoted = sql_quote (owner_filter);
1466 filter_owned_clause = g_strdup_printf ("(owner = (SELECT id"
1467 " FROM users"
1468 " WHERE name = '%s')"
1469 " AND %s)",
1470 quoted,
1471 owned_clause);
1472 g_free (quoted);
1473 }
1474 else
1475 filter_owned_clause = g_strdup_printf ("((owner = (%s))"
1476 " AND %s)",
1477 user_sql,
1478 owned_clause);
1479
1480 g_free (owned_clause);
1481
1482 return filter_owned_clause;
1483 }
1484
1485 /**
1486 * @brief Generate the ownership part of an SQL WHERE clause.
1487 *
1488 * @param[in] type Type of resource.
1489 * @param[in] get GET data.
1490 * @param[in] owned Only get items owned by the current user.
1491 * @param[in] owner_filter Owner filter keyword.
1492 * @param[in] resource Resource.
1493 * @param[in] permissions Permissions.
1494 * @param[in] with_optional Whether permissions WITH clauses are optional.
1495 * @param[out] with Address for WITH clause if allowed, else NULL.
1496 *
1497 * @return Newly allocated owned clause.
1498 */
1499 gchar *
acl_where_owned(const char * type,const get_data_t * get,int owned,const gchar * owner_filter,resource_t resource,array_t * permissions,int with_optional,gchar ** with)1500 acl_where_owned (const char *type, const get_data_t *get, int owned,
1501 const gchar *owner_filter, resource_t resource,
1502 array_t *permissions, int with_optional, gchar **with)
1503 {
1504 gchar *ret, *user_sql;
1505 if (current_credentials.uuid)
1506 user_sql = g_strdup_printf ("SELECT id FROM users WHERE users.uuid = '%s'",
1507 current_credentials.uuid);
1508 else
1509 user_sql = NULL;
1510 ret = acl_where_owned_user (current_credentials.uuid, user_sql, type, get,
1511 owned, owner_filter, resource, permissions,
1512 with_optional, NULL, with);
1513 g_free (user_sql);
1514 return ret;
1515 }
1516
1517 /**
1518 * @brief Generate ownership part of WHERE, for getting a type of resource.
1519 *
1520 * @param[in] type Type of resource.
1521 * @param[in] user_sql SQL for getting user. If NULL SQL will be for current
1522 * user.
1523 * @param[in] with_prefix Optional prefix for WITH clause.
1524 * @param[out] with Return location for WITH preselection clause if
1525 * desired, else NULL.
1526 *
1527 * @return Newly allocated owned clause.
1528 */
1529 gchar *
acl_where_owned_for_get(const char * type,const char * user_sql,const char * with_prefix,gchar ** with)1530 acl_where_owned_for_get (const char *type, const char *user_sql,
1531 const char *with_prefix, gchar **with)
1532 {
1533 gchar *owned_clause;
1534 get_data_t get;
1535 array_t *permissions;
1536 gchar *user_sql_new;
1537
1538 if (user_sql)
1539 user_sql_new = g_strdup (user_sql);
1540 else if (current_credentials.uuid)
1541 user_sql_new = g_strdup_printf ("SELECT id FROM users WHERE users.uuid = '%s'",
1542 current_credentials.uuid);
1543 else
1544 user_sql_new = NULL;
1545
1546 get.trash = 0;
1547 permissions = make_array ();
1548 array_add (permissions, g_strdup_printf ("get_%ss", type));
1549 owned_clause = acl_where_owned_user (current_credentials.uuid
1550 ? current_credentials.uuid
1551 /* Use user_sql_new. */
1552 : "",
1553 user_sql_new,
1554 type,
1555 &get,
1556 1, /* Do owner checks. */
1557 "any",
1558 0, /* Resource. */
1559 permissions,
1560 0, /* WITH not optional */
1561 with_prefix,
1562 with);
1563 array_free (permissions);
1564 g_free (user_sql_new);
1565
1566 return owned_clause;
1567 }
1568
1569 /**
1570 * @brief Get an SQL values expression of users that can get a resource
1571 *
1572 * @param[in] type The resource type.
1573 * @param[in] resource_id The UUID of the resource.
1574 * @param[in] users_where Optional clause to limit users.
1575 *
1576 * @return Newly allocated SQL string or NULL if no users have access.
1577 */
1578
1579 gchar *
acl_users_with_access_sql(const char * type,const char * resource_id,const char * users_where)1580 acl_users_with_access_sql (const char *type, const char *resource_id,
1581 const char *users_where)
1582 {
1583 GString *users_string;
1584 int users_count = 0;
1585 gchar *old_user_id, *command;
1586 iterator_t users;
1587
1588 old_user_id = current_credentials.uuid;
1589 init_iterator (&users, "SELECT id, uuid FROM users WHERE %s;",
1590 users_where ? users_where : "t()");
1591
1592 users_string = g_string_new ("(VALUES ");
1593
1594 command = g_strdup_printf ("get_%ss", type);
1595
1596 while (next (&users))
1597 {
1598 current_credentials.uuid = g_strdup (iterator_string (&users, 1));
1599 manage_session_init (current_credentials.uuid);
1600 if (acl_user_has_access_uuid (type, resource_id, command, 0))
1601 {
1602 if (users_count)
1603 g_string_append (users_string,
1604 ", ");
1605
1606 g_string_append_printf (users_string,
1607 "(%llu)",
1608 iterator_int64 (&users, 0));
1609 users_count ++;
1610 }
1611 g_free (current_credentials.uuid);
1612 }
1613 g_string_append(users_string, ")");
1614 cleanup_iterator (&users);
1615
1616 current_credentials.uuid = old_user_id;
1617 manage_session_init (old_user_id);
1618
1619 g_free (command);
1620
1621 if (users_count == 0)
1622 {
1623 g_string_free (users_string, TRUE);
1624 return NULL;
1625 }
1626
1627 return g_string_free (users_string, FALSE);
1628
1629 }
1630
1631 /**
1632 * @brief Get a static SQL condition selecting users that can get a resource
1633 *
1634 * @param[in] type The resource type.
1635 * @param[in] resource_id The UUID of the resource.
1636 * @param[in] users_where Optional clause to limit users.
1637 * @param[in] user_expr Expression for the user, e.g. the column name.
1638 *
1639 * @return Newly allocated SQL string or NULL if no users have access.
1640 */
1641
1642 gchar *
acl_users_with_access_where(const char * type,const char * resource_id,const char * users_where,const char * user_expr)1643 acl_users_with_access_where (const char *type, const char *resource_id,
1644 const char *users_where, const char* user_expr)
1645 {
1646 gchar *values, *ret;
1647 assert (user_expr);
1648 values = acl_users_with_access_sql (type, resource_id, users_where);
1649 if (values)
1650 ret = g_strdup_printf ("%s IN %s", user_expr, values);
1651 else
1652 ret = g_strdup ("NOT t()");
1653 g_free (values);
1654 return ret;
1655 }
1656