1 | /*------------------------------------------------------------------------- |
2 | * |
3 | * acl.c |
4 | * Basic access control list data structures manipulation routines. |
5 | * |
6 | * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group |
7 | * Portions Copyright (c) 1994, Regents of the University of California |
8 | * |
9 | * |
10 | * IDENTIFICATION |
11 | * src/backend/utils/adt/acl.c |
12 | * |
13 | *------------------------------------------------------------------------- |
14 | */ |
15 | #include "postgres.h" |
16 | |
17 | #include <ctype.h> |
18 | |
19 | #include "access/htup_details.h" |
20 | #include "catalog/catalog.h" |
21 | #include "catalog/namespace.h" |
22 | #include "catalog/pg_authid.h" |
23 | #include "catalog/pg_auth_members.h" |
24 | #include "catalog/pg_type.h" |
25 | #include "catalog/pg_class.h" |
26 | #include "commands/dbcommands.h" |
27 | #include "commands/proclang.h" |
28 | #include "commands/tablespace.h" |
29 | #include "foreign/foreign.h" |
30 | #include "funcapi.h" |
31 | #include "miscadmin.h" |
32 | #include "utils/acl.h" |
33 | #include "utils/builtins.h" |
34 | #include "utils/catcache.h" |
35 | #include "utils/hashutils.h" |
36 | #include "utils/inval.h" |
37 | #include "utils/lsyscache.h" |
38 | #include "utils/memutils.h" |
39 | #include "utils/syscache.h" |
40 | #include "utils/varlena.h" |
41 | |
42 | |
43 | typedef struct |
44 | { |
45 | const char *name; |
46 | AclMode value; |
47 | } priv_map; |
48 | |
49 | /* |
50 | * We frequently need to test whether a given role is a member of some other |
51 | * role. In most of these tests the "given role" is the same, namely the |
52 | * active current user. So we can optimize it by keeping a cached list of |
53 | * all the roles the "given role" is a member of, directly or indirectly. |
54 | * The cache is flushed whenever we detect a change in pg_auth_members. |
55 | * |
56 | * There are actually two caches, one computed under "has_privs" rules |
57 | * (do not recurse where rolinherit isn't true) and one computed under |
58 | * "is_member" rules (recurse regardless of rolinherit). |
59 | * |
60 | * Possibly this mechanism should be generalized to allow caching membership |
61 | * info for multiple roles? |
62 | * |
63 | * The has_privs cache is: |
64 | * cached_privs_role is the role OID the cache is for. |
65 | * cached_privs_roles is an OID list of roles that cached_privs_role |
66 | * has the privileges of (always including itself). |
67 | * The cache is valid if cached_privs_role is not InvalidOid. |
68 | * |
69 | * The is_member cache is similarly: |
70 | * cached_member_role is the role OID the cache is for. |
71 | * cached_membership_roles is an OID list of roles that cached_member_role |
72 | * is a member of (always including itself). |
73 | * The cache is valid if cached_member_role is not InvalidOid. |
74 | */ |
75 | static Oid cached_privs_role = InvalidOid; |
76 | static List *cached_privs_roles = NIL; |
77 | static Oid cached_member_role = InvalidOid; |
78 | static List *cached_membership_roles = NIL; |
79 | |
80 | |
81 | static const char *getid(const char *s, char *n); |
82 | static void putid(char *p, const char *s); |
83 | static Acl *allocacl(int n); |
84 | static void check_acl(const Acl *acl); |
85 | static const char *aclparse(const char *s, AclItem *aip); |
86 | static bool aclitem_match(const AclItem *a1, const AclItem *a2); |
87 | static int aclitemComparator(const void *arg1, const void *arg2); |
88 | static void check_circularity(const Acl *old_acl, const AclItem *mod_aip, |
89 | Oid ownerId); |
90 | static Acl *recursive_revoke(Acl *acl, Oid grantee, AclMode revoke_privs, |
91 | Oid ownerId, DropBehavior behavior); |
92 | |
93 | static AclMode convert_priv_string(text *priv_type_text); |
94 | static AclMode convert_any_priv_string(text *priv_type_text, |
95 | const priv_map *privileges); |
96 | |
97 | static Oid convert_table_name(text *tablename); |
98 | static AclMode convert_table_priv_string(text *priv_type_text); |
99 | static AclMode convert_sequence_priv_string(text *priv_type_text); |
100 | static AttrNumber convert_column_name(Oid tableoid, text *column); |
101 | static AclMode convert_column_priv_string(text *priv_type_text); |
102 | static Oid convert_database_name(text *databasename); |
103 | static AclMode convert_database_priv_string(text *priv_type_text); |
104 | static Oid convert_foreign_data_wrapper_name(text *fdwname); |
105 | static AclMode convert_foreign_data_wrapper_priv_string(text *priv_type_text); |
106 | static Oid convert_function_name(text *functionname); |
107 | static AclMode convert_function_priv_string(text *priv_type_text); |
108 | static Oid convert_language_name(text *languagename); |
109 | static AclMode convert_language_priv_string(text *priv_type_text); |
110 | static Oid convert_schema_name(text *schemaname); |
111 | static AclMode convert_schema_priv_string(text *priv_type_text); |
112 | static Oid convert_server_name(text *servername); |
113 | static AclMode convert_server_priv_string(text *priv_type_text); |
114 | static Oid convert_tablespace_name(text *tablespacename); |
115 | static AclMode convert_tablespace_priv_string(text *priv_type_text); |
116 | static Oid convert_type_name(text *typename); |
117 | static AclMode convert_type_priv_string(text *priv_type_text); |
118 | static AclMode convert_role_priv_string(text *priv_type_text); |
119 | static AclResult pg_role_aclcheck(Oid role_oid, Oid roleid, AclMode mode); |
120 | |
121 | static void RoleMembershipCacheCallback(Datum arg, int cacheid, uint32 hashvalue); |
122 | |
123 | |
124 | /* |
125 | * getid |
126 | * Consumes the first alphanumeric string (identifier) found in string |
127 | * 's', ignoring any leading white space. If it finds a double quote |
128 | * it returns the word inside the quotes. |
129 | * |
130 | * RETURNS: |
131 | * the string position in 's' that points to the next non-space character |
132 | * in 's', after any quotes. Also: |
133 | * - loads the identifier into 'n'. (If no identifier is found, 'n' |
134 | * contains an empty string.) 'n' must be NAMEDATALEN bytes. |
135 | */ |
136 | static const char * |
137 | getid(const char *s, char *n) |
138 | { |
139 | int len = 0; |
140 | bool in_quotes = false; |
141 | |
142 | Assert(s && n); |
143 | |
144 | while (isspace((unsigned char) *s)) |
145 | s++; |
146 | /* This code had better match what putid() does, below */ |
147 | for (; |
148 | *s != '\0' && |
149 | (isalnum((unsigned char) *s) || |
150 | *s == '_' || |
151 | *s == '"' || |
152 | in_quotes); |
153 | s++) |
154 | { |
155 | if (*s == '"') |
156 | { |
157 | /* safe to look at next char (could be '\0' though) */ |
158 | if (*(s + 1) != '"') |
159 | { |
160 | in_quotes = !in_quotes; |
161 | continue; |
162 | } |
163 | /* it's an escaped double quote; skip the escaping char */ |
164 | s++; |
165 | } |
166 | |
167 | /* Add the character to the string */ |
168 | if (len >= NAMEDATALEN - 1) |
169 | ereport(ERROR, |
170 | (errcode(ERRCODE_NAME_TOO_LONG), |
171 | errmsg("identifier too long" ), |
172 | errdetail("Identifier must be less than %d characters." , |
173 | NAMEDATALEN))); |
174 | |
175 | n[len++] = *s; |
176 | } |
177 | n[len] = '\0'; |
178 | while (isspace((unsigned char) *s)) |
179 | s++; |
180 | return s; |
181 | } |
182 | |
183 | /* |
184 | * Write a role name at *p, adding double quotes if needed. |
185 | * There must be at least (2*NAMEDATALEN)+2 bytes available at *p. |
186 | * This needs to be kept in sync with copyAclUserName in pg_dump/dumputils.c |
187 | */ |
188 | static void |
189 | putid(char *p, const char *s) |
190 | { |
191 | const char *src; |
192 | bool safe = true; |
193 | |
194 | for (src = s; *src; src++) |
195 | { |
196 | /* This test had better match what getid() does, above */ |
197 | if (!isalnum((unsigned char) *src) && *src != '_') |
198 | { |
199 | safe = false; |
200 | break; |
201 | } |
202 | } |
203 | if (!safe) |
204 | *p++ = '"'; |
205 | for (src = s; *src; src++) |
206 | { |
207 | /* A double quote character in a username is encoded as "" */ |
208 | if (*src == '"') |
209 | *p++ = '"'; |
210 | *p++ = *src; |
211 | } |
212 | if (!safe) |
213 | *p++ = '"'; |
214 | *p = '\0'; |
215 | } |
216 | |
217 | /* |
218 | * aclparse |
219 | * Consumes and parses an ACL specification of the form: |
220 | * [group|user] [A-Za-z0-9]*=[rwaR]* |
221 | * from string 's', ignoring any leading white space or white space |
222 | * between the optional id type keyword (group|user) and the actual |
223 | * ACL specification. |
224 | * |
225 | * The group|user decoration is unnecessary in the roles world, |
226 | * but we still accept it for backward compatibility. |
227 | * |
228 | * This routine is called by the parser as well as aclitemin(), hence |
229 | * the added generality. |
230 | * |
231 | * RETURNS: |
232 | * the string position in 's' immediately following the ACL |
233 | * specification. Also: |
234 | * - loads the structure pointed to by 'aip' with the appropriate |
235 | * UID/GID, id type identifier and mode type values. |
236 | */ |
237 | static const char * |
238 | aclparse(const char *s, AclItem *aip) |
239 | { |
240 | AclMode privs, |
241 | goption, |
242 | read; |
243 | char name[NAMEDATALEN]; |
244 | char name2[NAMEDATALEN]; |
245 | |
246 | Assert(s && aip); |
247 | |
248 | #ifdef ACLDEBUG |
249 | elog(LOG, "aclparse: input = \"%s\"" , s); |
250 | #endif |
251 | s = getid(s, name); |
252 | if (*s != '=') |
253 | { |
254 | /* we just read a keyword, not a name */ |
255 | if (strcmp(name, "group" ) != 0 && strcmp(name, "user" ) != 0) |
256 | ereport(ERROR, |
257 | (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), |
258 | errmsg("unrecognized key word: \"%s\"" , name), |
259 | errhint("ACL key word must be \"group\" or \"user\"." ))); |
260 | s = getid(s, name); /* move s to the name beyond the keyword */ |
261 | if (name[0] == '\0') |
262 | ereport(ERROR, |
263 | (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), |
264 | errmsg("missing name" ), |
265 | errhint("A name must follow the \"group\" or \"user\" key word." ))); |
266 | } |
267 | |
268 | if (*s != '=') |
269 | ereport(ERROR, |
270 | (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), |
271 | errmsg("missing \"=\" sign" ))); |
272 | |
273 | privs = goption = ACL_NO_RIGHTS; |
274 | |
275 | for (++s, read = 0; isalpha((unsigned char) *s) || *s == '*'; s++) |
276 | { |
277 | switch (*s) |
278 | { |
279 | case '*': |
280 | goption |= read; |
281 | break; |
282 | case ACL_INSERT_CHR: |
283 | read = ACL_INSERT; |
284 | break; |
285 | case ACL_SELECT_CHR: |
286 | read = ACL_SELECT; |
287 | break; |
288 | case ACL_UPDATE_CHR: |
289 | read = ACL_UPDATE; |
290 | break; |
291 | case ACL_DELETE_CHR: |
292 | read = ACL_DELETE; |
293 | break; |
294 | case ACL_TRUNCATE_CHR: |
295 | read = ACL_TRUNCATE; |
296 | break; |
297 | case ACL_REFERENCES_CHR: |
298 | read = ACL_REFERENCES; |
299 | break; |
300 | case ACL_TRIGGER_CHR: |
301 | read = ACL_TRIGGER; |
302 | break; |
303 | case ACL_EXECUTE_CHR: |
304 | read = ACL_EXECUTE; |
305 | break; |
306 | case ACL_USAGE_CHR: |
307 | read = ACL_USAGE; |
308 | break; |
309 | case ACL_CREATE_CHR: |
310 | read = ACL_CREATE; |
311 | break; |
312 | case ACL_CREATE_TEMP_CHR: |
313 | read = ACL_CREATE_TEMP; |
314 | break; |
315 | case ACL_CONNECT_CHR: |
316 | read = ACL_CONNECT; |
317 | break; |
318 | case 'R': /* ignore old RULE privileges */ |
319 | read = 0; |
320 | break; |
321 | default: |
322 | ereport(ERROR, |
323 | (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), |
324 | errmsg("invalid mode character: must be one of \"%s\"" , |
325 | ACL_ALL_RIGHTS_STR))); |
326 | } |
327 | |
328 | privs |= read; |
329 | } |
330 | |
331 | if (name[0] == '\0') |
332 | aip->ai_grantee = ACL_ID_PUBLIC; |
333 | else |
334 | aip->ai_grantee = get_role_oid(name, false); |
335 | |
336 | /* |
337 | * XXX Allow a degree of backward compatibility by defaulting the grantor |
338 | * to the superuser. |
339 | */ |
340 | if (*s == '/') |
341 | { |
342 | s = getid(s + 1, name2); |
343 | if (name2[0] == '\0') |
344 | ereport(ERROR, |
345 | (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), |
346 | errmsg("a name must follow the \"/\" sign" ))); |
347 | aip->ai_grantor = get_role_oid(name2, false); |
348 | } |
349 | else |
350 | { |
351 | aip->ai_grantor = BOOTSTRAP_SUPERUSERID; |
352 | ereport(WARNING, |
353 | (errcode(ERRCODE_INVALID_GRANTOR), |
354 | errmsg("defaulting grantor to user ID %u" , |
355 | BOOTSTRAP_SUPERUSERID))); |
356 | } |
357 | |
358 | ACLITEM_SET_PRIVS_GOPTIONS(*aip, privs, goption); |
359 | |
360 | #ifdef ACLDEBUG |
361 | elog(LOG, "aclparse: correctly read [%u %x %x]" , |
362 | aip->ai_grantee, privs, goption); |
363 | #endif |
364 | |
365 | return s; |
366 | } |
367 | |
368 | /* |
369 | * allocacl |
370 | * Allocates storage for a new Acl with 'n' entries. |
371 | * |
372 | * RETURNS: |
373 | * the new Acl |
374 | */ |
375 | static Acl * |
376 | allocacl(int n) |
377 | { |
378 | Acl *new_acl; |
379 | Size size; |
380 | |
381 | if (n < 0) |
382 | elog(ERROR, "invalid size: %d" , n); |
383 | size = ACL_N_SIZE(n); |
384 | new_acl = (Acl *) palloc0(size); |
385 | SET_VARSIZE(new_acl, size); |
386 | new_acl->ndim = 1; |
387 | new_acl->dataoffset = 0; /* we never put in any nulls */ |
388 | new_acl->elemtype = ACLITEMOID; |
389 | ARR_LBOUND(new_acl)[0] = 1; |
390 | ARR_DIMS(new_acl)[0] = n; |
391 | return new_acl; |
392 | } |
393 | |
394 | /* |
395 | * Create a zero-entry ACL |
396 | */ |
397 | Acl * |
398 | make_empty_acl(void) |
399 | { |
400 | return allocacl(0); |
401 | } |
402 | |
403 | /* |
404 | * Copy an ACL |
405 | */ |
406 | Acl * |
407 | aclcopy(const Acl *orig_acl) |
408 | { |
409 | Acl *result_acl; |
410 | |
411 | result_acl = allocacl(ACL_NUM(orig_acl)); |
412 | |
413 | memcpy(ACL_DAT(result_acl), |
414 | ACL_DAT(orig_acl), |
415 | ACL_NUM(orig_acl) * sizeof(AclItem)); |
416 | |
417 | return result_acl; |
418 | } |
419 | |
420 | /* |
421 | * Concatenate two ACLs |
422 | * |
423 | * This is a bit cheesy, since we may produce an ACL with redundant entries. |
424 | * Be careful what the result is used for! |
425 | */ |
426 | Acl * |
427 | aclconcat(const Acl *left_acl, const Acl *right_acl) |
428 | { |
429 | Acl *result_acl; |
430 | |
431 | result_acl = allocacl(ACL_NUM(left_acl) + ACL_NUM(right_acl)); |
432 | |
433 | memcpy(ACL_DAT(result_acl), |
434 | ACL_DAT(left_acl), |
435 | ACL_NUM(left_acl) * sizeof(AclItem)); |
436 | |
437 | memcpy(ACL_DAT(result_acl) + ACL_NUM(left_acl), |
438 | ACL_DAT(right_acl), |
439 | ACL_NUM(right_acl) * sizeof(AclItem)); |
440 | |
441 | return result_acl; |
442 | } |
443 | |
444 | /* |
445 | * Merge two ACLs |
446 | * |
447 | * This produces a properly merged ACL with no redundant entries. |
448 | * Returns NULL on NULL input. |
449 | */ |
450 | Acl * |
451 | aclmerge(const Acl *left_acl, const Acl *right_acl, Oid ownerId) |
452 | { |
453 | Acl *result_acl; |
454 | AclItem *aip; |
455 | int i, |
456 | num; |
457 | |
458 | /* Check for cases where one or both are empty/null */ |
459 | if (left_acl == NULL || ACL_NUM(left_acl) == 0) |
460 | { |
461 | if (right_acl == NULL || ACL_NUM(right_acl) == 0) |
462 | return NULL; |
463 | else |
464 | return aclcopy(right_acl); |
465 | } |
466 | else |
467 | { |
468 | if (right_acl == NULL || ACL_NUM(right_acl) == 0) |
469 | return aclcopy(left_acl); |
470 | } |
471 | |
472 | /* Merge them the hard way, one item at a time */ |
473 | result_acl = aclcopy(left_acl); |
474 | |
475 | aip = ACL_DAT(right_acl); |
476 | num = ACL_NUM(right_acl); |
477 | |
478 | for (i = 0; i < num; i++, aip++) |
479 | { |
480 | Acl *tmp_acl; |
481 | |
482 | tmp_acl = aclupdate(result_acl, aip, ACL_MODECHG_ADD, |
483 | ownerId, DROP_RESTRICT); |
484 | pfree(result_acl); |
485 | result_acl = tmp_acl; |
486 | } |
487 | |
488 | return result_acl; |
489 | } |
490 | |
491 | /* |
492 | * Sort the items in an ACL (into an arbitrary but consistent order) |
493 | */ |
494 | void |
495 | aclitemsort(Acl *acl) |
496 | { |
497 | if (acl != NULL && ACL_NUM(acl) > 1) |
498 | qsort(ACL_DAT(acl), ACL_NUM(acl), sizeof(AclItem), aclitemComparator); |
499 | } |
500 | |
501 | /* |
502 | * Check if two ACLs are exactly equal |
503 | * |
504 | * This will not detect equality if the two arrays contain the same items |
505 | * in different orders. To handle that case, sort both inputs first, |
506 | * using aclitemsort(). |
507 | */ |
508 | bool |
509 | aclequal(const Acl *left_acl, const Acl *right_acl) |
510 | { |
511 | /* Check for cases where one or both are empty/null */ |
512 | if (left_acl == NULL || ACL_NUM(left_acl) == 0) |
513 | { |
514 | if (right_acl == NULL || ACL_NUM(right_acl) == 0) |
515 | return true; |
516 | else |
517 | return false; |
518 | } |
519 | else |
520 | { |
521 | if (right_acl == NULL || ACL_NUM(right_acl) == 0) |
522 | return false; |
523 | } |
524 | |
525 | if (ACL_NUM(left_acl) != ACL_NUM(right_acl)) |
526 | return false; |
527 | |
528 | if (memcmp(ACL_DAT(left_acl), |
529 | ACL_DAT(right_acl), |
530 | ACL_NUM(left_acl) * sizeof(AclItem)) == 0) |
531 | return true; |
532 | |
533 | return false; |
534 | } |
535 | |
536 | /* |
537 | * Verify that an ACL array is acceptable (one-dimensional and has no nulls) |
538 | */ |
539 | static void |
540 | check_acl(const Acl *acl) |
541 | { |
542 | if (ARR_ELEMTYPE(acl) != ACLITEMOID) |
543 | ereport(ERROR, |
544 | (errcode(ERRCODE_INVALID_PARAMETER_VALUE), |
545 | errmsg("ACL array contains wrong data type" ))); |
546 | if (ARR_NDIM(acl) != 1) |
547 | ereport(ERROR, |
548 | (errcode(ERRCODE_INVALID_PARAMETER_VALUE), |
549 | errmsg("ACL arrays must be one-dimensional" ))); |
550 | if (ARR_HASNULL(acl)) |
551 | ereport(ERROR, |
552 | (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), |
553 | errmsg("ACL arrays must not contain null values" ))); |
554 | } |
555 | |
556 | /* |
557 | * aclitemin |
558 | * Allocates storage for, and fills in, a new AclItem given a string |
559 | * 's' that contains an ACL specification. See aclparse for details. |
560 | * |
561 | * RETURNS: |
562 | * the new AclItem |
563 | */ |
564 | Datum |
565 | aclitemin(PG_FUNCTION_ARGS) |
566 | { |
567 | const char *s = PG_GETARG_CSTRING(0); |
568 | AclItem *aip; |
569 | |
570 | aip = (AclItem *) palloc(sizeof(AclItem)); |
571 | s = aclparse(s, aip); |
572 | while (isspace((unsigned char) *s)) |
573 | ++s; |
574 | if (*s) |
575 | ereport(ERROR, |
576 | (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), |
577 | errmsg("extra garbage at the end of the ACL specification" ))); |
578 | |
579 | PG_RETURN_ACLITEM_P(aip); |
580 | } |
581 | |
582 | /* |
583 | * aclitemout |
584 | * Allocates storage for, and fills in, a new null-delimited string |
585 | * containing a formatted ACL specification. See aclparse for details. |
586 | * |
587 | * RETURNS: |
588 | * the new string |
589 | */ |
590 | Datum |
591 | aclitemout(PG_FUNCTION_ARGS) |
592 | { |
593 | AclItem *aip = PG_GETARG_ACLITEM_P(0); |
594 | char *p; |
595 | char *out; |
596 | HeapTuple htup; |
597 | unsigned i; |
598 | |
599 | out = palloc(strlen("=/" ) + |
600 | 2 * N_ACL_RIGHTS + |
601 | 2 * (2 * NAMEDATALEN + 2) + |
602 | 1); |
603 | |
604 | p = out; |
605 | *p = '\0'; |
606 | |
607 | if (aip->ai_grantee != ACL_ID_PUBLIC) |
608 | { |
609 | htup = SearchSysCache1(AUTHOID, ObjectIdGetDatum(aip->ai_grantee)); |
610 | if (HeapTupleIsValid(htup)) |
611 | { |
612 | putid(p, NameStr(((Form_pg_authid) GETSTRUCT(htup))->rolname)); |
613 | ReleaseSysCache(htup); |
614 | } |
615 | else |
616 | { |
617 | /* Generate numeric OID if we don't find an entry */ |
618 | sprintf(p, "%u" , aip->ai_grantee); |
619 | } |
620 | } |
621 | while (*p) |
622 | ++p; |
623 | |
624 | *p++ = '='; |
625 | |
626 | for (i = 0; i < N_ACL_RIGHTS; ++i) |
627 | { |
628 | if (ACLITEM_GET_PRIVS(*aip) & (1 << i)) |
629 | *p++ = ACL_ALL_RIGHTS_STR[i]; |
630 | if (ACLITEM_GET_GOPTIONS(*aip) & (1 << i)) |
631 | *p++ = '*'; |
632 | } |
633 | |
634 | *p++ = '/'; |
635 | *p = '\0'; |
636 | |
637 | htup = SearchSysCache1(AUTHOID, ObjectIdGetDatum(aip->ai_grantor)); |
638 | if (HeapTupleIsValid(htup)) |
639 | { |
640 | putid(p, NameStr(((Form_pg_authid) GETSTRUCT(htup))->rolname)); |
641 | ReleaseSysCache(htup); |
642 | } |
643 | else |
644 | { |
645 | /* Generate numeric OID if we don't find an entry */ |
646 | sprintf(p, "%u" , aip->ai_grantor); |
647 | } |
648 | |
649 | PG_RETURN_CSTRING(out); |
650 | } |
651 | |
652 | /* |
653 | * aclitem_match |
654 | * Two AclItems are considered to match iff they have the same |
655 | * grantee and grantor; the privileges are ignored. |
656 | */ |
657 | static bool |
658 | aclitem_match(const AclItem *a1, const AclItem *a2) |
659 | { |
660 | return a1->ai_grantee == a2->ai_grantee && |
661 | a1->ai_grantor == a2->ai_grantor; |
662 | } |
663 | |
664 | /* |
665 | * aclitemComparator |
666 | * qsort comparison function for AclItems |
667 | */ |
668 | static int |
669 | aclitemComparator(const void *arg1, const void *arg2) |
670 | { |
671 | const AclItem *a1 = (const AclItem *) arg1; |
672 | const AclItem *a2 = (const AclItem *) arg2; |
673 | |
674 | if (a1->ai_grantee > a2->ai_grantee) |
675 | return 1; |
676 | if (a1->ai_grantee < a2->ai_grantee) |
677 | return -1; |
678 | if (a1->ai_grantor > a2->ai_grantor) |
679 | return 1; |
680 | if (a1->ai_grantor < a2->ai_grantor) |
681 | return -1; |
682 | if (a1->ai_privs > a2->ai_privs) |
683 | return 1; |
684 | if (a1->ai_privs < a2->ai_privs) |
685 | return -1; |
686 | return 0; |
687 | } |
688 | |
689 | /* |
690 | * aclitem equality operator |
691 | */ |
692 | Datum |
693 | aclitem_eq(PG_FUNCTION_ARGS) |
694 | { |
695 | AclItem *a1 = PG_GETARG_ACLITEM_P(0); |
696 | AclItem *a2 = PG_GETARG_ACLITEM_P(1); |
697 | bool result; |
698 | |
699 | result = a1->ai_privs == a2->ai_privs && |
700 | a1->ai_grantee == a2->ai_grantee && |
701 | a1->ai_grantor == a2->ai_grantor; |
702 | PG_RETURN_BOOL(result); |
703 | } |
704 | |
705 | /* |
706 | * aclitem hash function |
707 | * |
708 | * We make aclitems hashable not so much because anyone is likely to hash |
709 | * them, as because we want array equality to work on aclitem arrays, and |
710 | * with the typcache mechanism we must have a hash or btree opclass. |
711 | */ |
712 | Datum |
713 | hash_aclitem(PG_FUNCTION_ARGS) |
714 | { |
715 | AclItem *a = PG_GETARG_ACLITEM_P(0); |
716 | |
717 | /* not very bright, but avoids any issue of padding in struct */ |
718 | PG_RETURN_UINT32((uint32) (a->ai_privs + a->ai_grantee + a->ai_grantor)); |
719 | } |
720 | |
721 | /* |
722 | * 64-bit hash function for aclitem. |
723 | * |
724 | * Similar to hash_aclitem, but accepts a seed and returns a uint64 value. |
725 | */ |
726 | Datum |
727 | hash_aclitem_extended(PG_FUNCTION_ARGS) |
728 | { |
729 | AclItem *a = PG_GETARG_ACLITEM_P(0); |
730 | uint64 seed = PG_GETARG_INT64(1); |
731 | uint32 sum = (uint32) (a->ai_privs + a->ai_grantee + a->ai_grantor); |
732 | |
733 | return (seed == 0) ? UInt64GetDatum(sum) : hash_uint32_extended(sum, seed); |
734 | } |
735 | |
736 | /* |
737 | * acldefault() --- create an ACL describing default access permissions |
738 | * |
739 | * Change this routine if you want to alter the default access policy for |
740 | * newly-created objects (or any object with a NULL acl entry). When |
741 | * you make a change here, don't forget to update the GRANT man page, |
742 | * which explains all the default permissions. |
743 | * |
744 | * Note that these are the hard-wired "defaults" that are used in the |
745 | * absence of any pg_default_acl entry. |
746 | */ |
747 | Acl * |
748 | acldefault(ObjectType objtype, Oid ownerId) |
749 | { |
750 | AclMode world_default; |
751 | AclMode owner_default; |
752 | int nacl; |
753 | Acl *acl; |
754 | AclItem *aip; |
755 | |
756 | switch (objtype) |
757 | { |
758 | case OBJECT_COLUMN: |
759 | /* by default, columns have no extra privileges */ |
760 | world_default = ACL_NO_RIGHTS; |
761 | owner_default = ACL_NO_RIGHTS; |
762 | break; |
763 | case OBJECT_TABLE: |
764 | world_default = ACL_NO_RIGHTS; |
765 | owner_default = ACL_ALL_RIGHTS_RELATION; |
766 | break; |
767 | case OBJECT_SEQUENCE: |
768 | world_default = ACL_NO_RIGHTS; |
769 | owner_default = ACL_ALL_RIGHTS_SEQUENCE; |
770 | break; |
771 | case OBJECT_DATABASE: |
772 | /* for backwards compatibility, grant some rights by default */ |
773 | world_default = ACL_CREATE_TEMP | ACL_CONNECT; |
774 | owner_default = ACL_ALL_RIGHTS_DATABASE; |
775 | break; |
776 | case OBJECT_FUNCTION: |
777 | /* Grant EXECUTE by default, for now */ |
778 | world_default = ACL_EXECUTE; |
779 | owner_default = ACL_ALL_RIGHTS_FUNCTION; |
780 | break; |
781 | case OBJECT_LANGUAGE: |
782 | /* Grant USAGE by default, for now */ |
783 | world_default = ACL_USAGE; |
784 | owner_default = ACL_ALL_RIGHTS_LANGUAGE; |
785 | break; |
786 | case OBJECT_LARGEOBJECT: |
787 | world_default = ACL_NO_RIGHTS; |
788 | owner_default = ACL_ALL_RIGHTS_LARGEOBJECT; |
789 | break; |
790 | case OBJECT_SCHEMA: |
791 | world_default = ACL_NO_RIGHTS; |
792 | owner_default = ACL_ALL_RIGHTS_SCHEMA; |
793 | break; |
794 | case OBJECT_TABLESPACE: |
795 | world_default = ACL_NO_RIGHTS; |
796 | owner_default = ACL_ALL_RIGHTS_TABLESPACE; |
797 | break; |
798 | case OBJECT_FDW: |
799 | world_default = ACL_NO_RIGHTS; |
800 | owner_default = ACL_ALL_RIGHTS_FDW; |
801 | break; |
802 | case OBJECT_FOREIGN_SERVER: |
803 | world_default = ACL_NO_RIGHTS; |
804 | owner_default = ACL_ALL_RIGHTS_FOREIGN_SERVER; |
805 | break; |
806 | case OBJECT_DOMAIN: |
807 | case OBJECT_TYPE: |
808 | world_default = ACL_USAGE; |
809 | owner_default = ACL_ALL_RIGHTS_TYPE; |
810 | break; |
811 | default: |
812 | elog(ERROR, "unrecognized objtype: %d" , (int) objtype); |
813 | world_default = ACL_NO_RIGHTS; /* keep compiler quiet */ |
814 | owner_default = ACL_NO_RIGHTS; |
815 | break; |
816 | } |
817 | |
818 | nacl = 0; |
819 | if (world_default != ACL_NO_RIGHTS) |
820 | nacl++; |
821 | if (owner_default != ACL_NO_RIGHTS) |
822 | nacl++; |
823 | |
824 | acl = allocacl(nacl); |
825 | aip = ACL_DAT(acl); |
826 | |
827 | if (world_default != ACL_NO_RIGHTS) |
828 | { |
829 | aip->ai_grantee = ACL_ID_PUBLIC; |
830 | aip->ai_grantor = ownerId; |
831 | ACLITEM_SET_PRIVS_GOPTIONS(*aip, world_default, ACL_NO_RIGHTS); |
832 | aip++; |
833 | } |
834 | |
835 | /* |
836 | * Note that the owner's entry shows all ordinary privileges but no grant |
837 | * options. This is because his grant options come "from the system" and |
838 | * not from his own efforts. (The SQL spec says that the owner's rights |
839 | * come from a "_SYSTEM" authid.) However, we do consider that the |
840 | * owner's ordinary privileges are self-granted; this lets him revoke |
841 | * them. We implement the owner's grant options without any explicit |
842 | * "_SYSTEM"-like ACL entry, by internally special-casing the owner |
843 | * wherever we are testing grant options. |
844 | */ |
845 | if (owner_default != ACL_NO_RIGHTS) |
846 | { |
847 | aip->ai_grantee = ownerId; |
848 | aip->ai_grantor = ownerId; |
849 | ACLITEM_SET_PRIVS_GOPTIONS(*aip, owner_default, ACL_NO_RIGHTS); |
850 | } |
851 | |
852 | return acl; |
853 | } |
854 | |
855 | |
856 | /* |
857 | * SQL-accessible version of acldefault(). Hackish mapping from "char" type to |
858 | * OBJECT_* values. |
859 | */ |
860 | Datum |
861 | acldefault_sql(PG_FUNCTION_ARGS) |
862 | { |
863 | char objtypec = PG_GETARG_CHAR(0); |
864 | Oid owner = PG_GETARG_OID(1); |
865 | ObjectType objtype = 0; |
866 | |
867 | switch (objtypec) |
868 | { |
869 | case 'c': |
870 | objtype = OBJECT_COLUMN; |
871 | break; |
872 | case 'r': |
873 | objtype = OBJECT_TABLE; |
874 | break; |
875 | case 's': |
876 | objtype = OBJECT_SEQUENCE; |
877 | break; |
878 | case 'd': |
879 | objtype = OBJECT_DATABASE; |
880 | break; |
881 | case 'f': |
882 | objtype = OBJECT_FUNCTION; |
883 | break; |
884 | case 'l': |
885 | objtype = OBJECT_LANGUAGE; |
886 | break; |
887 | case 'L': |
888 | objtype = OBJECT_LARGEOBJECT; |
889 | break; |
890 | case 'n': |
891 | objtype = OBJECT_SCHEMA; |
892 | break; |
893 | case 't': |
894 | objtype = OBJECT_TABLESPACE; |
895 | break; |
896 | case 'F': |
897 | objtype = OBJECT_FDW; |
898 | break; |
899 | case 'S': |
900 | objtype = OBJECT_FOREIGN_SERVER; |
901 | break; |
902 | case 'T': |
903 | objtype = OBJECT_TYPE; |
904 | break; |
905 | default: |
906 | elog(ERROR, "unrecognized objtype abbreviation: %c" , objtypec); |
907 | } |
908 | |
909 | PG_RETURN_ACL_P(acldefault(objtype, owner)); |
910 | } |
911 | |
912 | |
913 | /* |
914 | * Update an ACL array to add or remove specified privileges. |
915 | * |
916 | * old_acl: the input ACL array |
917 | * mod_aip: defines the privileges to be added, removed, or substituted |
918 | * modechg: ACL_MODECHG_ADD, ACL_MODECHG_DEL, or ACL_MODECHG_EQL |
919 | * ownerId: Oid of object owner |
920 | * behavior: RESTRICT or CASCADE behavior for recursive removal |
921 | * |
922 | * ownerid and behavior are only relevant when the update operation specifies |
923 | * deletion of grant options. |
924 | * |
925 | * The result is a modified copy; the input object is not changed. |
926 | * |
927 | * NB: caller is responsible for having detoasted the input ACL, if needed. |
928 | */ |
929 | Acl * |
930 | aclupdate(const Acl *old_acl, const AclItem *mod_aip, |
931 | int modechg, Oid ownerId, DropBehavior behavior) |
932 | { |
933 | Acl *new_acl = NULL; |
934 | AclItem *old_aip, |
935 | *new_aip = NULL; |
936 | AclMode old_rights, |
937 | old_goptions, |
938 | new_rights, |
939 | new_goptions; |
940 | int dst, |
941 | num; |
942 | |
943 | /* Caller probably already checked old_acl, but be safe */ |
944 | check_acl(old_acl); |
945 | |
946 | /* If granting grant options, check for circularity */ |
947 | if (modechg != ACL_MODECHG_DEL && |
948 | ACLITEM_GET_GOPTIONS(*mod_aip) != ACL_NO_RIGHTS) |
949 | check_circularity(old_acl, mod_aip, ownerId); |
950 | |
951 | num = ACL_NUM(old_acl); |
952 | old_aip = ACL_DAT(old_acl); |
953 | |
954 | /* |
955 | * Search the ACL for an existing entry for this grantee and grantor. If |
956 | * one exists, just modify the entry in-place (well, in the same position, |
957 | * since we actually return a copy); otherwise, insert the new entry at |
958 | * the end. |
959 | */ |
960 | |
961 | for (dst = 0; dst < num; ++dst) |
962 | { |
963 | if (aclitem_match(mod_aip, old_aip + dst)) |
964 | { |
965 | /* found a match, so modify existing item */ |
966 | new_acl = allocacl(num); |
967 | new_aip = ACL_DAT(new_acl); |
968 | memcpy(new_acl, old_acl, ACL_SIZE(old_acl)); |
969 | break; |
970 | } |
971 | } |
972 | |
973 | if (dst == num) |
974 | { |
975 | /* need to append a new item */ |
976 | new_acl = allocacl(num + 1); |
977 | new_aip = ACL_DAT(new_acl); |
978 | memcpy(new_aip, old_aip, num * sizeof(AclItem)); |
979 | |
980 | /* initialize the new entry with no permissions */ |
981 | new_aip[dst].ai_grantee = mod_aip->ai_grantee; |
982 | new_aip[dst].ai_grantor = mod_aip->ai_grantor; |
983 | ACLITEM_SET_PRIVS_GOPTIONS(new_aip[dst], |
984 | ACL_NO_RIGHTS, ACL_NO_RIGHTS); |
985 | num++; /* set num to the size of new_acl */ |
986 | } |
987 | |
988 | old_rights = ACLITEM_GET_RIGHTS(new_aip[dst]); |
989 | old_goptions = ACLITEM_GET_GOPTIONS(new_aip[dst]); |
990 | |
991 | /* apply the specified permissions change */ |
992 | switch (modechg) |
993 | { |
994 | case ACL_MODECHG_ADD: |
995 | ACLITEM_SET_RIGHTS(new_aip[dst], |
996 | old_rights | ACLITEM_GET_RIGHTS(*mod_aip)); |
997 | break; |
998 | case ACL_MODECHG_DEL: |
999 | ACLITEM_SET_RIGHTS(new_aip[dst], |
1000 | old_rights & ~ACLITEM_GET_RIGHTS(*mod_aip)); |
1001 | break; |
1002 | case ACL_MODECHG_EQL: |
1003 | ACLITEM_SET_RIGHTS(new_aip[dst], |
1004 | ACLITEM_GET_RIGHTS(*mod_aip)); |
1005 | break; |
1006 | } |
1007 | |
1008 | new_rights = ACLITEM_GET_RIGHTS(new_aip[dst]); |
1009 | new_goptions = ACLITEM_GET_GOPTIONS(new_aip[dst]); |
1010 | |
1011 | /* |
1012 | * If the adjusted entry has no permissions, delete it from the list. |
1013 | */ |
1014 | if (new_rights == ACL_NO_RIGHTS) |
1015 | { |
1016 | memmove(new_aip + dst, |
1017 | new_aip + dst + 1, |
1018 | (num - dst - 1) * sizeof(AclItem)); |
1019 | /* Adjust array size to be 'num - 1' items */ |
1020 | ARR_DIMS(new_acl)[0] = num - 1; |
1021 | SET_VARSIZE(new_acl, ACL_N_SIZE(num - 1)); |
1022 | } |
1023 | |
1024 | /* |
1025 | * Remove abandoned privileges (cascading revoke). Currently we can only |
1026 | * handle this when the grantee is not PUBLIC. |
1027 | */ |
1028 | if ((old_goptions & ~new_goptions) != 0) |
1029 | { |
1030 | Assert(mod_aip->ai_grantee != ACL_ID_PUBLIC); |
1031 | new_acl = recursive_revoke(new_acl, mod_aip->ai_grantee, |
1032 | (old_goptions & ~new_goptions), |
1033 | ownerId, behavior); |
1034 | } |
1035 | |
1036 | return new_acl; |
1037 | } |
1038 | |
1039 | /* |
1040 | * Update an ACL array to reflect a change of owner to the parent object |
1041 | * |
1042 | * old_acl: the input ACL array (must not be NULL) |
1043 | * oldOwnerId: Oid of the old object owner |
1044 | * newOwnerId: Oid of the new object owner |
1045 | * |
1046 | * The result is a modified copy; the input object is not changed. |
1047 | * |
1048 | * NB: caller is responsible for having detoasted the input ACL, if needed. |
1049 | */ |
1050 | Acl * |
1051 | aclnewowner(const Acl *old_acl, Oid oldOwnerId, Oid newOwnerId) |
1052 | { |
1053 | Acl *new_acl; |
1054 | AclItem *new_aip; |
1055 | AclItem *old_aip; |
1056 | AclItem *dst_aip; |
1057 | AclItem *src_aip; |
1058 | AclItem *targ_aip; |
1059 | bool newpresent = false; |
1060 | int dst, |
1061 | src, |
1062 | targ, |
1063 | num; |
1064 | |
1065 | check_acl(old_acl); |
1066 | |
1067 | /* |
1068 | * Make a copy of the given ACL, substituting new owner ID for old |
1069 | * wherever it appears as either grantor or grantee. Also note if the new |
1070 | * owner ID is already present. |
1071 | */ |
1072 | num = ACL_NUM(old_acl); |
1073 | old_aip = ACL_DAT(old_acl); |
1074 | new_acl = allocacl(num); |
1075 | new_aip = ACL_DAT(new_acl); |
1076 | memcpy(new_aip, old_aip, num * sizeof(AclItem)); |
1077 | for (dst = 0, dst_aip = new_aip; dst < num; dst++, dst_aip++) |
1078 | { |
1079 | if (dst_aip->ai_grantor == oldOwnerId) |
1080 | dst_aip->ai_grantor = newOwnerId; |
1081 | else if (dst_aip->ai_grantor == newOwnerId) |
1082 | newpresent = true; |
1083 | if (dst_aip->ai_grantee == oldOwnerId) |
1084 | dst_aip->ai_grantee = newOwnerId; |
1085 | else if (dst_aip->ai_grantee == newOwnerId) |
1086 | newpresent = true; |
1087 | } |
1088 | |
1089 | /* |
1090 | * If the old ACL contained any references to the new owner, then we may |
1091 | * now have generated an ACL containing duplicate entries. Find them and |
1092 | * merge them so that there are not duplicates. (This is relatively |
1093 | * expensive since we use a stupid O(N^2) algorithm, but it's unlikely to |
1094 | * be the normal case.) |
1095 | * |
1096 | * To simplify deletion of duplicate entries, we temporarily leave them in |
1097 | * the array but set their privilege masks to zero; when we reach such an |
1098 | * entry it's just skipped. (Thus, a side effect of this code will be to |
1099 | * remove privilege-free entries, should there be any in the input.) dst |
1100 | * is the next output slot, targ is the currently considered input slot |
1101 | * (always >= dst), and src scans entries to the right of targ looking for |
1102 | * duplicates. Once an entry has been emitted to dst it is known |
1103 | * duplicate-free and need not be considered anymore. |
1104 | */ |
1105 | if (newpresent) |
1106 | { |
1107 | dst = 0; |
1108 | for (targ = 0, targ_aip = new_aip; targ < num; targ++, targ_aip++) |
1109 | { |
1110 | /* ignore if deleted in an earlier pass */ |
1111 | if (ACLITEM_GET_RIGHTS(*targ_aip) == ACL_NO_RIGHTS) |
1112 | continue; |
1113 | /* find and merge any duplicates */ |
1114 | for (src = targ + 1, src_aip = targ_aip + 1; src < num; |
1115 | src++, src_aip++) |
1116 | { |
1117 | if (ACLITEM_GET_RIGHTS(*src_aip) == ACL_NO_RIGHTS) |
1118 | continue; |
1119 | if (aclitem_match(targ_aip, src_aip)) |
1120 | { |
1121 | ACLITEM_SET_RIGHTS(*targ_aip, |
1122 | ACLITEM_GET_RIGHTS(*targ_aip) | |
1123 | ACLITEM_GET_RIGHTS(*src_aip)); |
1124 | /* mark the duplicate deleted */ |
1125 | ACLITEM_SET_RIGHTS(*src_aip, ACL_NO_RIGHTS); |
1126 | } |
1127 | } |
1128 | /* and emit to output */ |
1129 | new_aip[dst] = *targ_aip; |
1130 | dst++; |
1131 | } |
1132 | /* Adjust array size to be 'dst' items */ |
1133 | ARR_DIMS(new_acl)[0] = dst; |
1134 | SET_VARSIZE(new_acl, ACL_N_SIZE(dst)); |
1135 | } |
1136 | |
1137 | return new_acl; |
1138 | } |
1139 | |
1140 | |
1141 | /* |
1142 | * When granting grant options, we must disallow attempts to set up circular |
1143 | * chains of grant options. Suppose A (the object owner) grants B some |
1144 | * privileges with grant option, and B re-grants them to C. If C could |
1145 | * grant the privileges to B as well, then A would be unable to effectively |
1146 | * revoke the privileges from B, since recursive_revoke would consider that |
1147 | * B still has 'em from C. |
1148 | * |
1149 | * We check for this by recursively deleting all grant options belonging to |
1150 | * the target grantee, and then seeing if the would-be grantor still has the |
1151 | * grant option or not. |
1152 | */ |
1153 | static void |
1154 | check_circularity(const Acl *old_acl, const AclItem *mod_aip, |
1155 | Oid ownerId) |
1156 | { |
1157 | Acl *acl; |
1158 | AclItem *aip; |
1159 | int i, |
1160 | num; |
1161 | AclMode own_privs; |
1162 | |
1163 | check_acl(old_acl); |
1164 | |
1165 | /* |
1166 | * For now, grant options can only be granted to roles, not PUBLIC. |
1167 | * Otherwise we'd have to work a bit harder here. |
1168 | */ |
1169 | Assert(mod_aip->ai_grantee != ACL_ID_PUBLIC); |
1170 | |
1171 | /* The owner always has grant options, no need to check */ |
1172 | if (mod_aip->ai_grantor == ownerId) |
1173 | return; |
1174 | |
1175 | /* Make a working copy */ |
1176 | acl = allocacl(ACL_NUM(old_acl)); |
1177 | memcpy(acl, old_acl, ACL_SIZE(old_acl)); |
1178 | |
1179 | /* Zap all grant options of target grantee, plus what depends on 'em */ |
1180 | cc_restart: |
1181 | num = ACL_NUM(acl); |
1182 | aip = ACL_DAT(acl); |
1183 | for (i = 0; i < num; i++) |
1184 | { |
1185 | if (aip[i].ai_grantee == mod_aip->ai_grantee && |
1186 | ACLITEM_GET_GOPTIONS(aip[i]) != ACL_NO_RIGHTS) |
1187 | { |
1188 | Acl *new_acl; |
1189 | |
1190 | /* We'll actually zap ordinary privs too, but no matter */ |
1191 | new_acl = aclupdate(acl, &aip[i], ACL_MODECHG_DEL, |
1192 | ownerId, DROP_CASCADE); |
1193 | |
1194 | pfree(acl); |
1195 | acl = new_acl; |
1196 | |
1197 | goto cc_restart; |
1198 | } |
1199 | } |
1200 | |
1201 | /* Now we can compute grantor's independently-derived privileges */ |
1202 | own_privs = aclmask(acl, |
1203 | mod_aip->ai_grantor, |
1204 | ownerId, |
1205 | ACL_GRANT_OPTION_FOR(ACLITEM_GET_GOPTIONS(*mod_aip)), |
1206 | ACLMASK_ALL); |
1207 | own_privs = ACL_OPTION_TO_PRIVS(own_privs); |
1208 | |
1209 | if ((ACLITEM_GET_GOPTIONS(*mod_aip) & ~own_privs) != 0) |
1210 | ereport(ERROR, |
1211 | (errcode(ERRCODE_INVALID_GRANT_OPERATION), |
1212 | errmsg("grant options cannot be granted back to your own grantor" ))); |
1213 | |
1214 | pfree(acl); |
1215 | } |
1216 | |
1217 | |
1218 | /* |
1219 | * Ensure that no privilege is "abandoned". A privilege is abandoned |
1220 | * if the user that granted the privilege loses the grant option. (So |
1221 | * the chain through which it was granted is broken.) Either the |
1222 | * abandoned privileges are revoked as well, or an error message is |
1223 | * printed, depending on the drop behavior option. |
1224 | * |
1225 | * acl: the input ACL list |
1226 | * grantee: the user from whom some grant options have been revoked |
1227 | * revoke_privs: the grant options being revoked |
1228 | * ownerId: Oid of object owner |
1229 | * behavior: RESTRICT or CASCADE behavior for recursive removal |
1230 | * |
1231 | * The input Acl object is pfree'd if replaced. |
1232 | */ |
1233 | static Acl * |
1234 | recursive_revoke(Acl *acl, |
1235 | Oid grantee, |
1236 | AclMode revoke_privs, |
1237 | Oid ownerId, |
1238 | DropBehavior behavior) |
1239 | { |
1240 | AclMode still_has; |
1241 | AclItem *aip; |
1242 | int i, |
1243 | num; |
1244 | |
1245 | check_acl(acl); |
1246 | |
1247 | /* The owner can never truly lose grant options, so short-circuit */ |
1248 | if (grantee == ownerId) |
1249 | return acl; |
1250 | |
1251 | /* The grantee might still have some grant options via another grantor */ |
1252 | still_has = aclmask(acl, grantee, ownerId, |
1253 | ACL_GRANT_OPTION_FOR(revoke_privs), |
1254 | ACLMASK_ALL); |
1255 | revoke_privs &= ~ACL_OPTION_TO_PRIVS(still_has); |
1256 | if (revoke_privs == ACL_NO_RIGHTS) |
1257 | return acl; |
1258 | |
1259 | restart: |
1260 | num = ACL_NUM(acl); |
1261 | aip = ACL_DAT(acl); |
1262 | for (i = 0; i < num; i++) |
1263 | { |
1264 | if (aip[i].ai_grantor == grantee |
1265 | && (ACLITEM_GET_PRIVS(aip[i]) & revoke_privs) != 0) |
1266 | { |
1267 | AclItem mod_acl; |
1268 | Acl *new_acl; |
1269 | |
1270 | if (behavior == DROP_RESTRICT) |
1271 | ereport(ERROR, |
1272 | (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST), |
1273 | errmsg("dependent privileges exist" ), |
1274 | errhint("Use CASCADE to revoke them too." ))); |
1275 | |
1276 | mod_acl.ai_grantor = grantee; |
1277 | mod_acl.ai_grantee = aip[i].ai_grantee; |
1278 | ACLITEM_SET_PRIVS_GOPTIONS(mod_acl, |
1279 | revoke_privs, |
1280 | revoke_privs); |
1281 | |
1282 | new_acl = aclupdate(acl, &mod_acl, ACL_MODECHG_DEL, |
1283 | ownerId, behavior); |
1284 | |
1285 | pfree(acl); |
1286 | acl = new_acl; |
1287 | |
1288 | goto restart; |
1289 | } |
1290 | } |
1291 | |
1292 | return acl; |
1293 | } |
1294 | |
1295 | |
1296 | /* |
1297 | * aclmask --- compute bitmask of all privileges held by roleid. |
1298 | * |
1299 | * When 'how' = ACLMASK_ALL, this simply returns the privilege bits |
1300 | * held by the given roleid according to the given ACL list, ANDed |
1301 | * with 'mask'. (The point of passing 'mask' is to let the routine |
1302 | * exit early if all privileges of interest have been found.) |
1303 | * |
1304 | * When 'how' = ACLMASK_ANY, returns as soon as any bit in the mask |
1305 | * is known true. (This lets us exit soonest in cases where the |
1306 | * caller is only going to test for zero or nonzero result.) |
1307 | * |
1308 | * Usage patterns: |
1309 | * |
1310 | * To see if any of a set of privileges are held: |
1311 | * if (aclmask(acl, roleid, ownerId, privs, ACLMASK_ANY) != 0) |
1312 | * |
1313 | * To see if all of a set of privileges are held: |
1314 | * if (aclmask(acl, roleid, ownerId, privs, ACLMASK_ALL) == privs) |
1315 | * |
1316 | * To determine exactly which of a set of privileges are held: |
1317 | * heldprivs = aclmask(acl, roleid, ownerId, privs, ACLMASK_ALL); |
1318 | */ |
1319 | AclMode |
1320 | aclmask(const Acl *acl, Oid roleid, Oid ownerId, |
1321 | AclMode mask, AclMaskHow how) |
1322 | { |
1323 | AclMode result; |
1324 | AclMode remaining; |
1325 | AclItem *aidat; |
1326 | int i, |
1327 | num; |
1328 | |
1329 | /* |
1330 | * Null ACL should not happen, since caller should have inserted |
1331 | * appropriate default |
1332 | */ |
1333 | if (acl == NULL) |
1334 | elog(ERROR, "null ACL" ); |
1335 | |
1336 | check_acl(acl); |
1337 | |
1338 | /* Quick exit for mask == 0 */ |
1339 | if (mask == 0) |
1340 | return 0; |
1341 | |
1342 | result = 0; |
1343 | |
1344 | /* Owner always implicitly has all grant options */ |
1345 | if ((mask & ACLITEM_ALL_GOPTION_BITS) && |
1346 | has_privs_of_role(roleid, ownerId)) |
1347 | { |
1348 | result = mask & ACLITEM_ALL_GOPTION_BITS; |
1349 | if ((how == ACLMASK_ALL) ? (result == mask) : (result != 0)) |
1350 | return result; |
1351 | } |
1352 | |
1353 | num = ACL_NUM(acl); |
1354 | aidat = ACL_DAT(acl); |
1355 | |
1356 | /* |
1357 | * Check privileges granted directly to roleid or to public |
1358 | */ |
1359 | for (i = 0; i < num; i++) |
1360 | { |
1361 | AclItem *aidata = &aidat[i]; |
1362 | |
1363 | if (aidata->ai_grantee == ACL_ID_PUBLIC || |
1364 | aidata->ai_grantee == roleid) |
1365 | { |
1366 | result |= aidata->ai_privs & mask; |
1367 | if ((how == ACLMASK_ALL) ? (result == mask) : (result != 0)) |
1368 | return result; |
1369 | } |
1370 | } |
1371 | |
1372 | /* |
1373 | * Check privileges granted indirectly via role memberships. We do this in |
1374 | * a separate pass to minimize expensive indirect membership tests. In |
1375 | * particular, it's worth testing whether a given ACL entry grants any |
1376 | * privileges still of interest before we perform the has_privs_of_role |
1377 | * test. |
1378 | */ |
1379 | remaining = mask & ~result; |
1380 | for (i = 0; i < num; i++) |
1381 | { |
1382 | AclItem *aidata = &aidat[i]; |
1383 | |
1384 | if (aidata->ai_grantee == ACL_ID_PUBLIC || |
1385 | aidata->ai_grantee == roleid) |
1386 | continue; /* already checked it */ |
1387 | |
1388 | if ((aidata->ai_privs & remaining) && |
1389 | has_privs_of_role(roleid, aidata->ai_grantee)) |
1390 | { |
1391 | result |= aidata->ai_privs & mask; |
1392 | if ((how == ACLMASK_ALL) ? (result == mask) : (result != 0)) |
1393 | return result; |
1394 | remaining = mask & ~result; |
1395 | } |
1396 | } |
1397 | |
1398 | return result; |
1399 | } |
1400 | |
1401 | |
1402 | /* |
1403 | * aclmask_direct --- compute bitmask of all privileges held by roleid. |
1404 | * |
1405 | * This is exactly like aclmask() except that we consider only privileges |
1406 | * held *directly* by roleid, not those inherited via role membership. |
1407 | */ |
1408 | static AclMode |
1409 | aclmask_direct(const Acl *acl, Oid roleid, Oid ownerId, |
1410 | AclMode mask, AclMaskHow how) |
1411 | { |
1412 | AclMode result; |
1413 | AclItem *aidat; |
1414 | int i, |
1415 | num; |
1416 | |
1417 | /* |
1418 | * Null ACL should not happen, since caller should have inserted |
1419 | * appropriate default |
1420 | */ |
1421 | if (acl == NULL) |
1422 | elog(ERROR, "null ACL" ); |
1423 | |
1424 | check_acl(acl); |
1425 | |
1426 | /* Quick exit for mask == 0 */ |
1427 | if (mask == 0) |
1428 | return 0; |
1429 | |
1430 | result = 0; |
1431 | |
1432 | /* Owner always implicitly has all grant options */ |
1433 | if ((mask & ACLITEM_ALL_GOPTION_BITS) && |
1434 | roleid == ownerId) |
1435 | { |
1436 | result = mask & ACLITEM_ALL_GOPTION_BITS; |
1437 | if ((how == ACLMASK_ALL) ? (result == mask) : (result != 0)) |
1438 | return result; |
1439 | } |
1440 | |
1441 | num = ACL_NUM(acl); |
1442 | aidat = ACL_DAT(acl); |
1443 | |
1444 | /* |
1445 | * Check privileges granted directly to roleid (and not to public) |
1446 | */ |
1447 | for (i = 0; i < num; i++) |
1448 | { |
1449 | AclItem *aidata = &aidat[i]; |
1450 | |
1451 | if (aidata->ai_grantee == roleid) |
1452 | { |
1453 | result |= aidata->ai_privs & mask; |
1454 | if ((how == ACLMASK_ALL) ? (result == mask) : (result != 0)) |
1455 | return result; |
1456 | } |
1457 | } |
1458 | |
1459 | return result; |
1460 | } |
1461 | |
1462 | |
1463 | /* |
1464 | * aclmembers |
1465 | * Find out all the roleids mentioned in an Acl. |
1466 | * Note that we do not distinguish grantors from grantees. |
1467 | * |
1468 | * *roleids is set to point to a palloc'd array containing distinct OIDs |
1469 | * in sorted order. The length of the array is the function result. |
1470 | */ |
1471 | int |
1472 | aclmembers(const Acl *acl, Oid **roleids) |
1473 | { |
1474 | Oid *list; |
1475 | const AclItem *acldat; |
1476 | int i, |
1477 | j, |
1478 | k; |
1479 | |
1480 | if (acl == NULL || ACL_NUM(acl) == 0) |
1481 | { |
1482 | *roleids = NULL; |
1483 | return 0; |
1484 | } |
1485 | |
1486 | check_acl(acl); |
1487 | |
1488 | /* Allocate the worst-case space requirement */ |
1489 | list = palloc(ACL_NUM(acl) * 2 * sizeof(Oid)); |
1490 | acldat = ACL_DAT(acl); |
1491 | |
1492 | /* |
1493 | * Walk the ACL collecting mentioned RoleIds. |
1494 | */ |
1495 | j = 0; |
1496 | for (i = 0; i < ACL_NUM(acl); i++) |
1497 | { |
1498 | const AclItem *ai = &acldat[i]; |
1499 | |
1500 | if (ai->ai_grantee != ACL_ID_PUBLIC) |
1501 | list[j++] = ai->ai_grantee; |
1502 | /* grantor is currently never PUBLIC, but let's check anyway */ |
1503 | if (ai->ai_grantor != ACL_ID_PUBLIC) |
1504 | list[j++] = ai->ai_grantor; |
1505 | } |
1506 | |
1507 | /* Sort the array */ |
1508 | qsort(list, j, sizeof(Oid), oid_cmp); |
1509 | |
1510 | /* Remove duplicates from the array */ |
1511 | k = 0; |
1512 | for (i = 1; i < j; i++) |
1513 | { |
1514 | if (list[k] != list[i]) |
1515 | list[++k] = list[i]; |
1516 | } |
1517 | |
1518 | /* |
1519 | * We could repalloc the array down to minimum size, but it's hardly worth |
1520 | * it since it's only transient memory. |
1521 | */ |
1522 | *roleids = list; |
1523 | |
1524 | return k + 1; |
1525 | } |
1526 | |
1527 | |
1528 | /* |
1529 | * aclinsert (exported function) |
1530 | */ |
1531 | Datum |
1532 | aclinsert(PG_FUNCTION_ARGS) |
1533 | { |
1534 | ereport(ERROR, |
1535 | (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), |
1536 | errmsg("aclinsert is no longer supported" ))); |
1537 | |
1538 | PG_RETURN_NULL(); /* keep compiler quiet */ |
1539 | } |
1540 | |
1541 | Datum |
1542 | aclremove(PG_FUNCTION_ARGS) |
1543 | { |
1544 | ereport(ERROR, |
1545 | (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), |
1546 | errmsg("aclremove is no longer supported" ))); |
1547 | |
1548 | PG_RETURN_NULL(); /* keep compiler quiet */ |
1549 | } |
1550 | |
1551 | Datum |
1552 | aclcontains(PG_FUNCTION_ARGS) |
1553 | { |
1554 | Acl *acl = PG_GETARG_ACL_P(0); |
1555 | AclItem *aip = PG_GETARG_ACLITEM_P(1); |
1556 | AclItem *aidat; |
1557 | int i, |
1558 | num; |
1559 | |
1560 | check_acl(acl); |
1561 | num = ACL_NUM(acl); |
1562 | aidat = ACL_DAT(acl); |
1563 | for (i = 0; i < num; ++i) |
1564 | { |
1565 | if (aip->ai_grantee == aidat[i].ai_grantee && |
1566 | aip->ai_grantor == aidat[i].ai_grantor && |
1567 | (ACLITEM_GET_RIGHTS(*aip) & ACLITEM_GET_RIGHTS(aidat[i])) == ACLITEM_GET_RIGHTS(*aip)) |
1568 | PG_RETURN_BOOL(true); |
1569 | } |
1570 | PG_RETURN_BOOL(false); |
1571 | } |
1572 | |
1573 | Datum |
1574 | makeaclitem(PG_FUNCTION_ARGS) |
1575 | { |
1576 | Oid grantee = PG_GETARG_OID(0); |
1577 | Oid grantor = PG_GETARG_OID(1); |
1578 | text *privtext = PG_GETARG_TEXT_PP(2); |
1579 | bool goption = PG_GETARG_BOOL(3); |
1580 | AclItem *result; |
1581 | AclMode priv; |
1582 | |
1583 | priv = convert_priv_string(privtext); |
1584 | |
1585 | result = (AclItem *) palloc(sizeof(AclItem)); |
1586 | |
1587 | result->ai_grantee = grantee; |
1588 | result->ai_grantor = grantor; |
1589 | |
1590 | ACLITEM_SET_PRIVS_GOPTIONS(*result, priv, |
1591 | (goption ? priv : ACL_NO_RIGHTS)); |
1592 | |
1593 | PG_RETURN_ACLITEM_P(result); |
1594 | } |
1595 | |
1596 | static AclMode |
1597 | convert_priv_string(text *priv_type_text) |
1598 | { |
1599 | char *priv_type = text_to_cstring(priv_type_text); |
1600 | |
1601 | if (pg_strcasecmp(priv_type, "SELECT" ) == 0) |
1602 | return ACL_SELECT; |
1603 | if (pg_strcasecmp(priv_type, "INSERT" ) == 0) |
1604 | return ACL_INSERT; |
1605 | if (pg_strcasecmp(priv_type, "UPDATE" ) == 0) |
1606 | return ACL_UPDATE; |
1607 | if (pg_strcasecmp(priv_type, "DELETE" ) == 0) |
1608 | return ACL_DELETE; |
1609 | if (pg_strcasecmp(priv_type, "TRUNCATE" ) == 0) |
1610 | return ACL_TRUNCATE; |
1611 | if (pg_strcasecmp(priv_type, "REFERENCES" ) == 0) |
1612 | return ACL_REFERENCES; |
1613 | if (pg_strcasecmp(priv_type, "TRIGGER" ) == 0) |
1614 | return ACL_TRIGGER; |
1615 | if (pg_strcasecmp(priv_type, "EXECUTE" ) == 0) |
1616 | return ACL_EXECUTE; |
1617 | if (pg_strcasecmp(priv_type, "USAGE" ) == 0) |
1618 | return ACL_USAGE; |
1619 | if (pg_strcasecmp(priv_type, "CREATE" ) == 0) |
1620 | return ACL_CREATE; |
1621 | if (pg_strcasecmp(priv_type, "TEMP" ) == 0) |
1622 | return ACL_CREATE_TEMP; |
1623 | if (pg_strcasecmp(priv_type, "TEMPORARY" ) == 0) |
1624 | return ACL_CREATE_TEMP; |
1625 | if (pg_strcasecmp(priv_type, "CONNECT" ) == 0) |
1626 | return ACL_CONNECT; |
1627 | if (pg_strcasecmp(priv_type, "RULE" ) == 0) |
1628 | return 0; /* ignore old RULE privileges */ |
1629 | |
1630 | ereport(ERROR, |
1631 | (errcode(ERRCODE_INVALID_PARAMETER_VALUE), |
1632 | errmsg("unrecognized privilege type: \"%s\"" , priv_type))); |
1633 | return ACL_NO_RIGHTS; /* keep compiler quiet */ |
1634 | } |
1635 | |
1636 | |
1637 | /* |
1638 | * convert_any_priv_string: recognize privilege strings for has_foo_privilege |
1639 | * |
1640 | * We accept a comma-separated list of case-insensitive privilege names, |
1641 | * producing a bitmask of the OR'd privilege bits. We are liberal about |
1642 | * whitespace between items, not so much about whitespace within items. |
1643 | * The allowed privilege names are given as an array of priv_map structs, |
1644 | * terminated by one with a NULL name pointer. |
1645 | */ |
1646 | static AclMode |
1647 | convert_any_priv_string(text *priv_type_text, |
1648 | const priv_map *privileges) |
1649 | { |
1650 | AclMode result = 0; |
1651 | char *priv_type = text_to_cstring(priv_type_text); |
1652 | char *chunk; |
1653 | char *next_chunk; |
1654 | |
1655 | /* We rely on priv_type being a private, modifiable string */ |
1656 | for (chunk = priv_type; chunk; chunk = next_chunk) |
1657 | { |
1658 | int chunk_len; |
1659 | const priv_map *this_priv; |
1660 | |
1661 | /* Split string at commas */ |
1662 | next_chunk = strchr(chunk, ','); |
1663 | if (next_chunk) |
1664 | *next_chunk++ = '\0'; |
1665 | |
1666 | /* Drop leading/trailing whitespace in this chunk */ |
1667 | while (*chunk && isspace((unsigned char) *chunk)) |
1668 | chunk++; |
1669 | chunk_len = strlen(chunk); |
1670 | while (chunk_len > 0 && isspace((unsigned char) chunk[chunk_len - 1])) |
1671 | chunk_len--; |
1672 | chunk[chunk_len] = '\0'; |
1673 | |
1674 | /* Match to the privileges list */ |
1675 | for (this_priv = privileges; this_priv->name; this_priv++) |
1676 | { |
1677 | if (pg_strcasecmp(this_priv->name, chunk) == 0) |
1678 | { |
1679 | result |= this_priv->value; |
1680 | break; |
1681 | } |
1682 | } |
1683 | if (!this_priv->name) |
1684 | ereport(ERROR, |
1685 | (errcode(ERRCODE_INVALID_PARAMETER_VALUE), |
1686 | errmsg("unrecognized privilege type: \"%s\"" , chunk))); |
1687 | } |
1688 | |
1689 | pfree(priv_type); |
1690 | return result; |
1691 | } |
1692 | |
1693 | |
1694 | static const char * |
1695 | convert_aclright_to_string(int aclright) |
1696 | { |
1697 | switch (aclright) |
1698 | { |
1699 | case ACL_INSERT: |
1700 | return "INSERT" ; |
1701 | case ACL_SELECT: |
1702 | return "SELECT" ; |
1703 | case ACL_UPDATE: |
1704 | return "UPDATE" ; |
1705 | case ACL_DELETE: |
1706 | return "DELETE" ; |
1707 | case ACL_TRUNCATE: |
1708 | return "TRUNCATE" ; |
1709 | case ACL_REFERENCES: |
1710 | return "REFERENCES" ; |
1711 | case ACL_TRIGGER: |
1712 | return "TRIGGER" ; |
1713 | case ACL_EXECUTE: |
1714 | return "EXECUTE" ; |
1715 | case ACL_USAGE: |
1716 | return "USAGE" ; |
1717 | case ACL_CREATE: |
1718 | return "CREATE" ; |
1719 | case ACL_CREATE_TEMP: |
1720 | return "TEMPORARY" ; |
1721 | case ACL_CONNECT: |
1722 | return "CONNECT" ; |
1723 | default: |
1724 | elog(ERROR, "unrecognized aclright: %d" , aclright); |
1725 | return NULL; |
1726 | } |
1727 | } |
1728 | |
1729 | |
1730 | /*---------- |
1731 | * Convert an aclitem[] to a table. |
1732 | * |
1733 | * Example: |
1734 | * |
1735 | * aclexplode('{=r/joe,foo=a*w/joe}'::aclitem[]) |
1736 | * |
1737 | * returns the table |
1738 | * |
1739 | * {{ OID(joe), 0::OID, 'SELECT', false }, |
1740 | * { OID(joe), OID(foo), 'INSERT', true }, |
1741 | * { OID(joe), OID(foo), 'UPDATE', false }} |
1742 | *---------- |
1743 | */ |
1744 | Datum |
1745 | aclexplode(PG_FUNCTION_ARGS) |
1746 | { |
1747 | Acl *acl = PG_GETARG_ACL_P(0); |
1748 | FuncCallContext *funcctx; |
1749 | int *idx; |
1750 | AclItem *aidat; |
1751 | |
1752 | if (SRF_IS_FIRSTCALL()) |
1753 | { |
1754 | TupleDesc tupdesc; |
1755 | MemoryContext oldcontext; |
1756 | |
1757 | check_acl(acl); |
1758 | |
1759 | funcctx = SRF_FIRSTCALL_INIT(); |
1760 | oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); |
1761 | |
1762 | /* |
1763 | * build tupdesc for result tuples (matches out parameters in pg_proc |
1764 | * entry) |
1765 | */ |
1766 | tupdesc = CreateTemplateTupleDesc(4); |
1767 | TupleDescInitEntry(tupdesc, (AttrNumber) 1, "grantor" , |
1768 | OIDOID, -1, 0); |
1769 | TupleDescInitEntry(tupdesc, (AttrNumber) 2, "grantee" , |
1770 | OIDOID, -1, 0); |
1771 | TupleDescInitEntry(tupdesc, (AttrNumber) 3, "privilege_type" , |
1772 | TEXTOID, -1, 0); |
1773 | TupleDescInitEntry(tupdesc, (AttrNumber) 4, "is_grantable" , |
1774 | BOOLOID, -1, 0); |
1775 | |
1776 | funcctx->tuple_desc = BlessTupleDesc(tupdesc); |
1777 | |
1778 | /* allocate memory for user context */ |
1779 | idx = (int *) palloc(sizeof(int[2])); |
1780 | idx[0] = 0; /* ACL array item index */ |
1781 | idx[1] = -1; /* privilege type counter */ |
1782 | funcctx->user_fctx = (void *) idx; |
1783 | |
1784 | MemoryContextSwitchTo(oldcontext); |
1785 | } |
1786 | |
1787 | funcctx = SRF_PERCALL_SETUP(); |
1788 | idx = (int *) funcctx->user_fctx; |
1789 | aidat = ACL_DAT(acl); |
1790 | |
1791 | /* need test here in case acl has no items */ |
1792 | while (idx[0] < ACL_NUM(acl)) |
1793 | { |
1794 | AclItem *aidata; |
1795 | AclMode priv_bit; |
1796 | |
1797 | idx[1]++; |
1798 | if (idx[1] == N_ACL_RIGHTS) |
1799 | { |
1800 | idx[1] = 0; |
1801 | idx[0]++; |
1802 | if (idx[0] >= ACL_NUM(acl)) /* done */ |
1803 | break; |
1804 | } |
1805 | aidata = &aidat[idx[0]]; |
1806 | priv_bit = 1 << idx[1]; |
1807 | |
1808 | if (ACLITEM_GET_PRIVS(*aidata) & priv_bit) |
1809 | { |
1810 | Datum result; |
1811 | Datum values[4]; |
1812 | bool nulls[4]; |
1813 | HeapTuple tuple; |
1814 | |
1815 | values[0] = ObjectIdGetDatum(aidata->ai_grantor); |
1816 | values[1] = ObjectIdGetDatum(aidata->ai_grantee); |
1817 | values[2] = CStringGetTextDatum(convert_aclright_to_string(priv_bit)); |
1818 | values[3] = BoolGetDatum((ACLITEM_GET_GOPTIONS(*aidata) & priv_bit) != 0); |
1819 | |
1820 | MemSet(nulls, 0, sizeof(nulls)); |
1821 | |
1822 | tuple = heap_form_tuple(funcctx->tuple_desc, values, nulls); |
1823 | result = HeapTupleGetDatum(tuple); |
1824 | |
1825 | SRF_RETURN_NEXT(funcctx, result); |
1826 | } |
1827 | } |
1828 | |
1829 | SRF_RETURN_DONE(funcctx); |
1830 | } |
1831 | |
1832 | |
1833 | /* |
1834 | * has_table_privilege variants |
1835 | * These are all named "has_table_privilege" at the SQL level. |
1836 | * They take various combinations of relation name, relation OID, |
1837 | * user name, user OID, or implicit user = current_user. |
1838 | * |
1839 | * The result is a boolean value: true if user has the indicated |
1840 | * privilege, false if not. The variants that take a relation OID |
1841 | * return NULL if the OID doesn't exist (rather than failing, as |
1842 | * they did before Postgres 8.4). |
1843 | */ |
1844 | |
1845 | /* |
1846 | * has_table_privilege_name_name |
1847 | * Check user privileges on a table given |
1848 | * name username, text tablename, and text priv name. |
1849 | */ |
1850 | Datum |
1851 | has_table_privilege_name_name(PG_FUNCTION_ARGS) |
1852 | { |
1853 | Name rolename = PG_GETARG_NAME(0); |
1854 | text *tablename = PG_GETARG_TEXT_PP(1); |
1855 | text *priv_type_text = PG_GETARG_TEXT_PP(2); |
1856 | Oid roleid; |
1857 | Oid tableoid; |
1858 | AclMode mode; |
1859 | AclResult aclresult; |
1860 | |
1861 | roleid = get_role_oid_or_public(NameStr(*rolename)); |
1862 | tableoid = convert_table_name(tablename); |
1863 | mode = convert_table_priv_string(priv_type_text); |
1864 | |
1865 | aclresult = pg_class_aclcheck(tableoid, roleid, mode); |
1866 | |
1867 | PG_RETURN_BOOL(aclresult == ACLCHECK_OK); |
1868 | } |
1869 | |
1870 | /* |
1871 | * has_table_privilege_name |
1872 | * Check user privileges on a table given |
1873 | * text tablename and text priv name. |
1874 | * current_user is assumed |
1875 | */ |
1876 | Datum |
1877 | has_table_privilege_name(PG_FUNCTION_ARGS) |
1878 | { |
1879 | text *tablename = PG_GETARG_TEXT_PP(0); |
1880 | text *priv_type_text = PG_GETARG_TEXT_PP(1); |
1881 | Oid roleid; |
1882 | Oid tableoid; |
1883 | AclMode mode; |
1884 | AclResult aclresult; |
1885 | |
1886 | roleid = GetUserId(); |
1887 | tableoid = convert_table_name(tablename); |
1888 | mode = convert_table_priv_string(priv_type_text); |
1889 | |
1890 | aclresult = pg_class_aclcheck(tableoid, roleid, mode); |
1891 | |
1892 | PG_RETURN_BOOL(aclresult == ACLCHECK_OK); |
1893 | } |
1894 | |
1895 | /* |
1896 | * has_table_privilege_name_id |
1897 | * Check user privileges on a table given |
1898 | * name usename, table oid, and text priv name. |
1899 | */ |
1900 | Datum |
1901 | has_table_privilege_name_id(PG_FUNCTION_ARGS) |
1902 | { |
1903 | Name username = PG_GETARG_NAME(0); |
1904 | Oid tableoid = PG_GETARG_OID(1); |
1905 | text *priv_type_text = PG_GETARG_TEXT_PP(2); |
1906 | Oid roleid; |
1907 | AclMode mode; |
1908 | AclResult aclresult; |
1909 | |
1910 | roleid = get_role_oid_or_public(NameStr(*username)); |
1911 | mode = convert_table_priv_string(priv_type_text); |
1912 | |
1913 | if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(tableoid))) |
1914 | PG_RETURN_NULL(); |
1915 | |
1916 | aclresult = pg_class_aclcheck(tableoid, roleid, mode); |
1917 | |
1918 | PG_RETURN_BOOL(aclresult == ACLCHECK_OK); |
1919 | } |
1920 | |
1921 | /* |
1922 | * has_table_privilege_id |
1923 | * Check user privileges on a table given |
1924 | * table oid, and text priv name. |
1925 | * current_user is assumed |
1926 | */ |
1927 | Datum |
1928 | has_table_privilege_id(PG_FUNCTION_ARGS) |
1929 | { |
1930 | Oid tableoid = PG_GETARG_OID(0); |
1931 | text *priv_type_text = PG_GETARG_TEXT_PP(1); |
1932 | Oid roleid; |
1933 | AclMode mode; |
1934 | AclResult aclresult; |
1935 | |
1936 | roleid = GetUserId(); |
1937 | mode = convert_table_priv_string(priv_type_text); |
1938 | |
1939 | if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(tableoid))) |
1940 | PG_RETURN_NULL(); |
1941 | |
1942 | aclresult = pg_class_aclcheck(tableoid, roleid, mode); |
1943 | |
1944 | PG_RETURN_BOOL(aclresult == ACLCHECK_OK); |
1945 | } |
1946 | |
1947 | /* |
1948 | * has_table_privilege_id_name |
1949 | * Check user privileges on a table given |
1950 | * roleid, text tablename, and text priv name. |
1951 | */ |
1952 | Datum |
1953 | has_table_privilege_id_name(PG_FUNCTION_ARGS) |
1954 | { |
1955 | Oid roleid = PG_GETARG_OID(0); |
1956 | text *tablename = PG_GETARG_TEXT_PP(1); |
1957 | text *priv_type_text = PG_GETARG_TEXT_PP(2); |
1958 | Oid tableoid; |
1959 | AclMode mode; |
1960 | AclResult aclresult; |
1961 | |
1962 | tableoid = convert_table_name(tablename); |
1963 | mode = convert_table_priv_string(priv_type_text); |
1964 | |
1965 | aclresult = pg_class_aclcheck(tableoid, roleid, mode); |
1966 | |
1967 | PG_RETURN_BOOL(aclresult == ACLCHECK_OK); |
1968 | } |
1969 | |
1970 | /* |
1971 | * has_table_privilege_id_id |
1972 | * Check user privileges on a table given |
1973 | * roleid, table oid, and text priv name. |
1974 | */ |
1975 | Datum |
1976 | has_table_privilege_id_id(PG_FUNCTION_ARGS) |
1977 | { |
1978 | Oid roleid = PG_GETARG_OID(0); |
1979 | Oid tableoid = PG_GETARG_OID(1); |
1980 | text *priv_type_text = PG_GETARG_TEXT_PP(2); |
1981 | AclMode mode; |
1982 | AclResult aclresult; |
1983 | |
1984 | mode = convert_table_priv_string(priv_type_text); |
1985 | |
1986 | if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(tableoid))) |
1987 | PG_RETURN_NULL(); |
1988 | |
1989 | aclresult = pg_class_aclcheck(tableoid, roleid, mode); |
1990 | |
1991 | PG_RETURN_BOOL(aclresult == ACLCHECK_OK); |
1992 | } |
1993 | |
1994 | /* |
1995 | * Support routines for has_table_privilege family. |
1996 | */ |
1997 | |
1998 | /* |
1999 | * Given a table name expressed as a string, look it up and return Oid |
2000 | */ |
2001 | static Oid |
2002 | convert_table_name(text *tablename) |
2003 | { |
2004 | RangeVar *relrv; |
2005 | |
2006 | relrv = makeRangeVarFromNameList(textToQualifiedNameList(tablename)); |
2007 | |
2008 | /* We might not even have permissions on this relation; don't lock it. */ |
2009 | return RangeVarGetRelid(relrv, NoLock, false); |
2010 | } |
2011 | |
2012 | /* |
2013 | * convert_table_priv_string |
2014 | * Convert text string to AclMode value. |
2015 | */ |
2016 | static AclMode |
2017 | convert_table_priv_string(text *priv_type_text) |
2018 | { |
2019 | static const priv_map table_priv_map[] = { |
2020 | {"SELECT" , ACL_SELECT}, |
2021 | {"SELECT WITH GRANT OPTION" , ACL_GRANT_OPTION_FOR(ACL_SELECT)}, |
2022 | {"INSERT" , ACL_INSERT}, |
2023 | {"INSERT WITH GRANT OPTION" , ACL_GRANT_OPTION_FOR(ACL_INSERT)}, |
2024 | {"UPDATE" , ACL_UPDATE}, |
2025 | {"UPDATE WITH GRANT OPTION" , ACL_GRANT_OPTION_FOR(ACL_UPDATE)}, |
2026 | {"DELETE" , ACL_DELETE}, |
2027 | {"DELETE WITH GRANT OPTION" , ACL_GRANT_OPTION_FOR(ACL_DELETE)}, |
2028 | {"TRUNCATE" , ACL_TRUNCATE}, |
2029 | {"TRUNCATE WITH GRANT OPTION" , ACL_GRANT_OPTION_FOR(ACL_TRUNCATE)}, |
2030 | {"REFERENCES" , ACL_REFERENCES}, |
2031 | {"REFERENCES WITH GRANT OPTION" , ACL_GRANT_OPTION_FOR(ACL_REFERENCES)}, |
2032 | {"TRIGGER" , ACL_TRIGGER}, |
2033 | {"TRIGGER WITH GRANT OPTION" , ACL_GRANT_OPTION_FOR(ACL_TRIGGER)}, |
2034 | {"RULE" , 0}, /* ignore old RULE privileges */ |
2035 | {"RULE WITH GRANT OPTION" , 0}, |
2036 | {NULL, 0} |
2037 | }; |
2038 | |
2039 | return convert_any_priv_string(priv_type_text, table_priv_map); |
2040 | } |
2041 | |
2042 | /* |
2043 | * has_sequence_privilege variants |
2044 | * These are all named "has_sequence_privilege" at the SQL level. |
2045 | * They take various combinations of relation name, relation OID, |
2046 | * user name, user OID, or implicit user = current_user. |
2047 | * |
2048 | * The result is a boolean value: true if user has the indicated |
2049 | * privilege, false if not. The variants that take a relation OID |
2050 | * return NULL if the OID doesn't exist. |
2051 | */ |
2052 | |
2053 | /* |
2054 | * has_sequence_privilege_name_name |
2055 | * Check user privileges on a sequence given |
2056 | * name username, text sequencename, and text priv name. |
2057 | */ |
2058 | Datum |
2059 | has_sequence_privilege_name_name(PG_FUNCTION_ARGS) |
2060 | { |
2061 | Name rolename = PG_GETARG_NAME(0); |
2062 | text *sequencename = PG_GETARG_TEXT_PP(1); |
2063 | text *priv_type_text = PG_GETARG_TEXT_PP(2); |
2064 | Oid roleid; |
2065 | Oid sequenceoid; |
2066 | AclMode mode; |
2067 | AclResult aclresult; |
2068 | |
2069 | roleid = get_role_oid_or_public(NameStr(*rolename)); |
2070 | mode = convert_sequence_priv_string(priv_type_text); |
2071 | sequenceoid = convert_table_name(sequencename); |
2072 | if (get_rel_relkind(sequenceoid) != RELKIND_SEQUENCE) |
2073 | ereport(ERROR, |
2074 | (errcode(ERRCODE_WRONG_OBJECT_TYPE), |
2075 | errmsg("\"%s\" is not a sequence" , |
2076 | text_to_cstring(sequencename)))); |
2077 | |
2078 | aclresult = pg_class_aclcheck(sequenceoid, roleid, mode); |
2079 | |
2080 | PG_RETURN_BOOL(aclresult == ACLCHECK_OK); |
2081 | } |
2082 | |
2083 | /* |
2084 | * has_sequence_privilege_name |
2085 | * Check user privileges on a sequence given |
2086 | * text sequencename and text priv name. |
2087 | * current_user is assumed |
2088 | */ |
2089 | Datum |
2090 | has_sequence_privilege_name(PG_FUNCTION_ARGS) |
2091 | { |
2092 | text *sequencename = PG_GETARG_TEXT_PP(0); |
2093 | text *priv_type_text = PG_GETARG_TEXT_PP(1); |
2094 | Oid roleid; |
2095 | Oid sequenceoid; |
2096 | AclMode mode; |
2097 | AclResult aclresult; |
2098 | |
2099 | roleid = GetUserId(); |
2100 | mode = convert_sequence_priv_string(priv_type_text); |
2101 | sequenceoid = convert_table_name(sequencename); |
2102 | if (get_rel_relkind(sequenceoid) != RELKIND_SEQUENCE) |
2103 | ereport(ERROR, |
2104 | (errcode(ERRCODE_WRONG_OBJECT_TYPE), |
2105 | errmsg("\"%s\" is not a sequence" , |
2106 | text_to_cstring(sequencename)))); |
2107 | |
2108 | aclresult = pg_class_aclcheck(sequenceoid, roleid, mode); |
2109 | |
2110 | PG_RETURN_BOOL(aclresult == ACLCHECK_OK); |
2111 | } |
2112 | |
2113 | /* |
2114 | * has_sequence_privilege_name_id |
2115 | * Check user privileges on a sequence given |
2116 | * name usename, sequence oid, and text priv name. |
2117 | */ |
2118 | Datum |
2119 | has_sequence_privilege_name_id(PG_FUNCTION_ARGS) |
2120 | { |
2121 | Name username = PG_GETARG_NAME(0); |
2122 | Oid sequenceoid = PG_GETARG_OID(1); |
2123 | text *priv_type_text = PG_GETARG_TEXT_PP(2); |
2124 | Oid roleid; |
2125 | AclMode mode; |
2126 | AclResult aclresult; |
2127 | char relkind; |
2128 | |
2129 | roleid = get_role_oid_or_public(NameStr(*username)); |
2130 | mode = convert_sequence_priv_string(priv_type_text); |
2131 | relkind = get_rel_relkind(sequenceoid); |
2132 | if (relkind == '\0') |
2133 | PG_RETURN_NULL(); |
2134 | else if (relkind != RELKIND_SEQUENCE) |
2135 | ereport(ERROR, |
2136 | (errcode(ERRCODE_WRONG_OBJECT_TYPE), |
2137 | errmsg("\"%s\" is not a sequence" , |
2138 | get_rel_name(sequenceoid)))); |
2139 | |
2140 | aclresult = pg_class_aclcheck(sequenceoid, roleid, mode); |
2141 | |
2142 | PG_RETURN_BOOL(aclresult == ACLCHECK_OK); |
2143 | } |
2144 | |
2145 | /* |
2146 | * has_sequence_privilege_id |
2147 | * Check user privileges on a sequence given |
2148 | * sequence oid, and text priv name. |
2149 | * current_user is assumed |
2150 | */ |
2151 | Datum |
2152 | has_sequence_privilege_id(PG_FUNCTION_ARGS) |
2153 | { |
2154 | Oid sequenceoid = PG_GETARG_OID(0); |
2155 | text *priv_type_text = PG_GETARG_TEXT_PP(1); |
2156 | Oid roleid; |
2157 | AclMode mode; |
2158 | AclResult aclresult; |
2159 | char relkind; |
2160 | |
2161 | roleid = GetUserId(); |
2162 | mode = convert_sequence_priv_string(priv_type_text); |
2163 | relkind = get_rel_relkind(sequenceoid); |
2164 | if (relkind == '\0') |
2165 | PG_RETURN_NULL(); |
2166 | else if (relkind != RELKIND_SEQUENCE) |
2167 | ereport(ERROR, |
2168 | (errcode(ERRCODE_WRONG_OBJECT_TYPE), |
2169 | errmsg("\"%s\" is not a sequence" , |
2170 | get_rel_name(sequenceoid)))); |
2171 | |
2172 | aclresult = pg_class_aclcheck(sequenceoid, roleid, mode); |
2173 | |
2174 | PG_RETURN_BOOL(aclresult == ACLCHECK_OK); |
2175 | } |
2176 | |
2177 | /* |
2178 | * has_sequence_privilege_id_name |
2179 | * Check user privileges on a sequence given |
2180 | * roleid, text sequencename, and text priv name. |
2181 | */ |
2182 | Datum |
2183 | has_sequence_privilege_id_name(PG_FUNCTION_ARGS) |
2184 | { |
2185 | Oid roleid = PG_GETARG_OID(0); |
2186 | text *sequencename = PG_GETARG_TEXT_PP(1); |
2187 | text *priv_type_text = PG_GETARG_TEXT_PP(2); |
2188 | Oid sequenceoid; |
2189 | AclMode mode; |
2190 | AclResult aclresult; |
2191 | |
2192 | mode = convert_sequence_priv_string(priv_type_text); |
2193 | sequenceoid = convert_table_name(sequencename); |
2194 | if (get_rel_relkind(sequenceoid) != RELKIND_SEQUENCE) |
2195 | ereport(ERROR, |
2196 | (errcode(ERRCODE_WRONG_OBJECT_TYPE), |
2197 | errmsg("\"%s\" is not a sequence" , |
2198 | text_to_cstring(sequencename)))); |
2199 | |
2200 | aclresult = pg_class_aclcheck(sequenceoid, roleid, mode); |
2201 | |
2202 | PG_RETURN_BOOL(aclresult == ACLCHECK_OK); |
2203 | } |
2204 | |
2205 | /* |
2206 | * has_sequence_privilege_id_id |
2207 | * Check user privileges on a sequence given |
2208 | * roleid, sequence oid, and text priv name. |
2209 | */ |
2210 | Datum |
2211 | has_sequence_privilege_id_id(PG_FUNCTION_ARGS) |
2212 | { |
2213 | Oid roleid = PG_GETARG_OID(0); |
2214 | Oid sequenceoid = PG_GETARG_OID(1); |
2215 | text *priv_type_text = PG_GETARG_TEXT_PP(2); |
2216 | AclMode mode; |
2217 | AclResult aclresult; |
2218 | char relkind; |
2219 | |
2220 | mode = convert_sequence_priv_string(priv_type_text); |
2221 | relkind = get_rel_relkind(sequenceoid); |
2222 | if (relkind == '\0') |
2223 | PG_RETURN_NULL(); |
2224 | else if (relkind != RELKIND_SEQUENCE) |
2225 | ereport(ERROR, |
2226 | (errcode(ERRCODE_WRONG_OBJECT_TYPE), |
2227 | errmsg("\"%s\" is not a sequence" , |
2228 | get_rel_name(sequenceoid)))); |
2229 | |
2230 | aclresult = pg_class_aclcheck(sequenceoid, roleid, mode); |
2231 | |
2232 | PG_RETURN_BOOL(aclresult == ACLCHECK_OK); |
2233 | } |
2234 | |
2235 | /* |
2236 | * convert_sequence_priv_string |
2237 | * Convert text string to AclMode value. |
2238 | */ |
2239 | static AclMode |
2240 | convert_sequence_priv_string(text *priv_type_text) |
2241 | { |
2242 | static const priv_map sequence_priv_map[] = { |
2243 | {"USAGE" , ACL_USAGE}, |
2244 | {"USAGE WITH GRANT OPTION" , ACL_GRANT_OPTION_FOR(ACL_USAGE)}, |
2245 | {"SELECT" , ACL_SELECT}, |
2246 | {"SELECT WITH GRANT OPTION" , ACL_GRANT_OPTION_FOR(ACL_SELECT)}, |
2247 | {"UPDATE" , ACL_UPDATE}, |
2248 | {"UPDATE WITH GRANT OPTION" , ACL_GRANT_OPTION_FOR(ACL_UPDATE)}, |
2249 | {NULL, 0} |
2250 | }; |
2251 | |
2252 | return convert_any_priv_string(priv_type_text, sequence_priv_map); |
2253 | } |
2254 | |
2255 | |
2256 | /* |
2257 | * has_any_column_privilege variants |
2258 | * These are all named "has_any_column_privilege" at the SQL level. |
2259 | * They take various combinations of relation name, relation OID, |
2260 | * user name, user OID, or implicit user = current_user. |
2261 | * |
2262 | * The result is a boolean value: true if user has the indicated |
2263 | * privilege for any column of the table, false if not. The variants |
2264 | * that take a relation OID return NULL if the OID doesn't exist. |
2265 | */ |
2266 | |
2267 | /* |
2268 | * has_any_column_privilege_name_name |
2269 | * Check user privileges on any column of a table given |
2270 | * name username, text tablename, and text priv name. |
2271 | */ |
2272 | Datum |
2273 | has_any_column_privilege_name_name(PG_FUNCTION_ARGS) |
2274 | { |
2275 | Name rolename = PG_GETARG_NAME(0); |
2276 | text *tablename = PG_GETARG_TEXT_PP(1); |
2277 | text *priv_type_text = PG_GETARG_TEXT_PP(2); |
2278 | Oid roleid; |
2279 | Oid tableoid; |
2280 | AclMode mode; |
2281 | AclResult aclresult; |
2282 | |
2283 | roleid = get_role_oid_or_public(NameStr(*rolename)); |
2284 | tableoid = convert_table_name(tablename); |
2285 | mode = convert_column_priv_string(priv_type_text); |
2286 | |
2287 | /* First check at table level, then examine each column if needed */ |
2288 | aclresult = pg_class_aclcheck(tableoid, roleid, mode); |
2289 | if (aclresult != ACLCHECK_OK) |
2290 | aclresult = pg_attribute_aclcheck_all(tableoid, roleid, mode, |
2291 | ACLMASK_ANY); |
2292 | |
2293 | PG_RETURN_BOOL(aclresult == ACLCHECK_OK); |
2294 | } |
2295 | |
2296 | /* |
2297 | * has_any_column_privilege_name |
2298 | * Check user privileges on any column of a table given |
2299 | * text tablename and text priv name. |
2300 | * current_user is assumed |
2301 | */ |
2302 | Datum |
2303 | has_any_column_privilege_name(PG_FUNCTION_ARGS) |
2304 | { |
2305 | text *tablename = PG_GETARG_TEXT_PP(0); |
2306 | text *priv_type_text = PG_GETARG_TEXT_PP(1); |
2307 | Oid roleid; |
2308 | Oid tableoid; |
2309 | AclMode mode; |
2310 | AclResult aclresult; |
2311 | |
2312 | roleid = GetUserId(); |
2313 | tableoid = convert_table_name(tablename); |
2314 | mode = convert_column_priv_string(priv_type_text); |
2315 | |
2316 | /* First check at table level, then examine each column if needed */ |
2317 | aclresult = pg_class_aclcheck(tableoid, roleid, mode); |
2318 | if (aclresult != ACLCHECK_OK) |
2319 | aclresult = pg_attribute_aclcheck_all(tableoid, roleid, mode, |
2320 | ACLMASK_ANY); |
2321 | |
2322 | PG_RETURN_BOOL(aclresult == ACLCHECK_OK); |
2323 | } |
2324 | |
2325 | /* |
2326 | * has_any_column_privilege_name_id |
2327 | * Check user privileges on any column of a table given |
2328 | * name usename, table oid, and text priv name. |
2329 | */ |
2330 | Datum |
2331 | has_any_column_privilege_name_id(PG_FUNCTION_ARGS) |
2332 | { |
2333 | Name username = PG_GETARG_NAME(0); |
2334 | Oid tableoid = PG_GETARG_OID(1); |
2335 | text *priv_type_text = PG_GETARG_TEXT_PP(2); |
2336 | Oid roleid; |
2337 | AclMode mode; |
2338 | AclResult aclresult; |
2339 | |
2340 | roleid = get_role_oid_or_public(NameStr(*username)); |
2341 | mode = convert_column_priv_string(priv_type_text); |
2342 | |
2343 | if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(tableoid))) |
2344 | PG_RETURN_NULL(); |
2345 | |
2346 | /* First check at table level, then examine each column if needed */ |
2347 | aclresult = pg_class_aclcheck(tableoid, roleid, mode); |
2348 | if (aclresult != ACLCHECK_OK) |
2349 | aclresult = pg_attribute_aclcheck_all(tableoid, roleid, mode, |
2350 | ACLMASK_ANY); |
2351 | |
2352 | PG_RETURN_BOOL(aclresult == ACLCHECK_OK); |
2353 | } |
2354 | |
2355 | /* |
2356 | * has_any_column_privilege_id |
2357 | * Check user privileges on any column of a table given |
2358 | * table oid, and text priv name. |
2359 | * current_user is assumed |
2360 | */ |
2361 | Datum |
2362 | has_any_column_privilege_id(PG_FUNCTION_ARGS) |
2363 | { |
2364 | Oid tableoid = PG_GETARG_OID(0); |
2365 | text *priv_type_text = PG_GETARG_TEXT_PP(1); |
2366 | Oid roleid; |
2367 | AclMode mode; |
2368 | AclResult aclresult; |
2369 | |
2370 | roleid = GetUserId(); |
2371 | mode = convert_column_priv_string(priv_type_text); |
2372 | |
2373 | if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(tableoid))) |
2374 | PG_RETURN_NULL(); |
2375 | |
2376 | /* First check at table level, then examine each column if needed */ |
2377 | aclresult = pg_class_aclcheck(tableoid, roleid, mode); |
2378 | if (aclresult != ACLCHECK_OK) |
2379 | aclresult = pg_attribute_aclcheck_all(tableoid, roleid, mode, |
2380 | ACLMASK_ANY); |
2381 | |
2382 | PG_RETURN_BOOL(aclresult == ACLCHECK_OK); |
2383 | } |
2384 | |
2385 | /* |
2386 | * has_any_column_privilege_id_name |
2387 | * Check user privileges on any column of a table given |
2388 | * roleid, text tablename, and text priv name. |
2389 | */ |
2390 | Datum |
2391 | has_any_column_privilege_id_name(PG_FUNCTION_ARGS) |
2392 | { |
2393 | Oid roleid = PG_GETARG_OID(0); |
2394 | text *tablename = PG_GETARG_TEXT_PP(1); |
2395 | text *priv_type_text = PG_GETARG_TEXT_PP(2); |
2396 | Oid tableoid; |
2397 | AclMode mode; |
2398 | AclResult aclresult; |
2399 | |
2400 | tableoid = convert_table_name(tablename); |
2401 | mode = convert_column_priv_string(priv_type_text); |
2402 | |
2403 | /* First check at table level, then examine each column if needed */ |
2404 | aclresult = pg_class_aclcheck(tableoid, roleid, mode); |
2405 | if (aclresult != ACLCHECK_OK) |
2406 | aclresult = pg_attribute_aclcheck_all(tableoid, roleid, mode, |
2407 | ACLMASK_ANY); |
2408 | |
2409 | PG_RETURN_BOOL(aclresult == ACLCHECK_OK); |
2410 | } |
2411 | |
2412 | /* |
2413 | * has_any_column_privilege_id_id |
2414 | * Check user privileges on any column of a table given |
2415 | * roleid, table oid, and text priv name. |
2416 | */ |
2417 | Datum |
2418 | has_any_column_privilege_id_id(PG_FUNCTION_ARGS) |
2419 | { |
2420 | Oid roleid = PG_GETARG_OID(0); |
2421 | Oid tableoid = PG_GETARG_OID(1); |
2422 | text *priv_type_text = PG_GETARG_TEXT_PP(2); |
2423 | AclMode mode; |
2424 | AclResult aclresult; |
2425 | |
2426 | mode = convert_column_priv_string(priv_type_text); |
2427 | |
2428 | if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(tableoid))) |
2429 | PG_RETURN_NULL(); |
2430 | |
2431 | /* First check at table level, then examine each column if needed */ |
2432 | aclresult = pg_class_aclcheck(tableoid, roleid, mode); |
2433 | if (aclresult != ACLCHECK_OK) |
2434 | aclresult = pg_attribute_aclcheck_all(tableoid, roleid, mode, |
2435 | ACLMASK_ANY); |
2436 | |
2437 | PG_RETURN_BOOL(aclresult == ACLCHECK_OK); |
2438 | } |
2439 | |
2440 | |
2441 | /* |
2442 | * has_column_privilege variants |
2443 | * These are all named "has_column_privilege" at the SQL level. |
2444 | * They take various combinations of relation name, relation OID, |
2445 | * column name, column attnum, user name, user OID, or |
2446 | * implicit user = current_user. |
2447 | * |
2448 | * The result is a boolean value: true if user has the indicated |
2449 | * privilege, false if not. The variants that take a relation OID |
2450 | * return NULL (rather than throwing an error) if that relation OID |
2451 | * doesn't exist. Likewise, the variants that take an integer attnum |
2452 | * return NULL (rather than throwing an error) if there is no such |
2453 | * pg_attribute entry. All variants return NULL if an attisdropped |
2454 | * column is selected. These rules are meant to avoid unnecessary |
2455 | * failures in queries that scan pg_attribute. |
2456 | */ |
2457 | |
2458 | /* |
2459 | * column_privilege_check: check column privileges, but don't throw an error |
2460 | * for dropped column or table |
2461 | * |
2462 | * Returns 1 if have the privilege, 0 if not, -1 if dropped column/table. |
2463 | */ |
2464 | static int |
2465 | column_privilege_check(Oid tableoid, AttrNumber attnum, |
2466 | Oid roleid, AclMode mode) |
2467 | { |
2468 | AclResult aclresult; |
2469 | HeapTuple attTuple; |
2470 | Form_pg_attribute attributeForm; |
2471 | |
2472 | /* |
2473 | * If convert_column_name failed, we can just return -1 immediately. |
2474 | */ |
2475 | if (attnum == InvalidAttrNumber) |
2476 | return -1; |
2477 | |
2478 | /* |
2479 | * First check if we have the privilege at the table level. We check |
2480 | * existence of the pg_class row before risking calling pg_class_aclcheck. |
2481 | * Note: it might seem there's a race condition against concurrent DROP, |
2482 | * but really it's safe because there will be no syscache flush between |
2483 | * here and there. So if we see the row in the syscache, so will |
2484 | * pg_class_aclcheck. |
2485 | */ |
2486 | if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(tableoid))) |
2487 | return -1; |
2488 | |
2489 | aclresult = pg_class_aclcheck(tableoid, roleid, mode); |
2490 | |
2491 | if (aclresult == ACLCHECK_OK) |
2492 | return true; |
2493 | |
2494 | /* |
2495 | * No table privilege, so try per-column privileges. Again, we have to |
2496 | * check for dropped attribute first, and we rely on the syscache not to |
2497 | * notice a concurrent drop before pg_attribute_aclcheck fetches the row. |
2498 | */ |
2499 | attTuple = SearchSysCache2(ATTNUM, |
2500 | ObjectIdGetDatum(tableoid), |
2501 | Int16GetDatum(attnum)); |
2502 | if (!HeapTupleIsValid(attTuple)) |
2503 | return -1; |
2504 | attributeForm = (Form_pg_attribute) GETSTRUCT(attTuple); |
2505 | if (attributeForm->attisdropped) |
2506 | { |
2507 | ReleaseSysCache(attTuple); |
2508 | return -1; |
2509 | } |
2510 | ReleaseSysCache(attTuple); |
2511 | |
2512 | aclresult = pg_attribute_aclcheck(tableoid, attnum, roleid, mode); |
2513 | |
2514 | return (aclresult == ACLCHECK_OK); |
2515 | } |
2516 | |
2517 | /* |
2518 | * has_column_privilege_name_name_name |
2519 | * Check user privileges on a column given |
2520 | * name username, text tablename, text colname, and text priv name. |
2521 | */ |
2522 | Datum |
2523 | has_column_privilege_name_name_name(PG_FUNCTION_ARGS) |
2524 | { |
2525 | Name rolename = PG_GETARG_NAME(0); |
2526 | text *tablename = PG_GETARG_TEXT_PP(1); |
2527 | text *column = PG_GETARG_TEXT_PP(2); |
2528 | text *priv_type_text = PG_GETARG_TEXT_PP(3); |
2529 | Oid roleid; |
2530 | Oid tableoid; |
2531 | AttrNumber colattnum; |
2532 | AclMode mode; |
2533 | int privresult; |
2534 | |
2535 | roleid = get_role_oid_or_public(NameStr(*rolename)); |
2536 | tableoid = convert_table_name(tablename); |
2537 | colattnum = convert_column_name(tableoid, column); |
2538 | mode = convert_column_priv_string(priv_type_text); |
2539 | |
2540 | privresult = column_privilege_check(tableoid, colattnum, roleid, mode); |
2541 | if (privresult < 0) |
2542 | PG_RETURN_NULL(); |
2543 | PG_RETURN_BOOL(privresult); |
2544 | } |
2545 | |
2546 | /* |
2547 | * has_column_privilege_name_name_attnum |
2548 | * Check user privileges on a column given |
2549 | * name username, text tablename, int attnum, and text priv name. |
2550 | */ |
2551 | Datum |
2552 | has_column_privilege_name_name_attnum(PG_FUNCTION_ARGS) |
2553 | { |
2554 | Name rolename = PG_GETARG_NAME(0); |
2555 | text *tablename = PG_GETARG_TEXT_PP(1); |
2556 | AttrNumber colattnum = PG_GETARG_INT16(2); |
2557 | text *priv_type_text = PG_GETARG_TEXT_PP(3); |
2558 | Oid roleid; |
2559 | Oid tableoid; |
2560 | AclMode mode; |
2561 | int privresult; |
2562 | |
2563 | roleid = get_role_oid_or_public(NameStr(*rolename)); |
2564 | tableoid = convert_table_name(tablename); |
2565 | mode = convert_column_priv_string(priv_type_text); |
2566 | |
2567 | privresult = column_privilege_check(tableoid, colattnum, roleid, mode); |
2568 | if (privresult < 0) |
2569 | PG_RETURN_NULL(); |
2570 | PG_RETURN_BOOL(privresult); |
2571 | } |
2572 | |
2573 | /* |
2574 | * has_column_privilege_name_id_name |
2575 | * Check user privileges on a column given |
2576 | * name username, table oid, text colname, and text priv name. |
2577 | */ |
2578 | Datum |
2579 | has_column_privilege_name_id_name(PG_FUNCTION_ARGS) |
2580 | { |
2581 | Name username = PG_GETARG_NAME(0); |
2582 | Oid tableoid = PG_GETARG_OID(1); |
2583 | text *column = PG_GETARG_TEXT_PP(2); |
2584 | text *priv_type_text = PG_GETARG_TEXT_PP(3); |
2585 | Oid roleid; |
2586 | AttrNumber colattnum; |
2587 | AclMode mode; |
2588 | int privresult; |
2589 | |
2590 | roleid = get_role_oid_or_public(NameStr(*username)); |
2591 | colattnum = convert_column_name(tableoid, column); |
2592 | mode = convert_column_priv_string(priv_type_text); |
2593 | |
2594 | privresult = column_privilege_check(tableoid, colattnum, roleid, mode); |
2595 | if (privresult < 0) |
2596 | PG_RETURN_NULL(); |
2597 | PG_RETURN_BOOL(privresult); |
2598 | } |
2599 | |
2600 | /* |
2601 | * has_column_privilege_name_id_attnum |
2602 | * Check user privileges on a column given |
2603 | * name username, table oid, int attnum, and text priv name. |
2604 | */ |
2605 | Datum |
2606 | has_column_privilege_name_id_attnum(PG_FUNCTION_ARGS) |
2607 | { |
2608 | Name username = PG_GETARG_NAME(0); |
2609 | Oid tableoid = PG_GETARG_OID(1); |
2610 | AttrNumber colattnum = PG_GETARG_INT16(2); |
2611 | text *priv_type_text = PG_GETARG_TEXT_PP(3); |
2612 | Oid roleid; |
2613 | AclMode mode; |
2614 | int privresult; |
2615 | |
2616 | roleid = get_role_oid_or_public(NameStr(*username)); |
2617 | mode = convert_column_priv_string(priv_type_text); |
2618 | |
2619 | privresult = column_privilege_check(tableoid, colattnum, roleid, mode); |
2620 | if (privresult < 0) |
2621 | PG_RETURN_NULL(); |
2622 | PG_RETURN_BOOL(privresult); |
2623 | } |
2624 | |
2625 | /* |
2626 | * has_column_privilege_id_name_name |
2627 | * Check user privileges on a column given |
2628 | * oid roleid, text tablename, text colname, and text priv name. |
2629 | */ |
2630 | Datum |
2631 | has_column_privilege_id_name_name(PG_FUNCTION_ARGS) |
2632 | { |
2633 | Oid roleid = PG_GETARG_OID(0); |
2634 | text *tablename = PG_GETARG_TEXT_PP(1); |
2635 | text *column = PG_GETARG_TEXT_PP(2); |
2636 | text *priv_type_text = PG_GETARG_TEXT_PP(3); |
2637 | Oid tableoid; |
2638 | AttrNumber colattnum; |
2639 | AclMode mode; |
2640 | int privresult; |
2641 | |
2642 | tableoid = convert_table_name(tablename); |
2643 | colattnum = convert_column_name(tableoid, column); |
2644 | mode = convert_column_priv_string(priv_type_text); |
2645 | |
2646 | privresult = column_privilege_check(tableoid, colattnum, roleid, mode); |
2647 | if (privresult < 0) |
2648 | PG_RETURN_NULL(); |
2649 | PG_RETURN_BOOL(privresult); |
2650 | } |
2651 | |
2652 | /* |
2653 | * has_column_privilege_id_name_attnum |
2654 | * Check user privileges on a column given |
2655 | * oid roleid, text tablename, int attnum, and text priv name. |
2656 | */ |
2657 | Datum |
2658 | has_column_privilege_id_name_attnum(PG_FUNCTION_ARGS) |
2659 | { |
2660 | Oid roleid = PG_GETARG_OID(0); |
2661 | text *tablename = PG_GETARG_TEXT_PP(1); |
2662 | AttrNumber colattnum = PG_GETARG_INT16(2); |
2663 | text *priv_type_text = PG_GETARG_TEXT_PP(3); |
2664 | Oid tableoid; |
2665 | AclMode mode; |
2666 | int privresult; |
2667 | |
2668 | tableoid = convert_table_name(tablename); |
2669 | mode = convert_column_priv_string(priv_type_text); |
2670 | |
2671 | privresult = column_privilege_check(tableoid, colattnum, roleid, mode); |
2672 | if (privresult < 0) |
2673 | PG_RETURN_NULL(); |
2674 | PG_RETURN_BOOL(privresult); |
2675 | } |
2676 | |
2677 | /* |
2678 | * has_column_privilege_id_id_name |
2679 | * Check user privileges on a column given |
2680 | * oid roleid, table oid, text colname, and text priv name. |
2681 | */ |
2682 | Datum |
2683 | has_column_privilege_id_id_name(PG_FUNCTION_ARGS) |
2684 | { |
2685 | Oid roleid = PG_GETARG_OID(0); |
2686 | Oid tableoid = PG_GETARG_OID(1); |
2687 | text *column = PG_GETARG_TEXT_PP(2); |
2688 | text *priv_type_text = PG_GETARG_TEXT_PP(3); |
2689 | AttrNumber colattnum; |
2690 | AclMode mode; |
2691 | int privresult; |
2692 | |
2693 | colattnum = convert_column_name(tableoid, column); |
2694 | mode = convert_column_priv_string(priv_type_text); |
2695 | |
2696 | privresult = column_privilege_check(tableoid, colattnum, roleid, mode); |
2697 | if (privresult < 0) |
2698 | PG_RETURN_NULL(); |
2699 | PG_RETURN_BOOL(privresult); |
2700 | } |
2701 | |
2702 | /* |
2703 | * has_column_privilege_id_id_attnum |
2704 | * Check user privileges on a column given |
2705 | * oid roleid, table oid, int attnum, and text priv name. |
2706 | */ |
2707 | Datum |
2708 | has_column_privilege_id_id_attnum(PG_FUNCTION_ARGS) |
2709 | { |
2710 | Oid roleid = PG_GETARG_OID(0); |
2711 | Oid tableoid = PG_GETARG_OID(1); |
2712 | AttrNumber colattnum = PG_GETARG_INT16(2); |
2713 | text *priv_type_text = PG_GETARG_TEXT_PP(3); |
2714 | AclMode mode; |
2715 | int privresult; |
2716 | |
2717 | mode = convert_column_priv_string(priv_type_text); |
2718 | |
2719 | privresult = column_privilege_check(tableoid, colattnum, roleid, mode); |
2720 | if (privresult < 0) |
2721 | PG_RETURN_NULL(); |
2722 | PG_RETURN_BOOL(privresult); |
2723 | } |
2724 | |
2725 | /* |
2726 | * has_column_privilege_name_name |
2727 | * Check user privileges on a column given |
2728 | * text tablename, text colname, and text priv name. |
2729 | * current_user is assumed |
2730 | */ |
2731 | Datum |
2732 | has_column_privilege_name_name(PG_FUNCTION_ARGS) |
2733 | { |
2734 | text *tablename = PG_GETARG_TEXT_PP(0); |
2735 | text *column = PG_GETARG_TEXT_PP(1); |
2736 | text *priv_type_text = PG_GETARG_TEXT_PP(2); |
2737 | Oid roleid; |
2738 | Oid tableoid; |
2739 | AttrNumber colattnum; |
2740 | AclMode mode; |
2741 | int privresult; |
2742 | |
2743 | roleid = GetUserId(); |
2744 | tableoid = convert_table_name(tablename); |
2745 | colattnum = convert_column_name(tableoid, column); |
2746 | mode = convert_column_priv_string(priv_type_text); |
2747 | |
2748 | privresult = column_privilege_check(tableoid, colattnum, roleid, mode); |
2749 | if (privresult < 0) |
2750 | PG_RETURN_NULL(); |
2751 | PG_RETURN_BOOL(privresult); |
2752 | } |
2753 | |
2754 | /* |
2755 | * has_column_privilege_name_attnum |
2756 | * Check user privileges on a column given |
2757 | * text tablename, int attnum, and text priv name. |
2758 | * current_user is assumed |
2759 | */ |
2760 | Datum |
2761 | has_column_privilege_name_attnum(PG_FUNCTION_ARGS) |
2762 | { |
2763 | text *tablename = PG_GETARG_TEXT_PP(0); |
2764 | AttrNumber colattnum = PG_GETARG_INT16(1); |
2765 | text *priv_type_text = PG_GETARG_TEXT_PP(2); |
2766 | Oid roleid; |
2767 | Oid tableoid; |
2768 | AclMode mode; |
2769 | int privresult; |
2770 | |
2771 | roleid = GetUserId(); |
2772 | tableoid = convert_table_name(tablename); |
2773 | mode = convert_column_priv_string(priv_type_text); |
2774 | |
2775 | privresult = column_privilege_check(tableoid, colattnum, roleid, mode); |
2776 | if (privresult < 0) |
2777 | PG_RETURN_NULL(); |
2778 | PG_RETURN_BOOL(privresult); |
2779 | } |
2780 | |
2781 | /* |
2782 | * has_column_privilege_id_name |
2783 | * Check user privileges on a column given |
2784 | * table oid, text colname, and text priv name. |
2785 | * current_user is assumed |
2786 | */ |
2787 | Datum |
2788 | has_column_privilege_id_name(PG_FUNCTION_ARGS) |
2789 | { |
2790 | Oid tableoid = PG_GETARG_OID(0); |
2791 | text *column = PG_GETARG_TEXT_PP(1); |
2792 | text *priv_type_text = PG_GETARG_TEXT_PP(2); |
2793 | Oid roleid; |
2794 | AttrNumber colattnum; |
2795 | AclMode mode; |
2796 | int privresult; |
2797 | |
2798 | roleid = GetUserId(); |
2799 | colattnum = convert_column_name(tableoid, column); |
2800 | mode = convert_column_priv_string(priv_type_text); |
2801 | |
2802 | privresult = column_privilege_check(tableoid, colattnum, roleid, mode); |
2803 | if (privresult < 0) |
2804 | PG_RETURN_NULL(); |
2805 | PG_RETURN_BOOL(privresult); |
2806 | } |
2807 | |
2808 | /* |
2809 | * has_column_privilege_id_attnum |
2810 | * Check user privileges on a column given |
2811 | * table oid, int attnum, and text priv name. |
2812 | * current_user is assumed |
2813 | */ |
2814 | Datum |
2815 | has_column_privilege_id_attnum(PG_FUNCTION_ARGS) |
2816 | { |
2817 | Oid tableoid = PG_GETARG_OID(0); |
2818 | AttrNumber colattnum = PG_GETARG_INT16(1); |
2819 | text *priv_type_text = PG_GETARG_TEXT_PP(2); |
2820 | Oid roleid; |
2821 | AclMode mode; |
2822 | int privresult; |
2823 | |
2824 | roleid = GetUserId(); |
2825 | mode = convert_column_priv_string(priv_type_text); |
2826 | |
2827 | privresult = column_privilege_check(tableoid, colattnum, roleid, mode); |
2828 | if (privresult < 0) |
2829 | PG_RETURN_NULL(); |
2830 | PG_RETURN_BOOL(privresult); |
2831 | } |
2832 | |
2833 | /* |
2834 | * Support routines for has_column_privilege family. |
2835 | */ |
2836 | |
2837 | /* |
2838 | * Given a table OID and a column name expressed as a string, look it up |
2839 | * and return the column number. Returns InvalidAttrNumber in cases |
2840 | * where caller should return NULL instead of failing. |
2841 | */ |
2842 | static AttrNumber |
2843 | convert_column_name(Oid tableoid, text *column) |
2844 | { |
2845 | char *colname; |
2846 | HeapTuple attTuple; |
2847 | AttrNumber attnum; |
2848 | |
2849 | colname = text_to_cstring(column); |
2850 | |
2851 | /* |
2852 | * We don't use get_attnum() here because it will report that dropped |
2853 | * columns don't exist. We need to treat dropped columns differently from |
2854 | * nonexistent columns. |
2855 | */ |
2856 | attTuple = SearchSysCache2(ATTNAME, |
2857 | ObjectIdGetDatum(tableoid), |
2858 | CStringGetDatum(colname)); |
2859 | if (HeapTupleIsValid(attTuple)) |
2860 | { |
2861 | Form_pg_attribute attributeForm; |
2862 | |
2863 | attributeForm = (Form_pg_attribute) GETSTRUCT(attTuple); |
2864 | /* We want to return NULL for dropped columns */ |
2865 | if (attributeForm->attisdropped) |
2866 | attnum = InvalidAttrNumber; |
2867 | else |
2868 | attnum = attributeForm->attnum; |
2869 | ReleaseSysCache(attTuple); |
2870 | } |
2871 | else |
2872 | { |
2873 | char *tablename = get_rel_name(tableoid); |
2874 | |
2875 | /* |
2876 | * If the table OID is bogus, or it's just been dropped, we'll get |
2877 | * NULL back. In such cases we want has_column_privilege to return |
2878 | * NULL too, so just return InvalidAttrNumber. |
2879 | */ |
2880 | if (tablename != NULL) |
2881 | { |
2882 | /* tableoid exists, colname does not, so throw error */ |
2883 | ereport(ERROR, |
2884 | (errcode(ERRCODE_UNDEFINED_COLUMN), |
2885 | errmsg("column \"%s\" of relation \"%s\" does not exist" , |
2886 | colname, tablename))); |
2887 | } |
2888 | /* tableoid doesn't exist, so act like attisdropped case */ |
2889 | attnum = InvalidAttrNumber; |
2890 | } |
2891 | |
2892 | pfree(colname); |
2893 | return attnum; |
2894 | } |
2895 | |
2896 | /* |
2897 | * convert_column_priv_string |
2898 | * Convert text string to AclMode value. |
2899 | */ |
2900 | static AclMode |
2901 | convert_column_priv_string(text *priv_type_text) |
2902 | { |
2903 | static const priv_map column_priv_map[] = { |
2904 | {"SELECT" , ACL_SELECT}, |
2905 | {"SELECT WITH GRANT OPTION" , ACL_GRANT_OPTION_FOR(ACL_SELECT)}, |
2906 | {"INSERT" , ACL_INSERT}, |
2907 | {"INSERT WITH GRANT OPTION" , ACL_GRANT_OPTION_FOR(ACL_INSERT)}, |
2908 | {"UPDATE" , ACL_UPDATE}, |
2909 | {"UPDATE WITH GRANT OPTION" , ACL_GRANT_OPTION_FOR(ACL_UPDATE)}, |
2910 | {"REFERENCES" , ACL_REFERENCES}, |
2911 | {"REFERENCES WITH GRANT OPTION" , ACL_GRANT_OPTION_FOR(ACL_REFERENCES)}, |
2912 | {NULL, 0} |
2913 | }; |
2914 | |
2915 | return convert_any_priv_string(priv_type_text, column_priv_map); |
2916 | } |
2917 | |
2918 | |
2919 | /* |
2920 | * has_database_privilege variants |
2921 | * These are all named "has_database_privilege" at the SQL level. |
2922 | * They take various combinations of database name, database OID, |
2923 | * user name, user OID, or implicit user = current_user. |
2924 | * |
2925 | * The result is a boolean value: true if user has the indicated |
2926 | * privilege, false if not, or NULL if object doesn't exist. |
2927 | */ |
2928 | |
2929 | /* |
2930 | * has_database_privilege_name_name |
2931 | * Check user privileges on a database given |
2932 | * name username, text databasename, and text priv name. |
2933 | */ |
2934 | Datum |
2935 | has_database_privilege_name_name(PG_FUNCTION_ARGS) |
2936 | { |
2937 | Name username = PG_GETARG_NAME(0); |
2938 | text *databasename = PG_GETARG_TEXT_PP(1); |
2939 | text *priv_type_text = PG_GETARG_TEXT_PP(2); |
2940 | Oid roleid; |
2941 | Oid databaseoid; |
2942 | AclMode mode; |
2943 | AclResult aclresult; |
2944 | |
2945 | roleid = get_role_oid_or_public(NameStr(*username)); |
2946 | databaseoid = convert_database_name(databasename); |
2947 | mode = convert_database_priv_string(priv_type_text); |
2948 | |
2949 | aclresult = pg_database_aclcheck(databaseoid, roleid, mode); |
2950 | |
2951 | PG_RETURN_BOOL(aclresult == ACLCHECK_OK); |
2952 | } |
2953 | |
2954 | /* |
2955 | * has_database_privilege_name |
2956 | * Check user privileges on a database given |
2957 | * text databasename and text priv name. |
2958 | * current_user is assumed |
2959 | */ |
2960 | Datum |
2961 | has_database_privilege_name(PG_FUNCTION_ARGS) |
2962 | { |
2963 | text *databasename = PG_GETARG_TEXT_PP(0); |
2964 | text *priv_type_text = PG_GETARG_TEXT_PP(1); |
2965 | Oid roleid; |
2966 | Oid databaseoid; |
2967 | AclMode mode; |
2968 | AclResult aclresult; |
2969 | |
2970 | roleid = GetUserId(); |
2971 | databaseoid = convert_database_name(databasename); |
2972 | mode = convert_database_priv_string(priv_type_text); |
2973 | |
2974 | aclresult = pg_database_aclcheck(databaseoid, roleid, mode); |
2975 | |
2976 | PG_RETURN_BOOL(aclresult == ACLCHECK_OK); |
2977 | } |
2978 | |
2979 | /* |
2980 | * has_database_privilege_name_id |
2981 | * Check user privileges on a database given |
2982 | * name usename, database oid, and text priv name. |
2983 | */ |
2984 | Datum |
2985 | has_database_privilege_name_id(PG_FUNCTION_ARGS) |
2986 | { |
2987 | Name username = PG_GETARG_NAME(0); |
2988 | Oid databaseoid = PG_GETARG_OID(1); |
2989 | text *priv_type_text = PG_GETARG_TEXT_PP(2); |
2990 | Oid roleid; |
2991 | AclMode mode; |
2992 | AclResult aclresult; |
2993 | |
2994 | roleid = get_role_oid_or_public(NameStr(*username)); |
2995 | mode = convert_database_priv_string(priv_type_text); |
2996 | |
2997 | if (!SearchSysCacheExists1(DATABASEOID, ObjectIdGetDatum(databaseoid))) |
2998 | PG_RETURN_NULL(); |
2999 | |
3000 | aclresult = pg_database_aclcheck(databaseoid, roleid, mode); |
3001 | |
3002 | PG_RETURN_BOOL(aclresult == ACLCHECK_OK); |
3003 | } |
3004 | |
3005 | /* |
3006 | * has_database_privilege_id |
3007 | * Check user privileges on a database given |
3008 | * database oid, and text priv name. |
3009 | * current_user is assumed |
3010 | */ |
3011 | Datum |
3012 | has_database_privilege_id(PG_FUNCTION_ARGS) |
3013 | { |
3014 | Oid databaseoid = PG_GETARG_OID(0); |
3015 | text *priv_type_text = PG_GETARG_TEXT_PP(1); |
3016 | Oid roleid; |
3017 | AclMode mode; |
3018 | AclResult aclresult; |
3019 | |
3020 | roleid = GetUserId(); |
3021 | mode = convert_database_priv_string(priv_type_text); |
3022 | |
3023 | if (!SearchSysCacheExists1(DATABASEOID, ObjectIdGetDatum(databaseoid))) |
3024 | PG_RETURN_NULL(); |
3025 | |
3026 | aclresult = pg_database_aclcheck(databaseoid, roleid, mode); |
3027 | |
3028 | PG_RETURN_BOOL(aclresult == ACLCHECK_OK); |
3029 | } |
3030 | |
3031 | /* |
3032 | * has_database_privilege_id_name |
3033 | * Check user privileges on a database given |
3034 | * roleid, text databasename, and text priv name. |
3035 | */ |
3036 | Datum |
3037 | has_database_privilege_id_name(PG_FUNCTION_ARGS) |
3038 | { |
3039 | Oid roleid = PG_GETARG_OID(0); |
3040 | text *databasename = PG_GETARG_TEXT_PP(1); |
3041 | text *priv_type_text = PG_GETARG_TEXT_PP(2); |
3042 | Oid databaseoid; |
3043 | AclMode mode; |
3044 | AclResult aclresult; |
3045 | |
3046 | databaseoid = convert_database_name(databasename); |
3047 | mode = convert_database_priv_string(priv_type_text); |
3048 | |
3049 | aclresult = pg_database_aclcheck(databaseoid, roleid, mode); |
3050 | |
3051 | PG_RETURN_BOOL(aclresult == ACLCHECK_OK); |
3052 | } |
3053 | |
3054 | /* |
3055 | * has_database_privilege_id_id |
3056 | * Check user privileges on a database given |
3057 | * roleid, database oid, and text priv name. |
3058 | */ |
3059 | Datum |
3060 | has_database_privilege_id_id(PG_FUNCTION_ARGS) |
3061 | { |
3062 | Oid roleid = PG_GETARG_OID(0); |
3063 | Oid databaseoid = PG_GETARG_OID(1); |
3064 | text *priv_type_text = PG_GETARG_TEXT_PP(2); |
3065 | AclMode mode; |
3066 | AclResult aclresult; |
3067 | |
3068 | mode = convert_database_priv_string(priv_type_text); |
3069 | |
3070 | if (!SearchSysCacheExists1(DATABASEOID, ObjectIdGetDatum(databaseoid))) |
3071 | PG_RETURN_NULL(); |
3072 | |
3073 | aclresult = pg_database_aclcheck(databaseoid, roleid, mode); |
3074 | |
3075 | PG_RETURN_BOOL(aclresult == ACLCHECK_OK); |
3076 | } |
3077 | |
3078 | /* |
3079 | * Support routines for has_database_privilege family. |
3080 | */ |
3081 | |
3082 | /* |
3083 | * Given a database name expressed as a string, look it up and return Oid |
3084 | */ |
3085 | static Oid |
3086 | convert_database_name(text *databasename) |
3087 | { |
3088 | char *dbname = text_to_cstring(databasename); |
3089 | |
3090 | return get_database_oid(dbname, false); |
3091 | } |
3092 | |
3093 | /* |
3094 | * convert_database_priv_string |
3095 | * Convert text string to AclMode value. |
3096 | */ |
3097 | static AclMode |
3098 | convert_database_priv_string(text *priv_type_text) |
3099 | { |
3100 | static const priv_map database_priv_map[] = { |
3101 | {"CREATE" , ACL_CREATE}, |
3102 | {"CREATE WITH GRANT OPTION" , ACL_GRANT_OPTION_FOR(ACL_CREATE)}, |
3103 | {"TEMPORARY" , ACL_CREATE_TEMP}, |
3104 | {"TEMPORARY WITH GRANT OPTION" , ACL_GRANT_OPTION_FOR(ACL_CREATE_TEMP)}, |
3105 | {"TEMP" , ACL_CREATE_TEMP}, |
3106 | {"TEMP WITH GRANT OPTION" , ACL_GRANT_OPTION_FOR(ACL_CREATE_TEMP)}, |
3107 | {"CONNECT" , ACL_CONNECT}, |
3108 | {"CONNECT WITH GRANT OPTION" , ACL_GRANT_OPTION_FOR(ACL_CONNECT)}, |
3109 | {NULL, 0} |
3110 | }; |
3111 | |
3112 | return convert_any_priv_string(priv_type_text, database_priv_map); |
3113 | |
3114 | } |
3115 | |
3116 | |
3117 | /* |
3118 | * has_foreign_data_wrapper_privilege variants |
3119 | * These are all named "has_foreign_data_wrapper_privilege" at the SQL level. |
3120 | * They take various combinations of foreign-data wrapper name, |
3121 | * fdw OID, user name, user OID, or implicit user = current_user. |
3122 | * |
3123 | * The result is a boolean value: true if user has the indicated |
3124 | * privilege, false if not. |
3125 | */ |
3126 | |
3127 | /* |
3128 | * has_foreign_data_wrapper_privilege_name_name |
3129 | * Check user privileges on a foreign-data wrapper given |
3130 | * name username, text fdwname, and text priv name. |
3131 | */ |
3132 | Datum |
3133 | has_foreign_data_wrapper_privilege_name_name(PG_FUNCTION_ARGS) |
3134 | { |
3135 | Name username = PG_GETARG_NAME(0); |
3136 | text *fdwname = PG_GETARG_TEXT_PP(1); |
3137 | text *priv_type_text = PG_GETARG_TEXT_PP(2); |
3138 | Oid roleid; |
3139 | Oid fdwid; |
3140 | AclMode mode; |
3141 | AclResult aclresult; |
3142 | |
3143 | roleid = get_role_oid_or_public(NameStr(*username)); |
3144 | fdwid = convert_foreign_data_wrapper_name(fdwname); |
3145 | mode = convert_foreign_data_wrapper_priv_string(priv_type_text); |
3146 | |
3147 | aclresult = pg_foreign_data_wrapper_aclcheck(fdwid, roleid, mode); |
3148 | |
3149 | PG_RETURN_BOOL(aclresult == ACLCHECK_OK); |
3150 | } |
3151 | |
3152 | /* |
3153 | * has_foreign_data_wrapper_privilege_name |
3154 | * Check user privileges on a foreign-data wrapper given |
3155 | * text fdwname and text priv name. |
3156 | * current_user is assumed |
3157 | */ |
3158 | Datum |
3159 | has_foreign_data_wrapper_privilege_name(PG_FUNCTION_ARGS) |
3160 | { |
3161 | text *fdwname = PG_GETARG_TEXT_PP(0); |
3162 | text *priv_type_text = PG_GETARG_TEXT_PP(1); |
3163 | Oid roleid; |
3164 | Oid fdwid; |
3165 | AclMode mode; |
3166 | AclResult aclresult; |
3167 | |
3168 | roleid = GetUserId(); |
3169 | fdwid = convert_foreign_data_wrapper_name(fdwname); |
3170 | mode = convert_foreign_data_wrapper_priv_string(priv_type_text); |
3171 | |
3172 | aclresult = pg_foreign_data_wrapper_aclcheck(fdwid, roleid, mode); |
3173 | |
3174 | PG_RETURN_BOOL(aclresult == ACLCHECK_OK); |
3175 | } |
3176 | |
3177 | /* |
3178 | * has_foreign_data_wrapper_privilege_name_id |
3179 | * Check user privileges on a foreign-data wrapper given |
3180 | * name usename, foreign-data wrapper oid, and text priv name. |
3181 | */ |
3182 | Datum |
3183 | has_foreign_data_wrapper_privilege_name_id(PG_FUNCTION_ARGS) |
3184 | { |
3185 | Name username = PG_GETARG_NAME(0); |
3186 | Oid fdwid = PG_GETARG_OID(1); |
3187 | text *priv_type_text = PG_GETARG_TEXT_PP(2); |
3188 | Oid roleid; |
3189 | AclMode mode; |
3190 | AclResult aclresult; |
3191 | |
3192 | roleid = get_role_oid_or_public(NameStr(*username)); |
3193 | mode = convert_foreign_data_wrapper_priv_string(priv_type_text); |
3194 | |
3195 | if (!SearchSysCacheExists1(FOREIGNDATAWRAPPEROID, ObjectIdGetDatum(fdwid))) |
3196 | PG_RETURN_NULL(); |
3197 | |
3198 | aclresult = pg_foreign_data_wrapper_aclcheck(fdwid, roleid, mode); |
3199 | |
3200 | PG_RETURN_BOOL(aclresult == ACLCHECK_OK); |
3201 | } |
3202 | |
3203 | /* |
3204 | * has_foreign_data_wrapper_privilege_id |
3205 | * Check user privileges on a foreign-data wrapper given |
3206 | * foreign-data wrapper oid, and text priv name. |
3207 | * current_user is assumed |
3208 | */ |
3209 | Datum |
3210 | has_foreign_data_wrapper_privilege_id(PG_FUNCTION_ARGS) |
3211 | { |
3212 | Oid fdwid = PG_GETARG_OID(0); |
3213 | text *priv_type_text = PG_GETARG_TEXT_PP(1); |
3214 | Oid roleid; |
3215 | AclMode mode; |
3216 | AclResult aclresult; |
3217 | |
3218 | roleid = GetUserId(); |
3219 | mode = convert_foreign_data_wrapper_priv_string(priv_type_text); |
3220 | |
3221 | if (!SearchSysCacheExists1(FOREIGNDATAWRAPPEROID, ObjectIdGetDatum(fdwid))) |
3222 | PG_RETURN_NULL(); |
3223 | |
3224 | aclresult = pg_foreign_data_wrapper_aclcheck(fdwid, roleid, mode); |
3225 | |
3226 | PG_RETURN_BOOL(aclresult == ACLCHECK_OK); |
3227 | } |
3228 | |
3229 | /* |
3230 | * has_foreign_data_wrapper_privilege_id_name |
3231 | * Check user privileges on a foreign-data wrapper given |
3232 | * roleid, text fdwname, and text priv name. |
3233 | */ |
3234 | Datum |
3235 | has_foreign_data_wrapper_privilege_id_name(PG_FUNCTION_ARGS) |
3236 | { |
3237 | Oid roleid = PG_GETARG_OID(0); |
3238 | text *fdwname = PG_GETARG_TEXT_PP(1); |
3239 | text *priv_type_text = PG_GETARG_TEXT_PP(2); |
3240 | Oid fdwid; |
3241 | AclMode mode; |
3242 | AclResult aclresult; |
3243 | |
3244 | fdwid = convert_foreign_data_wrapper_name(fdwname); |
3245 | mode = convert_foreign_data_wrapper_priv_string(priv_type_text); |
3246 | |
3247 | aclresult = pg_foreign_data_wrapper_aclcheck(fdwid, roleid, mode); |
3248 | |
3249 | PG_RETURN_BOOL(aclresult == ACLCHECK_OK); |
3250 | } |
3251 | |
3252 | /* |
3253 | * has_foreign_data_wrapper_privilege_id_id |
3254 | * Check user privileges on a foreign-data wrapper given |
3255 | * roleid, fdw oid, and text priv name. |
3256 | */ |
3257 | Datum |
3258 | has_foreign_data_wrapper_privilege_id_id(PG_FUNCTION_ARGS) |
3259 | { |
3260 | Oid roleid = PG_GETARG_OID(0); |
3261 | Oid fdwid = PG_GETARG_OID(1); |
3262 | text *priv_type_text = PG_GETARG_TEXT_PP(2); |
3263 | AclMode mode; |
3264 | AclResult aclresult; |
3265 | |
3266 | mode = convert_foreign_data_wrapper_priv_string(priv_type_text); |
3267 | |
3268 | if (!SearchSysCacheExists1(FOREIGNDATAWRAPPEROID, ObjectIdGetDatum(fdwid))) |
3269 | PG_RETURN_NULL(); |
3270 | |
3271 | aclresult = pg_foreign_data_wrapper_aclcheck(fdwid, roleid, mode); |
3272 | |
3273 | PG_RETURN_BOOL(aclresult == ACLCHECK_OK); |
3274 | } |
3275 | |
3276 | /* |
3277 | * Support routines for has_foreign_data_wrapper_privilege family. |
3278 | */ |
3279 | |
3280 | /* |
3281 | * Given a FDW name expressed as a string, look it up and return Oid |
3282 | */ |
3283 | static Oid |
3284 | convert_foreign_data_wrapper_name(text *fdwname) |
3285 | { |
3286 | char *fdwstr = text_to_cstring(fdwname); |
3287 | |
3288 | return get_foreign_data_wrapper_oid(fdwstr, false); |
3289 | } |
3290 | |
3291 | /* |
3292 | * convert_foreign_data_wrapper_priv_string |
3293 | * Convert text string to AclMode value. |
3294 | */ |
3295 | static AclMode |
3296 | convert_foreign_data_wrapper_priv_string(text *priv_type_text) |
3297 | { |
3298 | static const priv_map foreign_data_wrapper_priv_map[] = { |
3299 | {"USAGE" , ACL_USAGE}, |
3300 | {"USAGE WITH GRANT OPTION" , ACL_GRANT_OPTION_FOR(ACL_USAGE)}, |
3301 | {NULL, 0} |
3302 | }; |
3303 | |
3304 | return convert_any_priv_string(priv_type_text, foreign_data_wrapper_priv_map); |
3305 | } |
3306 | |
3307 | |
3308 | /* |
3309 | * has_function_privilege variants |
3310 | * These are all named "has_function_privilege" at the SQL level. |
3311 | * They take various combinations of function name, function OID, |
3312 | * user name, user OID, or implicit user = current_user. |
3313 | * |
3314 | * The result is a boolean value: true if user has the indicated |
3315 | * privilege, false if not, or NULL if object doesn't exist. |
3316 | */ |
3317 | |
3318 | /* |
3319 | * has_function_privilege_name_name |
3320 | * Check user privileges on a function given |
3321 | * name username, text functionname, and text priv name. |
3322 | */ |
3323 | Datum |
3324 | has_function_privilege_name_name(PG_FUNCTION_ARGS) |
3325 | { |
3326 | Name username = PG_GETARG_NAME(0); |
3327 | text *functionname = PG_GETARG_TEXT_PP(1); |
3328 | text *priv_type_text = PG_GETARG_TEXT_PP(2); |
3329 | Oid roleid; |
3330 | Oid functionoid; |
3331 | AclMode mode; |
3332 | AclResult aclresult; |
3333 | |
3334 | roleid = get_role_oid_or_public(NameStr(*username)); |
3335 | functionoid = convert_function_name(functionname); |
3336 | mode = convert_function_priv_string(priv_type_text); |
3337 | |
3338 | aclresult = pg_proc_aclcheck(functionoid, roleid, mode); |
3339 | |
3340 | PG_RETURN_BOOL(aclresult == ACLCHECK_OK); |
3341 | } |
3342 | |
3343 | /* |
3344 | * has_function_privilege_name |
3345 | * Check user privileges on a function given |
3346 | * text functionname and text priv name. |
3347 | * current_user is assumed |
3348 | */ |
3349 | Datum |
3350 | has_function_privilege_name(PG_FUNCTION_ARGS) |
3351 | { |
3352 | text *functionname = PG_GETARG_TEXT_PP(0); |
3353 | text *priv_type_text = PG_GETARG_TEXT_PP(1); |
3354 | Oid roleid; |
3355 | Oid functionoid; |
3356 | AclMode mode; |
3357 | AclResult aclresult; |
3358 | |
3359 | roleid = GetUserId(); |
3360 | functionoid = convert_function_name(functionname); |
3361 | mode = convert_function_priv_string(priv_type_text); |
3362 | |
3363 | aclresult = pg_proc_aclcheck(functionoid, roleid, mode); |
3364 | |
3365 | PG_RETURN_BOOL(aclresult == ACLCHECK_OK); |
3366 | } |
3367 | |
3368 | /* |
3369 | * has_function_privilege_name_id |
3370 | * Check user privileges on a function given |
3371 | * name usename, function oid, and text priv name. |
3372 | */ |
3373 | Datum |
3374 | has_function_privilege_name_id(PG_FUNCTION_ARGS) |
3375 | { |
3376 | Name username = PG_GETARG_NAME(0); |
3377 | Oid functionoid = PG_GETARG_OID(1); |
3378 | text *priv_type_text = PG_GETARG_TEXT_PP(2); |
3379 | Oid roleid; |
3380 | AclMode mode; |
3381 | AclResult aclresult; |
3382 | |
3383 | roleid = get_role_oid_or_public(NameStr(*username)); |
3384 | mode = convert_function_priv_string(priv_type_text); |
3385 | |
3386 | if (!SearchSysCacheExists1(PROCOID, ObjectIdGetDatum(functionoid))) |
3387 | PG_RETURN_NULL(); |
3388 | |
3389 | aclresult = pg_proc_aclcheck(functionoid, roleid, mode); |
3390 | |
3391 | PG_RETURN_BOOL(aclresult == ACLCHECK_OK); |
3392 | } |
3393 | |
3394 | /* |
3395 | * has_function_privilege_id |
3396 | * Check user privileges on a function given |
3397 | * function oid, and text priv name. |
3398 | * current_user is assumed |
3399 | */ |
3400 | Datum |
3401 | has_function_privilege_id(PG_FUNCTION_ARGS) |
3402 | { |
3403 | Oid functionoid = PG_GETARG_OID(0); |
3404 | text *priv_type_text = PG_GETARG_TEXT_PP(1); |
3405 | Oid roleid; |
3406 | AclMode mode; |
3407 | AclResult aclresult; |
3408 | |
3409 | roleid = GetUserId(); |
3410 | mode = convert_function_priv_string(priv_type_text); |
3411 | |
3412 | if (!SearchSysCacheExists1(PROCOID, ObjectIdGetDatum(functionoid))) |
3413 | PG_RETURN_NULL(); |
3414 | |
3415 | aclresult = pg_proc_aclcheck(functionoid, roleid, mode); |
3416 | |
3417 | PG_RETURN_BOOL(aclresult == ACLCHECK_OK); |
3418 | } |
3419 | |
3420 | /* |
3421 | * has_function_privilege_id_name |
3422 | * Check user privileges on a function given |
3423 | * roleid, text functionname, and text priv name. |
3424 | */ |
3425 | Datum |
3426 | has_function_privilege_id_name(PG_FUNCTION_ARGS) |
3427 | { |
3428 | Oid roleid = PG_GETARG_OID(0); |
3429 | text *functionname = PG_GETARG_TEXT_PP(1); |
3430 | text *priv_type_text = PG_GETARG_TEXT_PP(2); |
3431 | Oid functionoid; |
3432 | AclMode mode; |
3433 | AclResult aclresult; |
3434 | |
3435 | functionoid = convert_function_name(functionname); |
3436 | mode = convert_function_priv_string(priv_type_text); |
3437 | |
3438 | aclresult = pg_proc_aclcheck(functionoid, roleid, mode); |
3439 | |
3440 | PG_RETURN_BOOL(aclresult == ACLCHECK_OK); |
3441 | } |
3442 | |
3443 | /* |
3444 | * has_function_privilege_id_id |
3445 | * Check user privileges on a function given |
3446 | * roleid, function oid, and text priv name. |
3447 | */ |
3448 | Datum |
3449 | has_function_privilege_id_id(PG_FUNCTION_ARGS) |
3450 | { |
3451 | Oid roleid = PG_GETARG_OID(0); |
3452 | Oid functionoid = PG_GETARG_OID(1); |
3453 | text *priv_type_text = PG_GETARG_TEXT_PP(2); |
3454 | AclMode mode; |
3455 | AclResult aclresult; |
3456 | |
3457 | mode = convert_function_priv_string(priv_type_text); |
3458 | |
3459 | if (!SearchSysCacheExists1(PROCOID, ObjectIdGetDatum(functionoid))) |
3460 | PG_RETURN_NULL(); |
3461 | |
3462 | aclresult = pg_proc_aclcheck(functionoid, roleid, mode); |
3463 | |
3464 | PG_RETURN_BOOL(aclresult == ACLCHECK_OK); |
3465 | } |
3466 | |
3467 | /* |
3468 | * Support routines for has_function_privilege family. |
3469 | */ |
3470 | |
3471 | /* |
3472 | * Given a function name expressed as a string, look it up and return Oid |
3473 | */ |
3474 | static Oid |
3475 | convert_function_name(text *functionname) |
3476 | { |
3477 | char *funcname = text_to_cstring(functionname); |
3478 | Oid oid; |
3479 | |
3480 | oid = DatumGetObjectId(DirectFunctionCall1(regprocedurein, |
3481 | CStringGetDatum(funcname))); |
3482 | |
3483 | if (!OidIsValid(oid)) |
3484 | ereport(ERROR, |
3485 | (errcode(ERRCODE_UNDEFINED_FUNCTION), |
3486 | errmsg("function \"%s\" does not exist" , funcname))); |
3487 | |
3488 | return oid; |
3489 | } |
3490 | |
3491 | /* |
3492 | * convert_function_priv_string |
3493 | * Convert text string to AclMode value. |
3494 | */ |
3495 | static AclMode |
3496 | convert_function_priv_string(text *priv_type_text) |
3497 | { |
3498 | static const priv_map function_priv_map[] = { |
3499 | {"EXECUTE" , ACL_EXECUTE}, |
3500 | {"EXECUTE WITH GRANT OPTION" , ACL_GRANT_OPTION_FOR(ACL_EXECUTE)}, |
3501 | {NULL, 0} |
3502 | }; |
3503 | |
3504 | return convert_any_priv_string(priv_type_text, function_priv_map); |
3505 | } |
3506 | |
3507 | |
3508 | /* |
3509 | * has_language_privilege variants |
3510 | * These are all named "has_language_privilege" at the SQL level. |
3511 | * They take various combinations of language name, language OID, |
3512 | * user name, user OID, or implicit user = current_user. |
3513 | * |
3514 | * The result is a boolean value: true if user has the indicated |
3515 | * privilege, false if not, or NULL if object doesn't exist. |
3516 | */ |
3517 | |
3518 | /* |
3519 | * has_language_privilege_name_name |
3520 | * Check user privileges on a language given |
3521 | * name username, text languagename, and text priv name. |
3522 | */ |
3523 | Datum |
3524 | has_language_privilege_name_name(PG_FUNCTION_ARGS) |
3525 | { |
3526 | Name username = PG_GETARG_NAME(0); |
3527 | text *languagename = PG_GETARG_TEXT_PP(1); |
3528 | text *priv_type_text = PG_GETARG_TEXT_PP(2); |
3529 | Oid roleid; |
3530 | Oid languageoid; |
3531 | AclMode mode; |
3532 | AclResult aclresult; |
3533 | |
3534 | roleid = get_role_oid_or_public(NameStr(*username)); |
3535 | languageoid = convert_language_name(languagename); |
3536 | mode = convert_language_priv_string(priv_type_text); |
3537 | |
3538 | aclresult = pg_language_aclcheck(languageoid, roleid, mode); |
3539 | |
3540 | PG_RETURN_BOOL(aclresult == ACLCHECK_OK); |
3541 | } |
3542 | |
3543 | /* |
3544 | * has_language_privilege_name |
3545 | * Check user privileges on a language given |
3546 | * text languagename and text priv name. |
3547 | * current_user is assumed |
3548 | */ |
3549 | Datum |
3550 | has_language_privilege_name(PG_FUNCTION_ARGS) |
3551 | { |
3552 | text *languagename = PG_GETARG_TEXT_PP(0); |
3553 | text *priv_type_text = PG_GETARG_TEXT_PP(1); |
3554 | Oid roleid; |
3555 | Oid languageoid; |
3556 | AclMode mode; |
3557 | AclResult aclresult; |
3558 | |
3559 | roleid = GetUserId(); |
3560 | languageoid = convert_language_name(languagename); |
3561 | mode = convert_language_priv_string(priv_type_text); |
3562 | |
3563 | aclresult = pg_language_aclcheck(languageoid, roleid, mode); |
3564 | |
3565 | PG_RETURN_BOOL(aclresult == ACLCHECK_OK); |
3566 | } |
3567 | |
3568 | /* |
3569 | * has_language_privilege_name_id |
3570 | * Check user privileges on a language given |
3571 | * name usename, language oid, and text priv name. |
3572 | */ |
3573 | Datum |
3574 | has_language_privilege_name_id(PG_FUNCTION_ARGS) |
3575 | { |
3576 | Name username = PG_GETARG_NAME(0); |
3577 | Oid languageoid = PG_GETARG_OID(1); |
3578 | text *priv_type_text = PG_GETARG_TEXT_PP(2); |
3579 | Oid roleid; |
3580 | AclMode mode; |
3581 | AclResult aclresult; |
3582 | |
3583 | roleid = get_role_oid_or_public(NameStr(*username)); |
3584 | mode = convert_language_priv_string(priv_type_text); |
3585 | |
3586 | if (!SearchSysCacheExists1(LANGOID, ObjectIdGetDatum(languageoid))) |
3587 | PG_RETURN_NULL(); |
3588 | |
3589 | aclresult = pg_language_aclcheck(languageoid, roleid, mode); |
3590 | |
3591 | PG_RETURN_BOOL(aclresult == ACLCHECK_OK); |
3592 | } |
3593 | |
3594 | /* |
3595 | * has_language_privilege_id |
3596 | * Check user privileges on a language given |
3597 | * language oid, and text priv name. |
3598 | * current_user is assumed |
3599 | */ |
3600 | Datum |
3601 | has_language_privilege_id(PG_FUNCTION_ARGS) |
3602 | { |
3603 | Oid languageoid = PG_GETARG_OID(0); |
3604 | text *priv_type_text = PG_GETARG_TEXT_PP(1); |
3605 | Oid roleid; |
3606 | AclMode mode; |
3607 | AclResult aclresult; |
3608 | |
3609 | roleid = GetUserId(); |
3610 | mode = convert_language_priv_string(priv_type_text); |
3611 | |
3612 | if (!SearchSysCacheExists1(LANGOID, ObjectIdGetDatum(languageoid))) |
3613 | PG_RETURN_NULL(); |
3614 | |
3615 | aclresult = pg_language_aclcheck(languageoid, roleid, mode); |
3616 | |
3617 | PG_RETURN_BOOL(aclresult == ACLCHECK_OK); |
3618 | } |
3619 | |
3620 | /* |
3621 | * has_language_privilege_id_name |
3622 | * Check user privileges on a language given |
3623 | * roleid, text languagename, and text priv name. |
3624 | */ |
3625 | Datum |
3626 | has_language_privilege_id_name(PG_FUNCTION_ARGS) |
3627 | { |
3628 | Oid roleid = PG_GETARG_OID(0); |
3629 | text *languagename = PG_GETARG_TEXT_PP(1); |
3630 | text *priv_type_text = PG_GETARG_TEXT_PP(2); |
3631 | Oid languageoid; |
3632 | AclMode mode; |
3633 | AclResult aclresult; |
3634 | |
3635 | languageoid = convert_language_name(languagename); |
3636 | mode = convert_language_priv_string(priv_type_text); |
3637 | |
3638 | aclresult = pg_language_aclcheck(languageoid, roleid, mode); |
3639 | |
3640 | PG_RETURN_BOOL(aclresult == ACLCHECK_OK); |
3641 | } |
3642 | |
3643 | /* |
3644 | * has_language_privilege_id_id |
3645 | * Check user privileges on a language given |
3646 | * roleid, language oid, and text priv name. |
3647 | */ |
3648 | Datum |
3649 | has_language_privilege_id_id(PG_FUNCTION_ARGS) |
3650 | { |
3651 | Oid roleid = PG_GETARG_OID(0); |
3652 | Oid languageoid = PG_GETARG_OID(1); |
3653 | text *priv_type_text = PG_GETARG_TEXT_PP(2); |
3654 | AclMode mode; |
3655 | AclResult aclresult; |
3656 | |
3657 | mode = convert_language_priv_string(priv_type_text); |
3658 | |
3659 | if (!SearchSysCacheExists1(LANGOID, ObjectIdGetDatum(languageoid))) |
3660 | PG_RETURN_NULL(); |
3661 | |
3662 | aclresult = pg_language_aclcheck(languageoid, roleid, mode); |
3663 | |
3664 | PG_RETURN_BOOL(aclresult == ACLCHECK_OK); |
3665 | } |
3666 | |
3667 | /* |
3668 | * Support routines for has_language_privilege family. |
3669 | */ |
3670 | |
3671 | /* |
3672 | * Given a language name expressed as a string, look it up and return Oid |
3673 | */ |
3674 | static Oid |
3675 | convert_language_name(text *languagename) |
3676 | { |
3677 | char *langname = text_to_cstring(languagename); |
3678 | |
3679 | return get_language_oid(langname, false); |
3680 | } |
3681 | |
3682 | /* |
3683 | * convert_language_priv_string |
3684 | * Convert text string to AclMode value. |
3685 | */ |
3686 | static AclMode |
3687 | convert_language_priv_string(text *priv_type_text) |
3688 | { |
3689 | static const priv_map language_priv_map[] = { |
3690 | {"USAGE" , ACL_USAGE}, |
3691 | {"USAGE WITH GRANT OPTION" , ACL_GRANT_OPTION_FOR(ACL_USAGE)}, |
3692 | {NULL, 0} |
3693 | }; |
3694 | |
3695 | return convert_any_priv_string(priv_type_text, language_priv_map); |
3696 | } |
3697 | |
3698 | |
3699 | /* |
3700 | * has_schema_privilege variants |
3701 | * These are all named "has_schema_privilege" at the SQL level. |
3702 | * They take various combinations of schema name, schema OID, |
3703 | * user name, user OID, or implicit user = current_user. |
3704 | * |
3705 | * The result is a boolean value: true if user has the indicated |
3706 | * privilege, false if not, or NULL if object doesn't exist. |
3707 | */ |
3708 | |
3709 | /* |
3710 | * has_schema_privilege_name_name |
3711 | * Check user privileges on a schema given |
3712 | * name username, text schemaname, and text priv name. |
3713 | */ |
3714 | Datum |
3715 | has_schema_privilege_name_name(PG_FUNCTION_ARGS) |
3716 | { |
3717 | Name username = PG_GETARG_NAME(0); |
3718 | text *schemaname = PG_GETARG_TEXT_PP(1); |
3719 | text *priv_type_text = PG_GETARG_TEXT_PP(2); |
3720 | Oid roleid; |
3721 | Oid schemaoid; |
3722 | AclMode mode; |
3723 | AclResult aclresult; |
3724 | |
3725 | roleid = get_role_oid_or_public(NameStr(*username)); |
3726 | schemaoid = convert_schema_name(schemaname); |
3727 | mode = convert_schema_priv_string(priv_type_text); |
3728 | |
3729 | aclresult = pg_namespace_aclcheck(schemaoid, roleid, mode); |
3730 | |
3731 | PG_RETURN_BOOL(aclresult == ACLCHECK_OK); |
3732 | } |
3733 | |
3734 | /* |
3735 | * has_schema_privilege_name |
3736 | * Check user privileges on a schema given |
3737 | * text schemaname and text priv name. |
3738 | * current_user is assumed |
3739 | */ |
3740 | Datum |
3741 | has_schema_privilege_name(PG_FUNCTION_ARGS) |
3742 | { |
3743 | text *schemaname = PG_GETARG_TEXT_PP(0); |
3744 | text *priv_type_text = PG_GETARG_TEXT_PP(1); |
3745 | Oid roleid; |
3746 | Oid schemaoid; |
3747 | AclMode mode; |
3748 | AclResult aclresult; |
3749 | |
3750 | roleid = GetUserId(); |
3751 | schemaoid = convert_schema_name(schemaname); |
3752 | mode = convert_schema_priv_string(priv_type_text); |
3753 | |
3754 | aclresult = pg_namespace_aclcheck(schemaoid, roleid, mode); |
3755 | |
3756 | PG_RETURN_BOOL(aclresult == ACLCHECK_OK); |
3757 | } |
3758 | |
3759 | /* |
3760 | * has_schema_privilege_name_id |
3761 | * Check user privileges on a schema given |
3762 | * name usename, schema oid, and text priv name. |
3763 | */ |
3764 | Datum |
3765 | has_schema_privilege_name_id(PG_FUNCTION_ARGS) |
3766 | { |
3767 | Name username = PG_GETARG_NAME(0); |
3768 | Oid schemaoid = PG_GETARG_OID(1); |
3769 | text *priv_type_text = PG_GETARG_TEXT_PP(2); |
3770 | Oid roleid; |
3771 | AclMode mode; |
3772 | AclResult aclresult; |
3773 | |
3774 | roleid = get_role_oid_or_public(NameStr(*username)); |
3775 | mode = convert_schema_priv_string(priv_type_text); |
3776 | |
3777 | if (!SearchSysCacheExists1(NAMESPACEOID, ObjectIdGetDatum(schemaoid))) |
3778 | PG_RETURN_NULL(); |
3779 | |
3780 | aclresult = pg_namespace_aclcheck(schemaoid, roleid, mode); |
3781 | |
3782 | PG_RETURN_BOOL(aclresult == ACLCHECK_OK); |
3783 | } |
3784 | |
3785 | /* |
3786 | * has_schema_privilege_id |
3787 | * Check user privileges on a schema given |
3788 | * schema oid, and text priv name. |
3789 | * current_user is assumed |
3790 | */ |
3791 | Datum |
3792 | has_schema_privilege_id(PG_FUNCTION_ARGS) |
3793 | { |
3794 | Oid schemaoid = PG_GETARG_OID(0); |
3795 | text *priv_type_text = PG_GETARG_TEXT_PP(1); |
3796 | Oid roleid; |
3797 | AclMode mode; |
3798 | AclResult aclresult; |
3799 | |
3800 | roleid = GetUserId(); |
3801 | mode = convert_schema_priv_string(priv_type_text); |
3802 | |
3803 | if (!SearchSysCacheExists1(NAMESPACEOID, ObjectIdGetDatum(schemaoid))) |
3804 | PG_RETURN_NULL(); |
3805 | |
3806 | aclresult = pg_namespace_aclcheck(schemaoid, roleid, mode); |
3807 | |
3808 | PG_RETURN_BOOL(aclresult == ACLCHECK_OK); |
3809 | } |
3810 | |
3811 | /* |
3812 | * has_schema_privilege_id_name |
3813 | * Check user privileges on a schema given |
3814 | * roleid, text schemaname, and text priv name. |
3815 | */ |
3816 | Datum |
3817 | has_schema_privilege_id_name(PG_FUNCTION_ARGS) |
3818 | { |
3819 | Oid roleid = PG_GETARG_OID(0); |
3820 | text *schemaname = PG_GETARG_TEXT_PP(1); |
3821 | text *priv_type_text = PG_GETARG_TEXT_PP(2); |
3822 | Oid schemaoid; |
3823 | AclMode mode; |
3824 | AclResult aclresult; |
3825 | |
3826 | schemaoid = convert_schema_name(schemaname); |
3827 | mode = convert_schema_priv_string(priv_type_text); |
3828 | |
3829 | aclresult = pg_namespace_aclcheck(schemaoid, roleid, mode); |
3830 | |
3831 | PG_RETURN_BOOL(aclresult == ACLCHECK_OK); |
3832 | } |
3833 | |
3834 | /* |
3835 | * has_schema_privilege_id_id |
3836 | * Check user privileges on a schema given |
3837 | * roleid, schema oid, and text priv name. |
3838 | */ |
3839 | Datum |
3840 | has_schema_privilege_id_id(PG_FUNCTION_ARGS) |
3841 | { |
3842 | Oid roleid = PG_GETARG_OID(0); |
3843 | Oid schemaoid = PG_GETARG_OID(1); |
3844 | text *priv_type_text = PG_GETARG_TEXT_PP(2); |
3845 | AclMode mode; |
3846 | AclResult aclresult; |
3847 | |
3848 | mode = convert_schema_priv_string(priv_type_text); |
3849 | |
3850 | if (!SearchSysCacheExists1(NAMESPACEOID, ObjectIdGetDatum(schemaoid))) |
3851 | PG_RETURN_NULL(); |
3852 | |
3853 | aclresult = pg_namespace_aclcheck(schemaoid, roleid, mode); |
3854 | |
3855 | PG_RETURN_BOOL(aclresult == ACLCHECK_OK); |
3856 | } |
3857 | |
3858 | /* |
3859 | * Support routines for has_schema_privilege family. |
3860 | */ |
3861 | |
3862 | /* |
3863 | * Given a schema name expressed as a string, look it up and return Oid |
3864 | */ |
3865 | static Oid |
3866 | convert_schema_name(text *schemaname) |
3867 | { |
3868 | char *nspname = text_to_cstring(schemaname); |
3869 | |
3870 | return get_namespace_oid(nspname, false); |
3871 | } |
3872 | |
3873 | /* |
3874 | * convert_schema_priv_string |
3875 | * Convert text string to AclMode value. |
3876 | */ |
3877 | static AclMode |
3878 | convert_schema_priv_string(text *priv_type_text) |
3879 | { |
3880 | static const priv_map schema_priv_map[] = { |
3881 | {"CREATE" , ACL_CREATE}, |
3882 | {"CREATE WITH GRANT OPTION" , ACL_GRANT_OPTION_FOR(ACL_CREATE)}, |
3883 | {"USAGE" , ACL_USAGE}, |
3884 | {"USAGE WITH GRANT OPTION" , ACL_GRANT_OPTION_FOR(ACL_USAGE)}, |
3885 | {NULL, 0} |
3886 | }; |
3887 | |
3888 | return convert_any_priv_string(priv_type_text, schema_priv_map); |
3889 | } |
3890 | |
3891 | |
3892 | /* |
3893 | * has_server_privilege variants |
3894 | * These are all named "has_server_privilege" at the SQL level. |
3895 | * They take various combinations of foreign server name, |
3896 | * server OID, user name, user OID, or implicit user = current_user. |
3897 | * |
3898 | * The result is a boolean value: true if user has the indicated |
3899 | * privilege, false if not. |
3900 | */ |
3901 | |
3902 | /* |
3903 | * has_server_privilege_name_name |
3904 | * Check user privileges on a foreign server given |
3905 | * name username, text servername, and text priv name. |
3906 | */ |
3907 | Datum |
3908 | has_server_privilege_name_name(PG_FUNCTION_ARGS) |
3909 | { |
3910 | Name username = PG_GETARG_NAME(0); |
3911 | text *servername = PG_GETARG_TEXT_PP(1); |
3912 | text *priv_type_text = PG_GETARG_TEXT_PP(2); |
3913 | Oid roleid; |
3914 | Oid serverid; |
3915 | AclMode mode; |
3916 | AclResult aclresult; |
3917 | |
3918 | roleid = get_role_oid_or_public(NameStr(*username)); |
3919 | serverid = convert_server_name(servername); |
3920 | mode = convert_server_priv_string(priv_type_text); |
3921 | |
3922 | aclresult = pg_foreign_server_aclcheck(serverid, roleid, mode); |
3923 | |
3924 | PG_RETURN_BOOL(aclresult == ACLCHECK_OK); |
3925 | } |
3926 | |
3927 | /* |
3928 | * has_server_privilege_name |
3929 | * Check user privileges on a foreign server given |
3930 | * text servername and text priv name. |
3931 | * current_user is assumed |
3932 | */ |
3933 | Datum |
3934 | has_server_privilege_name(PG_FUNCTION_ARGS) |
3935 | { |
3936 | text *servername = PG_GETARG_TEXT_PP(0); |
3937 | text *priv_type_text = PG_GETARG_TEXT_PP(1); |
3938 | Oid roleid; |
3939 | Oid serverid; |
3940 | AclMode mode; |
3941 | AclResult aclresult; |
3942 | |
3943 | roleid = GetUserId(); |
3944 | serverid = convert_server_name(servername); |
3945 | mode = convert_server_priv_string(priv_type_text); |
3946 | |
3947 | aclresult = pg_foreign_server_aclcheck(serverid, roleid, mode); |
3948 | |
3949 | PG_RETURN_BOOL(aclresult == ACLCHECK_OK); |
3950 | } |
3951 | |
3952 | /* |
3953 | * has_server_privilege_name_id |
3954 | * Check user privileges on a foreign server given |
3955 | * name usename, foreign server oid, and text priv name. |
3956 | */ |
3957 | Datum |
3958 | has_server_privilege_name_id(PG_FUNCTION_ARGS) |
3959 | { |
3960 | Name username = PG_GETARG_NAME(0); |
3961 | Oid serverid = PG_GETARG_OID(1); |
3962 | text *priv_type_text = PG_GETARG_TEXT_PP(2); |
3963 | Oid roleid; |
3964 | AclMode mode; |
3965 | AclResult aclresult; |
3966 | |
3967 | roleid = get_role_oid_or_public(NameStr(*username)); |
3968 | mode = convert_server_priv_string(priv_type_text); |
3969 | |
3970 | if (!SearchSysCacheExists1(FOREIGNSERVEROID, ObjectIdGetDatum(serverid))) |
3971 | PG_RETURN_NULL(); |
3972 | |
3973 | aclresult = pg_foreign_server_aclcheck(serverid, roleid, mode); |
3974 | |
3975 | PG_RETURN_BOOL(aclresult == ACLCHECK_OK); |
3976 | } |
3977 | |
3978 | /* |
3979 | * has_server_privilege_id |
3980 | * Check user privileges on a foreign server given |
3981 | * server oid, and text priv name. |
3982 | * current_user is assumed |
3983 | */ |
3984 | Datum |
3985 | has_server_privilege_id(PG_FUNCTION_ARGS) |
3986 | { |
3987 | Oid serverid = PG_GETARG_OID(0); |
3988 | text *priv_type_text = PG_GETARG_TEXT_PP(1); |
3989 | Oid roleid; |
3990 | AclMode mode; |
3991 | AclResult aclresult; |
3992 | |
3993 | roleid = GetUserId(); |
3994 | mode = convert_server_priv_string(priv_type_text); |
3995 | |
3996 | if (!SearchSysCacheExists1(FOREIGNSERVEROID, ObjectIdGetDatum(serverid))) |
3997 | PG_RETURN_NULL(); |
3998 | |
3999 | aclresult = pg_foreign_server_aclcheck(serverid, roleid, mode); |
4000 | |
4001 | PG_RETURN_BOOL(aclresult == ACLCHECK_OK); |
4002 | } |
4003 | |
4004 | /* |
4005 | * has_server_privilege_id_name |
4006 | * Check user privileges on a foreign server given |
4007 | * roleid, text servername, and text priv name. |
4008 | */ |
4009 | Datum |
4010 | has_server_privilege_id_name(PG_FUNCTION_ARGS) |
4011 | { |
4012 | Oid roleid = PG_GETARG_OID(0); |
4013 | text *servername = PG_GETARG_TEXT_PP(1); |
4014 | text *priv_type_text = PG_GETARG_TEXT_PP(2); |
4015 | Oid serverid; |
4016 | AclMode mode; |
4017 | AclResult aclresult; |
4018 | |
4019 | serverid = convert_server_name(servername); |
4020 | mode = convert_server_priv_string(priv_type_text); |
4021 | |
4022 | aclresult = pg_foreign_server_aclcheck(serverid, roleid, mode); |
4023 | |
4024 | PG_RETURN_BOOL(aclresult == ACLCHECK_OK); |
4025 | } |
4026 | |
4027 | /* |
4028 | * has_server_privilege_id_id |
4029 | * Check user privileges on a foreign server given |
4030 | * roleid, server oid, and text priv name. |
4031 | */ |
4032 | Datum |
4033 | has_server_privilege_id_id(PG_FUNCTION_ARGS) |
4034 | { |
4035 | Oid roleid = PG_GETARG_OID(0); |
4036 | Oid serverid = PG_GETARG_OID(1); |
4037 | text *priv_type_text = PG_GETARG_TEXT_PP(2); |
4038 | AclMode mode; |
4039 | AclResult aclresult; |
4040 | |
4041 | mode = convert_server_priv_string(priv_type_text); |
4042 | |
4043 | if (!SearchSysCacheExists1(FOREIGNSERVEROID, ObjectIdGetDatum(serverid))) |
4044 | PG_RETURN_NULL(); |
4045 | |
4046 | aclresult = pg_foreign_server_aclcheck(serverid, roleid, mode); |
4047 | |
4048 | PG_RETURN_BOOL(aclresult == ACLCHECK_OK); |
4049 | } |
4050 | |
4051 | /* |
4052 | * Support routines for has_server_privilege family. |
4053 | */ |
4054 | |
4055 | /* |
4056 | * Given a server name expressed as a string, look it up and return Oid |
4057 | */ |
4058 | static Oid |
4059 | convert_server_name(text *servername) |
4060 | { |
4061 | char *serverstr = text_to_cstring(servername); |
4062 | |
4063 | return get_foreign_server_oid(serverstr, false); |
4064 | } |
4065 | |
4066 | /* |
4067 | * convert_server_priv_string |
4068 | * Convert text string to AclMode value. |
4069 | */ |
4070 | static AclMode |
4071 | convert_server_priv_string(text *priv_type_text) |
4072 | { |
4073 | static const priv_map server_priv_map[] = { |
4074 | {"USAGE" , ACL_USAGE}, |
4075 | {"USAGE WITH GRANT OPTION" , ACL_GRANT_OPTION_FOR(ACL_USAGE)}, |
4076 | {NULL, 0} |
4077 | }; |
4078 | |
4079 | return convert_any_priv_string(priv_type_text, server_priv_map); |
4080 | } |
4081 | |
4082 | |
4083 | /* |
4084 | * has_tablespace_privilege variants |
4085 | * These are all named "has_tablespace_privilege" at the SQL level. |
4086 | * They take various combinations of tablespace name, tablespace OID, |
4087 | * user name, user OID, or implicit user = current_user. |
4088 | * |
4089 | * The result is a boolean value: true if user has the indicated |
4090 | * privilege, false if not. |
4091 | */ |
4092 | |
4093 | /* |
4094 | * has_tablespace_privilege_name_name |
4095 | * Check user privileges on a tablespace given |
4096 | * name username, text tablespacename, and text priv name. |
4097 | */ |
4098 | Datum |
4099 | has_tablespace_privilege_name_name(PG_FUNCTION_ARGS) |
4100 | { |
4101 | Name username = PG_GETARG_NAME(0); |
4102 | text *tablespacename = PG_GETARG_TEXT_PP(1); |
4103 | text *priv_type_text = PG_GETARG_TEXT_PP(2); |
4104 | Oid roleid; |
4105 | Oid tablespaceoid; |
4106 | AclMode mode; |
4107 | AclResult aclresult; |
4108 | |
4109 | roleid = get_role_oid_or_public(NameStr(*username)); |
4110 | tablespaceoid = convert_tablespace_name(tablespacename); |
4111 | mode = convert_tablespace_priv_string(priv_type_text); |
4112 | |
4113 | aclresult = pg_tablespace_aclcheck(tablespaceoid, roleid, mode); |
4114 | |
4115 | PG_RETURN_BOOL(aclresult == ACLCHECK_OK); |
4116 | } |
4117 | |
4118 | /* |
4119 | * has_tablespace_privilege_name |
4120 | * Check user privileges on a tablespace given |
4121 | * text tablespacename and text priv name. |
4122 | * current_user is assumed |
4123 | */ |
4124 | Datum |
4125 | has_tablespace_privilege_name(PG_FUNCTION_ARGS) |
4126 | { |
4127 | text *tablespacename = PG_GETARG_TEXT_PP(0); |
4128 | text *priv_type_text = PG_GETARG_TEXT_PP(1); |
4129 | Oid roleid; |
4130 | Oid tablespaceoid; |
4131 | AclMode mode; |
4132 | AclResult aclresult; |
4133 | |
4134 | roleid = GetUserId(); |
4135 | tablespaceoid = convert_tablespace_name(tablespacename); |
4136 | mode = convert_tablespace_priv_string(priv_type_text); |
4137 | |
4138 | aclresult = pg_tablespace_aclcheck(tablespaceoid, roleid, mode); |
4139 | |
4140 | PG_RETURN_BOOL(aclresult == ACLCHECK_OK); |
4141 | } |
4142 | |
4143 | /* |
4144 | * has_tablespace_privilege_name_id |
4145 | * Check user privileges on a tablespace given |
4146 | * name usename, tablespace oid, and text priv name. |
4147 | */ |
4148 | Datum |
4149 | has_tablespace_privilege_name_id(PG_FUNCTION_ARGS) |
4150 | { |
4151 | Name username = PG_GETARG_NAME(0); |
4152 | Oid tablespaceoid = PG_GETARG_OID(1); |
4153 | text *priv_type_text = PG_GETARG_TEXT_PP(2); |
4154 | Oid roleid; |
4155 | AclMode mode; |
4156 | AclResult aclresult; |
4157 | |
4158 | roleid = get_role_oid_or_public(NameStr(*username)); |
4159 | mode = convert_tablespace_priv_string(priv_type_text); |
4160 | |
4161 | if (!SearchSysCacheExists1(TABLESPACEOID, ObjectIdGetDatum(tablespaceoid))) |
4162 | PG_RETURN_NULL(); |
4163 | |
4164 | aclresult = pg_tablespace_aclcheck(tablespaceoid, roleid, mode); |
4165 | |
4166 | PG_RETURN_BOOL(aclresult == ACLCHECK_OK); |
4167 | } |
4168 | |
4169 | /* |
4170 | * has_tablespace_privilege_id |
4171 | * Check user privileges on a tablespace given |
4172 | * tablespace oid, and text priv name. |
4173 | * current_user is assumed |
4174 | */ |
4175 | Datum |
4176 | has_tablespace_privilege_id(PG_FUNCTION_ARGS) |
4177 | { |
4178 | Oid tablespaceoid = PG_GETARG_OID(0); |
4179 | text *priv_type_text = PG_GETARG_TEXT_PP(1); |
4180 | Oid roleid; |
4181 | AclMode mode; |
4182 | AclResult aclresult; |
4183 | |
4184 | roleid = GetUserId(); |
4185 | mode = convert_tablespace_priv_string(priv_type_text); |
4186 | |
4187 | if (!SearchSysCacheExists1(TABLESPACEOID, ObjectIdGetDatum(tablespaceoid))) |
4188 | PG_RETURN_NULL(); |
4189 | |
4190 | aclresult = pg_tablespace_aclcheck(tablespaceoid, roleid, mode); |
4191 | |
4192 | PG_RETURN_BOOL(aclresult == ACLCHECK_OK); |
4193 | } |
4194 | |
4195 | /* |
4196 | * has_tablespace_privilege_id_name |
4197 | * Check user privileges on a tablespace given |
4198 | * roleid, text tablespacename, and text priv name. |
4199 | */ |
4200 | Datum |
4201 | has_tablespace_privilege_id_name(PG_FUNCTION_ARGS) |
4202 | { |
4203 | Oid roleid = PG_GETARG_OID(0); |
4204 | text *tablespacename = PG_GETARG_TEXT_PP(1); |
4205 | text *priv_type_text = PG_GETARG_TEXT_PP(2); |
4206 | Oid tablespaceoid; |
4207 | AclMode mode; |
4208 | AclResult aclresult; |
4209 | |
4210 | tablespaceoid = convert_tablespace_name(tablespacename); |
4211 | mode = convert_tablespace_priv_string(priv_type_text); |
4212 | |
4213 | aclresult = pg_tablespace_aclcheck(tablespaceoid, roleid, mode); |
4214 | |
4215 | PG_RETURN_BOOL(aclresult == ACLCHECK_OK); |
4216 | } |
4217 | |
4218 | /* |
4219 | * has_tablespace_privilege_id_id |
4220 | * Check user privileges on a tablespace given |
4221 | * roleid, tablespace oid, and text priv name. |
4222 | */ |
4223 | Datum |
4224 | has_tablespace_privilege_id_id(PG_FUNCTION_ARGS) |
4225 | { |
4226 | Oid roleid = PG_GETARG_OID(0); |
4227 | Oid tablespaceoid = PG_GETARG_OID(1); |
4228 | text *priv_type_text = PG_GETARG_TEXT_PP(2); |
4229 | AclMode mode; |
4230 | AclResult aclresult; |
4231 | |
4232 | mode = convert_tablespace_priv_string(priv_type_text); |
4233 | |
4234 | if (!SearchSysCacheExists1(TABLESPACEOID, ObjectIdGetDatum(tablespaceoid))) |
4235 | PG_RETURN_NULL(); |
4236 | |
4237 | aclresult = pg_tablespace_aclcheck(tablespaceoid, roleid, mode); |
4238 | |
4239 | PG_RETURN_BOOL(aclresult == ACLCHECK_OK); |
4240 | } |
4241 | |
4242 | /* |
4243 | * Support routines for has_tablespace_privilege family. |
4244 | */ |
4245 | |
4246 | /* |
4247 | * Given a tablespace name expressed as a string, look it up and return Oid |
4248 | */ |
4249 | static Oid |
4250 | convert_tablespace_name(text *tablespacename) |
4251 | { |
4252 | char *spcname = text_to_cstring(tablespacename); |
4253 | |
4254 | return get_tablespace_oid(spcname, false); |
4255 | } |
4256 | |
4257 | /* |
4258 | * convert_tablespace_priv_string |
4259 | * Convert text string to AclMode value. |
4260 | */ |
4261 | static AclMode |
4262 | convert_tablespace_priv_string(text *priv_type_text) |
4263 | { |
4264 | static const priv_map tablespace_priv_map[] = { |
4265 | {"CREATE" , ACL_CREATE}, |
4266 | {"CREATE WITH GRANT OPTION" , ACL_GRANT_OPTION_FOR(ACL_CREATE)}, |
4267 | {NULL, 0} |
4268 | }; |
4269 | |
4270 | return convert_any_priv_string(priv_type_text, tablespace_priv_map); |
4271 | } |
4272 | |
4273 | /* |
4274 | * has_type_privilege variants |
4275 | * These are all named "has_type_privilege" at the SQL level. |
4276 | * They take various combinations of type name, type OID, |
4277 | * user name, user OID, or implicit user = current_user. |
4278 | * |
4279 | * The result is a boolean value: true if user has the indicated |
4280 | * privilege, false if not, or NULL if object doesn't exist. |
4281 | */ |
4282 | |
4283 | /* |
4284 | * has_type_privilege_name_name |
4285 | * Check user privileges on a type given |
4286 | * name username, text typename, and text priv name. |
4287 | */ |
4288 | Datum |
4289 | has_type_privilege_name_name(PG_FUNCTION_ARGS) |
4290 | { |
4291 | Name username = PG_GETARG_NAME(0); |
4292 | text *typename = PG_GETARG_TEXT_PP(1); |
4293 | text *priv_type_text = PG_GETARG_TEXT_PP(2); |
4294 | Oid roleid; |
4295 | Oid typeoid; |
4296 | AclMode mode; |
4297 | AclResult aclresult; |
4298 | |
4299 | roleid = get_role_oid_or_public(NameStr(*username)); |
4300 | typeoid = convert_type_name(typename); |
4301 | mode = convert_type_priv_string(priv_type_text); |
4302 | |
4303 | aclresult = pg_type_aclcheck(typeoid, roleid, mode); |
4304 | |
4305 | PG_RETURN_BOOL(aclresult == ACLCHECK_OK); |
4306 | } |
4307 | |
4308 | /* |
4309 | * has_type_privilege_name |
4310 | * Check user privileges on a type given |
4311 | * text typename and text priv name. |
4312 | * current_user is assumed |
4313 | */ |
4314 | Datum |
4315 | has_type_privilege_name(PG_FUNCTION_ARGS) |
4316 | { |
4317 | text *typename = PG_GETARG_TEXT_PP(0); |
4318 | text *priv_type_text = PG_GETARG_TEXT_PP(1); |
4319 | Oid roleid; |
4320 | Oid typeoid; |
4321 | AclMode mode; |
4322 | AclResult aclresult; |
4323 | |
4324 | roleid = GetUserId(); |
4325 | typeoid = convert_type_name(typename); |
4326 | mode = convert_type_priv_string(priv_type_text); |
4327 | |
4328 | aclresult = pg_type_aclcheck(typeoid, roleid, mode); |
4329 | |
4330 | PG_RETURN_BOOL(aclresult == ACLCHECK_OK); |
4331 | } |
4332 | |
4333 | /* |
4334 | * has_type_privilege_name_id |
4335 | * Check user privileges on a type given |
4336 | * name usename, type oid, and text priv name. |
4337 | */ |
4338 | Datum |
4339 | has_type_privilege_name_id(PG_FUNCTION_ARGS) |
4340 | { |
4341 | Name username = PG_GETARG_NAME(0); |
4342 | Oid typeoid = PG_GETARG_OID(1); |
4343 | text *priv_type_text = PG_GETARG_TEXT_PP(2); |
4344 | Oid roleid; |
4345 | AclMode mode; |
4346 | AclResult aclresult; |
4347 | |
4348 | roleid = get_role_oid_or_public(NameStr(*username)); |
4349 | mode = convert_type_priv_string(priv_type_text); |
4350 | |
4351 | if (!SearchSysCacheExists1(TYPEOID, ObjectIdGetDatum(typeoid))) |
4352 | PG_RETURN_NULL(); |
4353 | |
4354 | aclresult = pg_type_aclcheck(typeoid, roleid, mode); |
4355 | |
4356 | PG_RETURN_BOOL(aclresult == ACLCHECK_OK); |
4357 | } |
4358 | |
4359 | /* |
4360 | * has_type_privilege_id |
4361 | * Check user privileges on a type given |
4362 | * type oid, and text priv name. |
4363 | * current_user is assumed |
4364 | */ |
4365 | Datum |
4366 | has_type_privilege_id(PG_FUNCTION_ARGS) |
4367 | { |
4368 | Oid typeoid = PG_GETARG_OID(0); |
4369 | text *priv_type_text = PG_GETARG_TEXT_PP(1); |
4370 | Oid roleid; |
4371 | AclMode mode; |
4372 | AclResult aclresult; |
4373 | |
4374 | roleid = GetUserId(); |
4375 | mode = convert_type_priv_string(priv_type_text); |
4376 | |
4377 | if (!SearchSysCacheExists1(TYPEOID, ObjectIdGetDatum(typeoid))) |
4378 | PG_RETURN_NULL(); |
4379 | |
4380 | aclresult = pg_type_aclcheck(typeoid, roleid, mode); |
4381 | |
4382 | PG_RETURN_BOOL(aclresult == ACLCHECK_OK); |
4383 | } |
4384 | |
4385 | /* |
4386 | * has_type_privilege_id_name |
4387 | * Check user privileges on a type given |
4388 | * roleid, text typename, and text priv name. |
4389 | */ |
4390 | Datum |
4391 | has_type_privilege_id_name(PG_FUNCTION_ARGS) |
4392 | { |
4393 | Oid roleid = PG_GETARG_OID(0); |
4394 | text *typename = PG_GETARG_TEXT_PP(1); |
4395 | text *priv_type_text = PG_GETARG_TEXT_PP(2); |
4396 | Oid typeoid; |
4397 | AclMode mode; |
4398 | AclResult aclresult; |
4399 | |
4400 | typeoid = convert_type_name(typename); |
4401 | mode = convert_type_priv_string(priv_type_text); |
4402 | |
4403 | aclresult = pg_type_aclcheck(typeoid, roleid, mode); |
4404 | |
4405 | PG_RETURN_BOOL(aclresult == ACLCHECK_OK); |
4406 | } |
4407 | |
4408 | /* |
4409 | * has_type_privilege_id_id |
4410 | * Check user privileges on a type given |
4411 | * roleid, type oid, and text priv name. |
4412 | */ |
4413 | Datum |
4414 | has_type_privilege_id_id(PG_FUNCTION_ARGS) |
4415 | { |
4416 | Oid roleid = PG_GETARG_OID(0); |
4417 | Oid typeoid = PG_GETARG_OID(1); |
4418 | text *priv_type_text = PG_GETARG_TEXT_PP(2); |
4419 | AclMode mode; |
4420 | AclResult aclresult; |
4421 | |
4422 | mode = convert_type_priv_string(priv_type_text); |
4423 | |
4424 | if (!SearchSysCacheExists1(TYPEOID, ObjectIdGetDatum(typeoid))) |
4425 | PG_RETURN_NULL(); |
4426 | |
4427 | aclresult = pg_type_aclcheck(typeoid, roleid, mode); |
4428 | |
4429 | PG_RETURN_BOOL(aclresult == ACLCHECK_OK); |
4430 | } |
4431 | |
4432 | /* |
4433 | * Support routines for has_type_privilege family. |
4434 | */ |
4435 | |
4436 | /* |
4437 | * Given a type name expressed as a string, look it up and return Oid |
4438 | */ |
4439 | static Oid |
4440 | convert_type_name(text *typename) |
4441 | { |
4442 | char *typname = text_to_cstring(typename); |
4443 | Oid oid; |
4444 | |
4445 | oid = DatumGetObjectId(DirectFunctionCall1(regtypein, |
4446 | CStringGetDatum(typname))); |
4447 | |
4448 | if (!OidIsValid(oid)) |
4449 | ereport(ERROR, |
4450 | (errcode(ERRCODE_UNDEFINED_OBJECT), |
4451 | errmsg("type \"%s\" does not exist" , typname))); |
4452 | |
4453 | return oid; |
4454 | } |
4455 | |
4456 | /* |
4457 | * convert_type_priv_string |
4458 | * Convert text string to AclMode value. |
4459 | */ |
4460 | static AclMode |
4461 | convert_type_priv_string(text *priv_type_text) |
4462 | { |
4463 | static const priv_map type_priv_map[] = { |
4464 | {"USAGE" , ACL_USAGE}, |
4465 | {"USAGE WITH GRANT OPTION" , ACL_GRANT_OPTION_FOR(ACL_USAGE)}, |
4466 | {NULL, 0} |
4467 | }; |
4468 | |
4469 | return convert_any_priv_string(priv_type_text, type_priv_map); |
4470 | } |
4471 | |
4472 | |
4473 | /* |
4474 | * pg_has_role variants |
4475 | * These are all named "pg_has_role" at the SQL level. |
4476 | * They take various combinations of role name, role OID, |
4477 | * user name, user OID, or implicit user = current_user. |
4478 | * |
4479 | * The result is a boolean value: true if user has the indicated |
4480 | * privilege, false if not. |
4481 | */ |
4482 | |
4483 | /* |
4484 | * pg_has_role_name_name |
4485 | * Check user privileges on a role given |
4486 | * name username, name rolename, and text priv name. |
4487 | */ |
4488 | Datum |
4489 | pg_has_role_name_name(PG_FUNCTION_ARGS) |
4490 | { |
4491 | Name username = PG_GETARG_NAME(0); |
4492 | Name rolename = PG_GETARG_NAME(1); |
4493 | text *priv_type_text = PG_GETARG_TEXT_PP(2); |
4494 | Oid roleid; |
4495 | Oid roleoid; |
4496 | AclMode mode; |
4497 | AclResult aclresult; |
4498 | |
4499 | roleid = get_role_oid(NameStr(*username), false); |
4500 | roleoid = get_role_oid(NameStr(*rolename), false); |
4501 | mode = convert_role_priv_string(priv_type_text); |
4502 | |
4503 | aclresult = pg_role_aclcheck(roleoid, roleid, mode); |
4504 | |
4505 | PG_RETURN_BOOL(aclresult == ACLCHECK_OK); |
4506 | } |
4507 | |
4508 | /* |
4509 | * pg_has_role_name |
4510 | * Check user privileges on a role given |
4511 | * name rolename and text priv name. |
4512 | * current_user is assumed |
4513 | */ |
4514 | Datum |
4515 | pg_has_role_name(PG_FUNCTION_ARGS) |
4516 | { |
4517 | Name rolename = PG_GETARG_NAME(0); |
4518 | text *priv_type_text = PG_GETARG_TEXT_PP(1); |
4519 | Oid roleid; |
4520 | Oid roleoid; |
4521 | AclMode mode; |
4522 | AclResult aclresult; |
4523 | |
4524 | roleid = GetUserId(); |
4525 | roleoid = get_role_oid(NameStr(*rolename), false); |
4526 | mode = convert_role_priv_string(priv_type_text); |
4527 | |
4528 | aclresult = pg_role_aclcheck(roleoid, roleid, mode); |
4529 | |
4530 | PG_RETURN_BOOL(aclresult == ACLCHECK_OK); |
4531 | } |
4532 | |
4533 | /* |
4534 | * pg_has_role_name_id |
4535 | * Check user privileges on a role given |
4536 | * name usename, role oid, and text priv name. |
4537 | */ |
4538 | Datum |
4539 | pg_has_role_name_id(PG_FUNCTION_ARGS) |
4540 | { |
4541 | Name username = PG_GETARG_NAME(0); |
4542 | Oid roleoid = PG_GETARG_OID(1); |
4543 | text *priv_type_text = PG_GETARG_TEXT_PP(2); |
4544 | Oid roleid; |
4545 | AclMode mode; |
4546 | AclResult aclresult; |
4547 | |
4548 | roleid = get_role_oid(NameStr(*username), false); |
4549 | mode = convert_role_priv_string(priv_type_text); |
4550 | |
4551 | aclresult = pg_role_aclcheck(roleoid, roleid, mode); |
4552 | |
4553 | PG_RETURN_BOOL(aclresult == ACLCHECK_OK); |
4554 | } |
4555 | |
4556 | /* |
4557 | * pg_has_role_id |
4558 | * Check user privileges on a role given |
4559 | * role oid, and text priv name. |
4560 | * current_user is assumed |
4561 | */ |
4562 | Datum |
4563 | pg_has_role_id(PG_FUNCTION_ARGS) |
4564 | { |
4565 | Oid roleoid = PG_GETARG_OID(0); |
4566 | text *priv_type_text = PG_GETARG_TEXT_PP(1); |
4567 | Oid roleid; |
4568 | AclMode mode; |
4569 | AclResult aclresult; |
4570 | |
4571 | roleid = GetUserId(); |
4572 | mode = convert_role_priv_string(priv_type_text); |
4573 | |
4574 | aclresult = pg_role_aclcheck(roleoid, roleid, mode); |
4575 | |
4576 | PG_RETURN_BOOL(aclresult == ACLCHECK_OK); |
4577 | } |
4578 | |
4579 | /* |
4580 | * pg_has_role_id_name |
4581 | * Check user privileges on a role given |
4582 | * roleid, name rolename, and text priv name. |
4583 | */ |
4584 | Datum |
4585 | pg_has_role_id_name(PG_FUNCTION_ARGS) |
4586 | { |
4587 | Oid roleid = PG_GETARG_OID(0); |
4588 | Name rolename = PG_GETARG_NAME(1); |
4589 | text *priv_type_text = PG_GETARG_TEXT_PP(2); |
4590 | Oid roleoid; |
4591 | AclMode mode; |
4592 | AclResult aclresult; |
4593 | |
4594 | roleoid = get_role_oid(NameStr(*rolename), false); |
4595 | mode = convert_role_priv_string(priv_type_text); |
4596 | |
4597 | aclresult = pg_role_aclcheck(roleoid, roleid, mode); |
4598 | |
4599 | PG_RETURN_BOOL(aclresult == ACLCHECK_OK); |
4600 | } |
4601 | |
4602 | /* |
4603 | * pg_has_role_id_id |
4604 | * Check user privileges on a role given |
4605 | * roleid, role oid, and text priv name. |
4606 | */ |
4607 | Datum |
4608 | pg_has_role_id_id(PG_FUNCTION_ARGS) |
4609 | { |
4610 | Oid roleid = PG_GETARG_OID(0); |
4611 | Oid roleoid = PG_GETARG_OID(1); |
4612 | text *priv_type_text = PG_GETARG_TEXT_PP(2); |
4613 | AclMode mode; |
4614 | AclResult aclresult; |
4615 | |
4616 | mode = convert_role_priv_string(priv_type_text); |
4617 | |
4618 | aclresult = pg_role_aclcheck(roleoid, roleid, mode); |
4619 | |
4620 | PG_RETURN_BOOL(aclresult == ACLCHECK_OK); |
4621 | } |
4622 | |
4623 | /* |
4624 | * Support routines for pg_has_role family. |
4625 | */ |
4626 | |
4627 | /* |
4628 | * convert_role_priv_string |
4629 | * Convert text string to AclMode value. |
4630 | * |
4631 | * We use USAGE to denote whether the privileges of the role are accessible |
4632 | * (has_privs), MEMBER to denote is_member, and MEMBER WITH GRANT OPTION |
4633 | * (or ADMIN OPTION) to denote is_admin. There is no ACL bit corresponding |
4634 | * to MEMBER so we cheat and use ACL_CREATE for that. This convention |
4635 | * is shared only with pg_role_aclcheck, below. |
4636 | */ |
4637 | static AclMode |
4638 | convert_role_priv_string(text *priv_type_text) |
4639 | { |
4640 | static const priv_map role_priv_map[] = { |
4641 | {"USAGE" , ACL_USAGE}, |
4642 | {"MEMBER" , ACL_CREATE}, |
4643 | {"USAGE WITH GRANT OPTION" , ACL_GRANT_OPTION_FOR(ACL_CREATE)}, |
4644 | {"USAGE WITH ADMIN OPTION" , ACL_GRANT_OPTION_FOR(ACL_CREATE)}, |
4645 | {"MEMBER WITH GRANT OPTION" , ACL_GRANT_OPTION_FOR(ACL_CREATE)}, |
4646 | {"MEMBER WITH ADMIN OPTION" , ACL_GRANT_OPTION_FOR(ACL_CREATE)}, |
4647 | {NULL, 0} |
4648 | }; |
4649 | |
4650 | return convert_any_priv_string(priv_type_text, role_priv_map); |
4651 | } |
4652 | |
4653 | /* |
4654 | * pg_role_aclcheck |
4655 | * Quick-and-dirty support for pg_has_role |
4656 | */ |
4657 | static AclResult |
4658 | pg_role_aclcheck(Oid role_oid, Oid roleid, AclMode mode) |
4659 | { |
4660 | if (mode & ACL_GRANT_OPTION_FOR(ACL_CREATE)) |
4661 | { |
4662 | /* |
4663 | * XXX For roleid == role_oid, is_admin_of_role() also examines the |
4664 | * session and call stack. That suits two-argument pg_has_role(), but |
4665 | * it gives the three-argument version a lamentable whimsy. |
4666 | */ |
4667 | if (is_admin_of_role(roleid, role_oid)) |
4668 | return ACLCHECK_OK; |
4669 | } |
4670 | if (mode & ACL_CREATE) |
4671 | { |
4672 | if (is_member_of_role(roleid, role_oid)) |
4673 | return ACLCHECK_OK; |
4674 | } |
4675 | if (mode & ACL_USAGE) |
4676 | { |
4677 | if (has_privs_of_role(roleid, role_oid)) |
4678 | return ACLCHECK_OK; |
4679 | } |
4680 | return ACLCHECK_NO_PRIV; |
4681 | } |
4682 | |
4683 | |
4684 | /* |
4685 | * initialization function (called by InitPostgres) |
4686 | */ |
4687 | void |
4688 | initialize_acl(void) |
4689 | { |
4690 | if (!IsBootstrapProcessingMode()) |
4691 | { |
4692 | /* |
4693 | * In normal mode, set a callback on any syscache invalidation of |
4694 | * pg_auth_members rows |
4695 | */ |
4696 | CacheRegisterSyscacheCallback(AUTHMEMROLEMEM, |
4697 | RoleMembershipCacheCallback, |
4698 | (Datum) 0); |
4699 | } |
4700 | } |
4701 | |
4702 | /* |
4703 | * RoleMembershipCacheCallback |
4704 | * Syscache inval callback function |
4705 | */ |
4706 | static void |
4707 | RoleMembershipCacheCallback(Datum arg, int cacheid, uint32 hashvalue) |
4708 | { |
4709 | /* Force membership caches to be recomputed on next use */ |
4710 | cached_privs_role = InvalidOid; |
4711 | cached_member_role = InvalidOid; |
4712 | } |
4713 | |
4714 | |
4715 | /* Check if specified role has rolinherit set */ |
4716 | static bool |
4717 | has_rolinherit(Oid roleid) |
4718 | { |
4719 | bool result = false; |
4720 | HeapTuple utup; |
4721 | |
4722 | utup = SearchSysCache1(AUTHOID, ObjectIdGetDatum(roleid)); |
4723 | if (HeapTupleIsValid(utup)) |
4724 | { |
4725 | result = ((Form_pg_authid) GETSTRUCT(utup))->rolinherit; |
4726 | ReleaseSysCache(utup); |
4727 | } |
4728 | return result; |
4729 | } |
4730 | |
4731 | |
4732 | /* |
4733 | * Get a list of roles that the specified roleid has the privileges of |
4734 | * |
4735 | * This is defined not to recurse through roles that don't have rolinherit |
4736 | * set; for such roles, membership implies the ability to do SET ROLE, but |
4737 | * the privileges are not available until you've done so. |
4738 | * |
4739 | * Since indirect membership testing is relatively expensive, we cache |
4740 | * a list of memberships. Hence, the result is only guaranteed good until |
4741 | * the next call of roles_has_privs_of()! |
4742 | * |
4743 | * For the benefit of select_best_grantor, the result is defined to be |
4744 | * in breadth-first order, ie, closer relationships earlier. |
4745 | */ |
4746 | static List * |
4747 | roles_has_privs_of(Oid roleid) |
4748 | { |
4749 | List *roles_list; |
4750 | ListCell *l; |
4751 | List *new_cached_privs_roles; |
4752 | MemoryContext oldctx; |
4753 | |
4754 | /* If cache is already valid, just return the list */ |
4755 | if (OidIsValid(cached_privs_role) && cached_privs_role == roleid) |
4756 | return cached_privs_roles; |
4757 | |
4758 | /* |
4759 | * Find all the roles that roleid is a member of, including multi-level |
4760 | * recursion. The role itself will always be the first element of the |
4761 | * resulting list. |
4762 | * |
4763 | * Each element of the list is scanned to see if it adds any indirect |
4764 | * memberships. We can use a single list as both the record of |
4765 | * already-found memberships and the agenda of roles yet to be scanned. |
4766 | * This is a bit tricky but works because the foreach() macro doesn't |
4767 | * fetch the next list element until the bottom of the loop. |
4768 | */ |
4769 | roles_list = list_make1_oid(roleid); |
4770 | |
4771 | foreach(l, roles_list) |
4772 | { |
4773 | Oid memberid = lfirst_oid(l); |
4774 | CatCList *memlist; |
4775 | int i; |
4776 | |
4777 | /* Ignore non-inheriting roles */ |
4778 | if (!has_rolinherit(memberid)) |
4779 | continue; |
4780 | |
4781 | /* Find roles that memberid is directly a member of */ |
4782 | memlist = SearchSysCacheList1(AUTHMEMMEMROLE, |
4783 | ObjectIdGetDatum(memberid)); |
4784 | for (i = 0; i < memlist->n_members; i++) |
4785 | { |
4786 | HeapTuple tup = &memlist->members[i]->tuple; |
4787 | Oid otherid = ((Form_pg_auth_members) GETSTRUCT(tup))->roleid; |
4788 | |
4789 | /* |
4790 | * Even though there shouldn't be any loops in the membership |
4791 | * graph, we must test for having already seen this role. It is |
4792 | * legal for instance to have both A->B and A->C->B. |
4793 | */ |
4794 | roles_list = list_append_unique_oid(roles_list, otherid); |
4795 | } |
4796 | ReleaseSysCacheList(memlist); |
4797 | } |
4798 | |
4799 | /* |
4800 | * Copy the completed list into TopMemoryContext so it will persist. |
4801 | */ |
4802 | oldctx = MemoryContextSwitchTo(TopMemoryContext); |
4803 | new_cached_privs_roles = list_copy(roles_list); |
4804 | MemoryContextSwitchTo(oldctx); |
4805 | list_free(roles_list); |
4806 | |
4807 | /* |
4808 | * Now safe to assign to state variable |
4809 | */ |
4810 | cached_privs_role = InvalidOid; /* just paranoia */ |
4811 | list_free(cached_privs_roles); |
4812 | cached_privs_roles = new_cached_privs_roles; |
4813 | cached_privs_role = roleid; |
4814 | |
4815 | /* And now we can return the answer */ |
4816 | return cached_privs_roles; |
4817 | } |
4818 | |
4819 | |
4820 | /* |
4821 | * Get a list of roles that the specified roleid is a member of |
4822 | * |
4823 | * This is defined to recurse through roles regardless of rolinherit. |
4824 | * |
4825 | * Since indirect membership testing is relatively expensive, we cache |
4826 | * a list of memberships. Hence, the result is only guaranteed good until |
4827 | * the next call of roles_is_member_of()! |
4828 | */ |
4829 | static List * |
4830 | roles_is_member_of(Oid roleid) |
4831 | { |
4832 | List *roles_list; |
4833 | ListCell *l; |
4834 | List *new_cached_membership_roles; |
4835 | MemoryContext oldctx; |
4836 | |
4837 | /* If cache is already valid, just return the list */ |
4838 | if (OidIsValid(cached_member_role) && cached_member_role == roleid) |
4839 | return cached_membership_roles; |
4840 | |
4841 | /* |
4842 | * Find all the roles that roleid is a member of, including multi-level |
4843 | * recursion. The role itself will always be the first element of the |
4844 | * resulting list. |
4845 | * |
4846 | * Each element of the list is scanned to see if it adds any indirect |
4847 | * memberships. We can use a single list as both the record of |
4848 | * already-found memberships and the agenda of roles yet to be scanned. |
4849 | * This is a bit tricky but works because the foreach() macro doesn't |
4850 | * fetch the next list element until the bottom of the loop. |
4851 | */ |
4852 | roles_list = list_make1_oid(roleid); |
4853 | |
4854 | foreach(l, roles_list) |
4855 | { |
4856 | Oid memberid = lfirst_oid(l); |
4857 | CatCList *memlist; |
4858 | int i; |
4859 | |
4860 | /* Find roles that memberid is directly a member of */ |
4861 | memlist = SearchSysCacheList1(AUTHMEMMEMROLE, |
4862 | ObjectIdGetDatum(memberid)); |
4863 | for (i = 0; i < memlist->n_members; i++) |
4864 | { |
4865 | HeapTuple tup = &memlist->members[i]->tuple; |
4866 | Oid otherid = ((Form_pg_auth_members) GETSTRUCT(tup))->roleid; |
4867 | |
4868 | /* |
4869 | * Even though there shouldn't be any loops in the membership |
4870 | * graph, we must test for having already seen this role. It is |
4871 | * legal for instance to have both A->B and A->C->B. |
4872 | */ |
4873 | roles_list = list_append_unique_oid(roles_list, otherid); |
4874 | } |
4875 | ReleaseSysCacheList(memlist); |
4876 | } |
4877 | |
4878 | /* |
4879 | * Copy the completed list into TopMemoryContext so it will persist. |
4880 | */ |
4881 | oldctx = MemoryContextSwitchTo(TopMemoryContext); |
4882 | new_cached_membership_roles = list_copy(roles_list); |
4883 | MemoryContextSwitchTo(oldctx); |
4884 | list_free(roles_list); |
4885 | |
4886 | /* |
4887 | * Now safe to assign to state variable |
4888 | */ |
4889 | cached_member_role = InvalidOid; /* just paranoia */ |
4890 | list_free(cached_membership_roles); |
4891 | cached_membership_roles = new_cached_membership_roles; |
4892 | cached_member_role = roleid; |
4893 | |
4894 | /* And now we can return the answer */ |
4895 | return cached_membership_roles; |
4896 | } |
4897 | |
4898 | |
4899 | /* |
4900 | * Does member have the privileges of role (directly or indirectly)? |
4901 | * |
4902 | * This is defined not to recurse through roles that don't have rolinherit |
4903 | * set; for such roles, membership implies the ability to do SET ROLE, but |
4904 | * the privileges are not available until you've done so. |
4905 | */ |
4906 | bool |
4907 | has_privs_of_role(Oid member, Oid role) |
4908 | { |
4909 | /* Fast path for simple case */ |
4910 | if (member == role) |
4911 | return true; |
4912 | |
4913 | /* Superusers have every privilege, so are part of every role */ |
4914 | if (superuser_arg(member)) |
4915 | return true; |
4916 | |
4917 | /* |
4918 | * Find all the roles that member has the privileges of, including |
4919 | * multi-level recursion, then see if target role is any one of them. |
4920 | */ |
4921 | return list_member_oid(roles_has_privs_of(member), role); |
4922 | } |
4923 | |
4924 | |
4925 | /* |
4926 | * Is member a member of role (directly or indirectly)? |
4927 | * |
4928 | * This is defined to recurse through roles regardless of rolinherit. |
4929 | */ |
4930 | bool |
4931 | is_member_of_role(Oid member, Oid role) |
4932 | { |
4933 | /* Fast path for simple case */ |
4934 | if (member == role) |
4935 | return true; |
4936 | |
4937 | /* Superusers have every privilege, so are part of every role */ |
4938 | if (superuser_arg(member)) |
4939 | return true; |
4940 | |
4941 | /* |
4942 | * Find all the roles that member is a member of, including multi-level |
4943 | * recursion, then see if target role is any one of them. |
4944 | */ |
4945 | return list_member_oid(roles_is_member_of(member), role); |
4946 | } |
4947 | |
4948 | /* |
4949 | * check_is_member_of_role |
4950 | * is_member_of_role with a standard permission-violation error if not |
4951 | */ |
4952 | void |
4953 | check_is_member_of_role(Oid member, Oid role) |
4954 | { |
4955 | if (!is_member_of_role(member, role)) |
4956 | ereport(ERROR, |
4957 | (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), |
4958 | errmsg("must be member of role \"%s\"" , |
4959 | GetUserNameFromId(role, false)))); |
4960 | } |
4961 | |
4962 | /* |
4963 | * Is member a member of role, not considering superuserness? |
4964 | * |
4965 | * This is identical to is_member_of_role except we ignore superuser |
4966 | * status. |
4967 | */ |
4968 | bool |
4969 | is_member_of_role_nosuper(Oid member, Oid role) |
4970 | { |
4971 | /* Fast path for simple case */ |
4972 | if (member == role) |
4973 | return true; |
4974 | |
4975 | /* |
4976 | * Find all the roles that member is a member of, including multi-level |
4977 | * recursion, then see if target role is any one of them. |
4978 | */ |
4979 | return list_member_oid(roles_is_member_of(member), role); |
4980 | } |
4981 | |
4982 | |
4983 | /* |
4984 | * Is member an admin of role? That is, is member the role itself (subject to |
4985 | * restrictions below), a member (directly or indirectly) WITH ADMIN OPTION, |
4986 | * or a superuser? |
4987 | */ |
4988 | bool |
4989 | is_admin_of_role(Oid member, Oid role) |
4990 | { |
4991 | bool result = false; |
4992 | List *roles_list; |
4993 | ListCell *l; |
4994 | |
4995 | if (superuser_arg(member)) |
4996 | return true; |
4997 | |
4998 | if (member == role) |
4999 | |
5000 | /* |
5001 | * A role can admin itself when it matches the session user and we're |
5002 | * outside any security-restricted operation, SECURITY DEFINER or |
5003 | * similar context. SQL-standard roles cannot self-admin. However, |
5004 | * SQL-standard users are distinct from roles, and they are not |
5005 | * grantable like roles: PostgreSQL's role-user duality extends the |
5006 | * standard. Checking for a session user match has the effect of |
5007 | * letting a role self-admin only when it's conspicuously behaving |
5008 | * like a user. Note that allowing self-admin under a mere SET ROLE |
5009 | * would make WITH ADMIN OPTION largely irrelevant; any member could |
5010 | * SET ROLE to issue the otherwise-forbidden command. |
5011 | * |
5012 | * Withholding self-admin in a security-restricted operation prevents |
5013 | * object owners from harnessing the session user identity during |
5014 | * administrative maintenance. Suppose Alice owns a database, has |
5015 | * issued "GRANT alice TO bob", and runs a daily ANALYZE. Bob creates |
5016 | * an alice-owned SECURITY DEFINER function that issues "REVOKE alice |
5017 | * FROM carol". If he creates an expression index calling that |
5018 | * function, Alice will attempt the REVOKE during each ANALYZE. |
5019 | * Checking InSecurityRestrictedOperation() thwarts that attack. |
5020 | * |
5021 | * Withholding self-admin in SECURITY DEFINER functions makes their |
5022 | * behavior independent of the calling user. There's no security or |
5023 | * SQL-standard-conformance need for that restriction, though. |
5024 | * |
5025 | * A role cannot have actual WITH ADMIN OPTION on itself, because that |
5026 | * would imply a membership loop. Therefore, we're done either way. |
5027 | */ |
5028 | return member == GetSessionUserId() && |
5029 | !InLocalUserIdChange() && !InSecurityRestrictedOperation(); |
5030 | |
5031 | /* |
5032 | * Find all the roles that member is a member of, including multi-level |
5033 | * recursion. We build a list in the same way that is_member_of_role does |
5034 | * to track visited and unvisited roles. |
5035 | */ |
5036 | roles_list = list_make1_oid(member); |
5037 | |
5038 | foreach(l, roles_list) |
5039 | { |
5040 | Oid memberid = lfirst_oid(l); |
5041 | CatCList *memlist; |
5042 | int i; |
5043 | |
5044 | /* Find roles that memberid is directly a member of */ |
5045 | memlist = SearchSysCacheList1(AUTHMEMMEMROLE, |
5046 | ObjectIdGetDatum(memberid)); |
5047 | for (i = 0; i < memlist->n_members; i++) |
5048 | { |
5049 | HeapTuple tup = &memlist->members[i]->tuple; |
5050 | Oid otherid = ((Form_pg_auth_members) GETSTRUCT(tup))->roleid; |
5051 | |
5052 | if (otherid == role && |
5053 | ((Form_pg_auth_members) GETSTRUCT(tup))->admin_option) |
5054 | { |
5055 | /* Found what we came for, so can stop searching */ |
5056 | result = true; |
5057 | break; |
5058 | } |
5059 | |
5060 | roles_list = list_append_unique_oid(roles_list, otherid); |
5061 | } |
5062 | ReleaseSysCacheList(memlist); |
5063 | if (result) |
5064 | break; |
5065 | } |
5066 | |
5067 | list_free(roles_list); |
5068 | |
5069 | return result; |
5070 | } |
5071 | |
5072 | |
5073 | /* does what it says ... */ |
5074 | static int |
5075 | count_one_bits(AclMode mask) |
5076 | { |
5077 | int nbits = 0; |
5078 | |
5079 | /* this code relies on AclMode being an unsigned type */ |
5080 | while (mask) |
5081 | { |
5082 | if (mask & 1) |
5083 | nbits++; |
5084 | mask >>= 1; |
5085 | } |
5086 | return nbits; |
5087 | } |
5088 | |
5089 | |
5090 | /* |
5091 | * Select the effective grantor ID for a GRANT or REVOKE operation. |
5092 | * |
5093 | * The grantor must always be either the object owner or some role that has |
5094 | * been explicitly granted grant options. This ensures that all granted |
5095 | * privileges appear to flow from the object owner, and there are never |
5096 | * multiple "original sources" of a privilege. Therefore, if the would-be |
5097 | * grantor is a member of a role that has the needed grant options, we have |
5098 | * to do the grant as that role instead. |
5099 | * |
5100 | * It is possible that the would-be grantor is a member of several roles |
5101 | * that have different subsets of the desired grant options, but no one |
5102 | * role has 'em all. In this case we pick a role with the largest number |
5103 | * of desired options. Ties are broken in favor of closer ancestors. |
5104 | * |
5105 | * roleId: the role attempting to do the GRANT/REVOKE |
5106 | * privileges: the privileges to be granted/revoked |
5107 | * acl: the ACL of the object in question |
5108 | * ownerId: the role owning the object in question |
5109 | * *grantorId: receives the OID of the role to do the grant as |
5110 | * *grantOptions: receives the grant options actually held by grantorId |
5111 | * |
5112 | * If no grant options exist, we set grantorId to roleId, grantOptions to 0. |
5113 | */ |
5114 | void |
5115 | select_best_grantor(Oid roleId, AclMode privileges, |
5116 | const Acl *acl, Oid ownerId, |
5117 | Oid *grantorId, AclMode *grantOptions) |
5118 | { |
5119 | AclMode needed_goptions = ACL_GRANT_OPTION_FOR(privileges); |
5120 | List *roles_list; |
5121 | int nrights; |
5122 | ListCell *l; |
5123 | |
5124 | /* |
5125 | * The object owner is always treated as having all grant options, so if |
5126 | * roleId is the owner it's easy. Also, if roleId is a superuser it's |
5127 | * easy: superusers are implicitly members of every role, so they act as |
5128 | * the object owner. |
5129 | */ |
5130 | if (roleId == ownerId || superuser_arg(roleId)) |
5131 | { |
5132 | *grantorId = ownerId; |
5133 | *grantOptions = needed_goptions; |
5134 | return; |
5135 | } |
5136 | |
5137 | /* |
5138 | * Otherwise we have to do a careful search to see if roleId has the |
5139 | * privileges of any suitable role. Note: we can hang onto the result of |
5140 | * roles_has_privs_of() throughout this loop, because aclmask_direct() |
5141 | * doesn't query any role memberships. |
5142 | */ |
5143 | roles_list = roles_has_privs_of(roleId); |
5144 | |
5145 | /* initialize candidate result as default */ |
5146 | *grantorId = roleId; |
5147 | *grantOptions = ACL_NO_RIGHTS; |
5148 | nrights = 0; |
5149 | |
5150 | foreach(l, roles_list) |
5151 | { |
5152 | Oid otherrole = lfirst_oid(l); |
5153 | AclMode otherprivs; |
5154 | |
5155 | otherprivs = aclmask_direct(acl, otherrole, ownerId, |
5156 | needed_goptions, ACLMASK_ALL); |
5157 | if (otherprivs == needed_goptions) |
5158 | { |
5159 | /* Found a suitable grantor */ |
5160 | *grantorId = otherrole; |
5161 | *grantOptions = otherprivs; |
5162 | return; |
5163 | } |
5164 | |
5165 | /* |
5166 | * If it has just some of the needed privileges, remember best |
5167 | * candidate. |
5168 | */ |
5169 | if (otherprivs != ACL_NO_RIGHTS) |
5170 | { |
5171 | int nnewrights = count_one_bits(otherprivs); |
5172 | |
5173 | if (nnewrights > nrights) |
5174 | { |
5175 | *grantorId = otherrole; |
5176 | *grantOptions = otherprivs; |
5177 | nrights = nnewrights; |
5178 | } |
5179 | } |
5180 | } |
5181 | } |
5182 | |
5183 | /* |
5184 | * get_role_oid - Given a role name, look up the role's OID. |
5185 | * |
5186 | * If missing_ok is false, throw an error if role name not found. If |
5187 | * true, just return InvalidOid. |
5188 | */ |
5189 | Oid |
5190 | get_role_oid(const char *rolname, bool missing_ok) |
5191 | { |
5192 | Oid oid; |
5193 | |
5194 | oid = GetSysCacheOid1(AUTHNAME, Anum_pg_authid_oid, |
5195 | CStringGetDatum(rolname)); |
5196 | if (!OidIsValid(oid) && !missing_ok) |
5197 | ereport(ERROR, |
5198 | (errcode(ERRCODE_UNDEFINED_OBJECT), |
5199 | errmsg("role \"%s\" does not exist" , rolname))); |
5200 | return oid; |
5201 | } |
5202 | |
5203 | /* |
5204 | * get_role_oid_or_public - As above, but return ACL_ID_PUBLIC if the |
5205 | * role name is "public". |
5206 | */ |
5207 | Oid |
5208 | get_role_oid_or_public(const char *rolname) |
5209 | { |
5210 | if (strcmp(rolname, "public" ) == 0) |
5211 | return ACL_ID_PUBLIC; |
5212 | |
5213 | return get_role_oid(rolname, false); |
5214 | } |
5215 | |
5216 | /* |
5217 | * Given a RoleSpec node, return the OID it corresponds to. If missing_ok is |
5218 | * true, return InvalidOid if the role does not exist. |
5219 | * |
5220 | * PUBLIC is always disallowed here. Routines wanting to handle the PUBLIC |
5221 | * case must check the case separately. |
5222 | */ |
5223 | Oid |
5224 | get_rolespec_oid(const RoleSpec *role, bool missing_ok) |
5225 | { |
5226 | Oid oid; |
5227 | |
5228 | switch (role->roletype) |
5229 | { |
5230 | case ROLESPEC_CSTRING: |
5231 | Assert(role->rolename); |
5232 | oid = get_role_oid(role->rolename, missing_ok); |
5233 | break; |
5234 | |
5235 | case ROLESPEC_CURRENT_USER: |
5236 | oid = GetUserId(); |
5237 | break; |
5238 | |
5239 | case ROLESPEC_SESSION_USER: |
5240 | oid = GetSessionUserId(); |
5241 | break; |
5242 | |
5243 | case ROLESPEC_PUBLIC: |
5244 | ereport(ERROR, |
5245 | (errcode(ERRCODE_UNDEFINED_OBJECT), |
5246 | errmsg("role \"%s\" does not exist" , "public" ))); |
5247 | oid = InvalidOid; /* make compiler happy */ |
5248 | break; |
5249 | |
5250 | default: |
5251 | elog(ERROR, "unexpected role type %d" , role->roletype); |
5252 | } |
5253 | |
5254 | return oid; |
5255 | } |
5256 | |
5257 | /* |
5258 | * Given a RoleSpec node, return the pg_authid HeapTuple it corresponds to. |
5259 | * Caller must ReleaseSysCache when done with the result tuple. |
5260 | */ |
5261 | HeapTuple |
5262 | get_rolespec_tuple(const RoleSpec *role) |
5263 | { |
5264 | HeapTuple tuple; |
5265 | |
5266 | switch (role->roletype) |
5267 | { |
5268 | case ROLESPEC_CSTRING: |
5269 | Assert(role->rolename); |
5270 | tuple = SearchSysCache1(AUTHNAME, CStringGetDatum(role->rolename)); |
5271 | if (!HeapTupleIsValid(tuple)) |
5272 | ereport(ERROR, |
5273 | (errcode(ERRCODE_UNDEFINED_OBJECT), |
5274 | errmsg("role \"%s\" does not exist" , role->rolename))); |
5275 | break; |
5276 | |
5277 | case ROLESPEC_CURRENT_USER: |
5278 | tuple = SearchSysCache1(AUTHOID, GetUserId()); |
5279 | if (!HeapTupleIsValid(tuple)) |
5280 | elog(ERROR, "cache lookup failed for role %u" , GetUserId()); |
5281 | break; |
5282 | |
5283 | case ROLESPEC_SESSION_USER: |
5284 | tuple = SearchSysCache1(AUTHOID, GetSessionUserId()); |
5285 | if (!HeapTupleIsValid(tuple)) |
5286 | elog(ERROR, "cache lookup failed for role %u" , GetSessionUserId()); |
5287 | break; |
5288 | |
5289 | case ROLESPEC_PUBLIC: |
5290 | ereport(ERROR, |
5291 | (errcode(ERRCODE_UNDEFINED_OBJECT), |
5292 | errmsg("role \"%s\" does not exist" , "public" ))); |
5293 | tuple = NULL; /* make compiler happy */ |
5294 | break; |
5295 | |
5296 | default: |
5297 | elog(ERROR, "unexpected role type %d" , role->roletype); |
5298 | } |
5299 | |
5300 | return tuple; |
5301 | } |
5302 | |
5303 | /* |
5304 | * Given a RoleSpec, returns a palloc'ed copy of the corresponding role's name. |
5305 | */ |
5306 | char * |
5307 | get_rolespec_name(const RoleSpec *role) |
5308 | { |
5309 | HeapTuple tp; |
5310 | Form_pg_authid authForm; |
5311 | char *rolename; |
5312 | |
5313 | tp = get_rolespec_tuple(role); |
5314 | authForm = (Form_pg_authid) GETSTRUCT(tp); |
5315 | rolename = pstrdup(NameStr(authForm->rolname)); |
5316 | ReleaseSysCache(tp); |
5317 | |
5318 | return rolename; |
5319 | } |
5320 | |
5321 | /* |
5322 | * Given a RoleSpec, throw an error if the name is reserved, using detail_msg, |
5323 | * if provided. |
5324 | * |
5325 | * If node is NULL, no error is thrown. If detail_msg is NULL then no detail |
5326 | * message is provided. |
5327 | */ |
5328 | void |
5329 | check_rolespec_name(const RoleSpec *role, const char *detail_msg) |
5330 | { |
5331 | if (!role) |
5332 | return; |
5333 | |
5334 | if (role->roletype != ROLESPEC_CSTRING) |
5335 | return; |
5336 | |
5337 | if (IsReservedName(role->rolename)) |
5338 | { |
5339 | if (detail_msg) |
5340 | ereport(ERROR, |
5341 | (errcode(ERRCODE_RESERVED_NAME), |
5342 | errmsg("role name \"%s\" is reserved" , |
5343 | role->rolename), |
5344 | errdetail("%s" , detail_msg))); |
5345 | else |
5346 | ereport(ERROR, |
5347 | (errcode(ERRCODE_RESERVED_NAME), |
5348 | errmsg("role name \"%s\" is reserved" , |
5349 | role->rolename))); |
5350 | } |
5351 | } |
5352 | |