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