Branch data Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * user.c
4 : : * Commands for manipulating roles (formerly called users).
5 : : *
6 : : * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
7 : : * Portions Copyright (c) 1994, Regents of the University of California
8 : : *
9 : : * src/backend/commands/user.c
10 : : *
11 : : *-------------------------------------------------------------------------
12 : : */
13 : : #include "postgres.h"
14 : :
15 : : #include "access/genam.h"
16 : : #include "access/htup_details.h"
17 : : #include "access/table.h"
18 : : #include "access/xact.h"
19 : : #include "catalog/binary_upgrade.h"
20 : : #include "catalog/catalog.h"
21 : : #include "catalog/dependency.h"
22 : : #include "catalog/indexing.h"
23 : : #include "catalog/objectaccess.h"
24 : : #include "catalog/pg_auth_members.h"
25 : : #include "catalog/pg_authid.h"
26 : : #include "catalog/pg_database.h"
27 : : #include "catalog/pg_db_role_setting.h"
28 : : #include "commands/comment.h"
29 : : #include "commands/dbcommands.h"
30 : : #include "commands/defrem.h"
31 : : #include "commands/seclabel.h"
32 : : #include "commands/user.h"
33 : : #include "libpq/crypt.h"
34 : : #include "miscadmin.h"
35 : : #include "port/pg_bitutils.h"
36 : : #include "storage/lmgr.h"
37 : : #include "utils/acl.h"
38 : : #include "utils/builtins.h"
39 : : #include "utils/catcache.h"
40 : : #include "utils/fmgroids.h"
41 : : #include "utils/syscache.h"
42 : : #include "utils/varlena.h"
43 : :
44 : : /*
45 : : * Removing a role grant - or the admin option on it - might recurse to
46 : : * dependent grants. We use these values to reason about what would need to
47 : : * be done in such cases.
48 : : *
49 : : * RRG_NOOP indicates a grant that would not need to be altered by the
50 : : * operation.
51 : : *
52 : : * RRG_REMOVE_ADMIN_OPTION indicates a grant that would need to have
53 : : * admin_option set to false by the operation.
54 : : *
55 : : * Similarly, RRG_REMOVE_INHERIT_OPTION and RRG_REMOVE_SET_OPTION indicate
56 : : * grants that would need to have the corresponding options set to false.
57 : : *
58 : : * RRG_DELETE_GRANT indicates a grant that would need to be removed entirely
59 : : * by the operation.
60 : : */
61 : : typedef enum
62 : : {
63 : : RRG_NOOP,
64 : : RRG_REMOVE_ADMIN_OPTION,
65 : : RRG_REMOVE_INHERIT_OPTION,
66 : : RRG_REMOVE_SET_OPTION,
67 : : RRG_DELETE_GRANT,
68 : : } RevokeRoleGrantAction;
69 : :
70 : : /* Potentially set by pg_upgrade_support functions */
71 : : Oid binary_upgrade_next_pg_authid_oid = InvalidOid;
72 : :
73 : : typedef struct
74 : : {
75 : : unsigned specified;
76 : : bool admin;
77 : : bool inherit;
78 : : bool set;
79 : : } GrantRoleOptions;
80 : :
81 : : #define GRANT_ROLE_SPECIFIED_ADMIN 0x0001
82 : : #define GRANT_ROLE_SPECIFIED_INHERIT 0x0002
83 : : #define GRANT_ROLE_SPECIFIED_SET 0x0004
84 : :
85 : : /* GUC parameters */
86 : : int Password_encryption = PASSWORD_TYPE_SCRAM_SHA_256;
87 : : char *createrole_self_grant = "";
88 : : static bool createrole_self_grant_enabled = false;
89 : : static GrantRoleOptions createrole_self_grant_options;
90 : :
91 : : /* Hook to check passwords in CreateRole() and AlterRole() */
92 : : check_password_hook_type check_password_hook = NULL;
93 : :
94 : : static void AddRoleMems(Oid currentUserId, const char *rolename, Oid roleid,
95 : : List *memberSpecs, List *memberIds,
96 : : Oid grantorId, GrantRoleOptions *popt);
97 : : static void DelRoleMems(Oid currentUserId, const char *rolename, Oid roleid,
98 : : List *memberSpecs, List *memberIds,
99 : : Oid grantorId, GrantRoleOptions *popt,
100 : : DropBehavior behavior);
101 : : static void check_role_membership_authorization(Oid currentUserId, Oid roleid,
102 : : bool is_grant);
103 : : static Oid check_role_grantor(Oid currentUserId, Oid roleid, Oid grantorId,
104 : : bool is_grant);
105 : : static RevokeRoleGrantAction *initialize_revoke_actions(CatCList *memlist);
106 : : static bool plan_single_revoke(CatCList *memlist,
107 : : RevokeRoleGrantAction *actions,
108 : : Oid member, Oid grantor,
109 : : GrantRoleOptions *popt,
110 : : DropBehavior behavior);
111 : : static void plan_member_revoke(CatCList *memlist,
112 : : RevokeRoleGrantAction *actions, Oid member);
113 : : static void plan_recursive_revoke(CatCList *memlist,
114 : : RevokeRoleGrantAction *actions,
115 : : int index,
116 : : bool revoke_admin_option_only,
117 : : DropBehavior behavior);
118 : : static void InitGrantRoleOptions(GrantRoleOptions *popt);
119 : :
120 : :
121 : : /* Check if current user has createrole privileges */
122 : : static bool
123 : 315 : have_createrole_privilege(void)
124 : : {
125 : 315 : return has_createrole_privilege(GetUserId());
126 : : }
127 : :
128 : :
129 : : /*
130 : : * CREATE ROLE
131 : : */
132 : : Oid
133 : 236 : CreateRole(ParseState *pstate, CreateRoleStmt *stmt)
134 : : {
135 : 236 : Relation pg_authid_rel;
136 : 236 : TupleDesc pg_authid_dsc;
137 : 236 : HeapTuple tuple;
138 : 236 : Datum new_record[Natts_pg_authid] = {0};
139 : 236 : bool new_record_nulls[Natts_pg_authid] = {0};
140 : 236 : Oid currentUserId = GetUserId();
141 : 236 : Oid roleid;
142 : 236 : ListCell *item;
143 : 236 : ListCell *option;
144 : 236 : char *password = NULL; /* user password */
145 : 236 : bool issuper = false; /* Make the user a superuser? */
146 : 236 : bool inherit = true; /* Auto inherit privileges? */
147 : 236 : bool createrole = false; /* Can this user create roles? */
148 : 236 : bool createdb = false; /* Can the user create databases? */
149 : 236 : bool canlogin = false; /* Can this user login? */
150 : 236 : bool isreplication = false; /* Is this a replication role? */
151 : 236 : bool bypassrls = false; /* Is this a row security enabled role? */
152 : 236 : int connlimit = -1; /* maximum connections allowed */
153 : 236 : List *addroleto = NIL; /* roles to make this a member of */
154 : 236 : List *rolemembers = NIL; /* roles to be members of this role */
155 : 236 : List *adminmembers = NIL; /* roles to be admins of this role */
156 : 236 : char *validUntil = NULL; /* time the login is valid until */
157 : 236 : Datum validUntil_datum; /* same, as timestamptz Datum */
158 : 236 : bool validUntil_null;
159 : 236 : DefElem *dpassword = NULL;
160 : 236 : DefElem *dissuper = NULL;
161 : 236 : DefElem *dinherit = NULL;
162 : 236 : DefElem *dcreaterole = NULL;
163 : 236 : DefElem *dcreatedb = NULL;
164 : 236 : DefElem *dcanlogin = NULL;
165 : 236 : DefElem *disreplication = NULL;
166 : 236 : DefElem *dconnlimit = NULL;
167 : 236 : DefElem *daddroleto = NULL;
168 : 236 : DefElem *drolemembers = NULL;
169 : 236 : DefElem *dadminmembers = NULL;
170 : 236 : DefElem *dvalidUntil = NULL;
171 : 236 : DefElem *dbypassRLS = NULL;
172 : 236 : GrantRoleOptions popt;
173 : :
174 : : /* The defaults can vary depending on the original statement type */
175 [ + + ]: 236 : switch (stmt->stmt_type)
176 : : {
177 : : case ROLESTMT_ROLE:
178 : : break;
179 : : case ROLESTMT_USER:
180 : 62 : canlogin = true;
181 : : /* may eventually want inherit to default to false here */
182 : 62 : break;
183 : : case ROLESTMT_GROUP:
184 : : break;
185 : : }
186 : :
187 : : /* Extract options from the statement node tree */
188 [ + + + + : 341 : foreach(option, stmt->options)
+ + ]
189 : : {
190 : 105 : DefElem *defel = (DefElem *) lfirst(option);
191 : :
192 [ + + ]: 105 : if (strcmp(defel->defname, "password") == 0)
193 : : {
194 [ - + ]: 14 : if (dpassword)
195 : 0 : errorConflictingDefElem(defel, pstate);
196 : 14 : dpassword = defel;
197 : 14 : }
198 [ + + ]: 91 : else if (strcmp(defel->defname, "sysid") == 0)
199 : : {
200 [ - + + - ]: 1 : ereport(NOTICE,
201 : : (errmsg("SYSID can no longer be specified")));
202 : 1 : }
203 [ + + ]: 90 : else if (strcmp(defel->defname, "superuser") == 0)
204 : : {
205 [ - + ]: 15 : if (dissuper)
206 : 0 : errorConflictingDefElem(defel, pstate);
207 : 15 : dissuper = defel;
208 : 15 : }
209 [ + + ]: 75 : else if (strcmp(defel->defname, "inherit") == 0)
210 : : {
211 [ + - ]: 4 : if (dinherit)
212 : 0 : errorConflictingDefElem(defel, pstate);
213 : 4 : dinherit = defel;
214 : 4 : }
215 [ + + ]: 71 : else if (strcmp(defel->defname, "createrole") == 0)
216 : : {
217 [ + - ]: 8 : if (dcreaterole)
218 : 0 : errorConflictingDefElem(defel, pstate);
219 : 8 : dcreaterole = defel;
220 : 8 : }
221 [ + + ]: 63 : else if (strcmp(defel->defname, "createdb") == 0)
222 : : {
223 [ + - ]: 5 : if (dcreatedb)
224 : 0 : errorConflictingDefElem(defel, pstate);
225 : 5 : dcreatedb = defel;
226 : 5 : }
227 [ + + ]: 58 : else if (strcmp(defel->defname, "canlogin") == 0)
228 : : {
229 [ - + ]: 21 : if (dcanlogin)
230 : 0 : errorConflictingDefElem(defel, pstate);
231 : 21 : dcanlogin = defel;
232 : 21 : }
233 [ + + ]: 37 : else if (strcmp(defel->defname, "isreplication") == 0)
234 : : {
235 [ + - ]: 6 : if (disreplication)
236 : 0 : errorConflictingDefElem(defel, pstate);
237 : 6 : disreplication = defel;
238 : 6 : }
239 [ + + ]: 31 : else if (strcmp(defel->defname, "connectionlimit") == 0)
240 : : {
241 [ + - ]: 2 : if (dconnlimit)
242 : 0 : errorConflictingDefElem(defel, pstate);
243 : 2 : dconnlimit = defel;
244 : 2 : }
245 [ + + ]: 29 : else if (strcmp(defel->defname, "addroleto") == 0)
246 : : {
247 [ + - ]: 15 : if (daddroleto)
248 : 0 : errorConflictingDefElem(defel, pstate);
249 : 15 : daddroleto = defel;
250 : 15 : }
251 [ + + ]: 14 : else if (strcmp(defel->defname, "rolemembers") == 0)
252 : : {
253 [ + - ]: 4 : if (drolemembers)
254 : 0 : errorConflictingDefElem(defel, pstate);
255 : 4 : drolemembers = defel;
256 : 4 : }
257 [ + + ]: 10 : else if (strcmp(defel->defname, "adminmembers") == 0)
258 : : {
259 [ - + ]: 3 : if (dadminmembers)
260 : 0 : errorConflictingDefElem(defel, pstate);
261 : 3 : dadminmembers = defel;
262 : 3 : }
263 [ + - ]: 7 : else if (strcmp(defel->defname, "validUntil") == 0)
264 : : {
265 [ # # ]: 0 : if (dvalidUntil)
266 : 0 : errorConflictingDefElem(defel, pstate);
267 : 0 : dvalidUntil = defel;
268 : 0 : }
269 [ + - ]: 7 : else if (strcmp(defel->defname, "bypassrls") == 0)
270 : : {
271 [ + - ]: 7 : if (dbypassRLS)
272 : 0 : errorConflictingDefElem(defel, pstate);
273 : 7 : dbypassRLS = defel;
274 : 7 : }
275 : : else
276 [ # # # # ]: 0 : elog(ERROR, "option \"%s\" not recognized",
277 : : defel->defname);
278 : 105 : }
279 : :
280 [ + + + + ]: 236 : if (dpassword && dpassword->arg)
281 : 12 : password = strVal(dpassword->arg);
282 [ + + ]: 236 : if (dissuper)
283 : 15 : issuper = boolVal(dissuper->arg);
284 [ + + ]: 236 : if (dinherit)
285 : 4 : inherit = boolVal(dinherit->arg);
286 [ + + ]: 236 : if (dcreaterole)
287 : 8 : createrole = boolVal(dcreaterole->arg);
288 [ + + ]: 236 : if (dcreatedb)
289 : 5 : createdb = boolVal(dcreatedb->arg);
290 [ + + ]: 236 : if (dcanlogin)
291 : 21 : canlogin = boolVal(dcanlogin->arg);
292 [ + + ]: 236 : if (disreplication)
293 : 6 : isreplication = boolVal(disreplication->arg);
294 [ + + ]: 236 : if (dconnlimit)
295 : : {
296 : 2 : connlimit = intVal(dconnlimit->arg);
297 [ + - ]: 2 : if (connlimit < -1)
298 [ # # # # ]: 0 : ereport(ERROR,
299 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
300 : : errmsg("invalid connection limit: %d", connlimit)));
301 : 2 : }
302 [ + + ]: 236 : if (daddroleto)
303 : 15 : addroleto = (List *) daddroleto->arg;
304 [ + + ]: 236 : if (drolemembers)
305 : 4 : rolemembers = (List *) drolemembers->arg;
306 [ + + ]: 236 : if (dadminmembers)
307 : 3 : adminmembers = (List *) dadminmembers->arg;
308 [ + - ]: 236 : if (dvalidUntil)
309 : 0 : validUntil = strVal(dvalidUntil->arg);
310 [ + + ]: 236 : if (dbypassRLS)
311 : 7 : bypassrls = boolVal(dbypassRLS->arg);
312 : :
313 : : /* Check some permissions first */
314 [ + + ]: 236 : if (!superuser_arg(currentUserId))
315 : : {
316 [ + - ]: 37 : if (!has_createrole_privilege(currentUserId))
317 [ # # # # ]: 0 : ereport(ERROR,
318 : : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
319 : : errmsg("permission denied to create role"),
320 : : errdetail("Only roles with the %s attribute may create roles.",
321 : : "CREATEROLE")));
322 [ + + ]: 37 : if (issuper)
323 [ + - + - ]: 1 : ereport(ERROR,
324 : : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
325 : : errmsg("permission denied to create role"),
326 : : errdetail("Only roles with the %s attribute may create roles with the %s attribute.",
327 : : "SUPERUSER", "SUPERUSER")));
328 [ + + + + ]: 36 : if (createdb && !have_createdb_privilege())
329 [ + - + - ]: 1 : ereport(ERROR,
330 : : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
331 : : errmsg("permission denied to create role"),
332 : : errdetail("Only roles with the %s attribute may create roles with the %s attribute.",
333 : : "CREATEDB", "CREATEDB")));
334 [ + + + + ]: 35 : if (isreplication && !has_rolreplication(currentUserId))
335 [ + - + - ]: 2 : ereport(ERROR,
336 : : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
337 : : errmsg("permission denied to create role"),
338 : : errdetail("Only roles with the %s attribute may create roles with the %s attribute.",
339 : : "REPLICATION", "REPLICATION")));
340 [ + + + + ]: 33 : if (bypassrls && !has_bypassrls_privilege(currentUserId))
341 [ + - + - ]: 1 : ereport(ERROR,
342 : : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
343 : : errmsg("permission denied to create role"),
344 : : errdetail("Only roles with the %s attribute may create roles with the %s attribute.",
345 : : "BYPASSRLS", "BYPASSRLS")));
346 : 32 : }
347 : :
348 : : /*
349 : : * Check that the user is not trying to create a role in the reserved
350 : : * "pg_" namespace.
351 : : */
352 [ + - ]: 231 : if (IsReservedName(stmt->role))
353 [ # # # # ]: 0 : ereport(ERROR,
354 : : (errcode(ERRCODE_RESERVED_NAME),
355 : : errmsg("role name \"%s\" is reserved",
356 : : stmt->role),
357 : : errdetail("Role names starting with \"pg_\" are reserved.")));
358 : :
359 : : /*
360 : : * If built with appropriate switch, whine when regression-testing
361 : : * conventions for role names are violated.
362 : : */
363 : : #ifdef ENFORCE_REGRESSION_TEST_NAME_RESTRICTIONS
364 : : if (strncmp(stmt->role, "regress_", 8) != 0)
365 : : elog(WARNING, "roles created by regression test cases should have names starting with \"regress_\"");
366 : : #endif
367 : :
368 : : /*
369 : : * Check the pg_authid relation to be certain the role doesn't already
370 : : * exist.
371 : : */
372 : 231 : pg_authid_rel = table_open(AuthIdRelationId, RowExclusiveLock);
373 : 231 : pg_authid_dsc = RelationGetDescr(pg_authid_rel);
374 : :
375 [ + + ]: 231 : if (OidIsValid(get_role_oid(stmt->role, true)))
376 [ + - + - ]: 1 : ereport(ERROR,
377 : : (errcode(ERRCODE_DUPLICATE_OBJECT),
378 : : errmsg("role \"%s\" already exists",
379 : : stmt->role)));
380 : :
381 : : /* Convert validuntil to internal form */
382 [ - + ]: 230 : if (validUntil)
383 : : {
384 : 0 : validUntil_datum = DirectFunctionCall3(timestamptz_in,
385 : : CStringGetDatum(validUntil),
386 : : ObjectIdGetDatum(InvalidOid),
387 : : Int32GetDatum(-1));
388 : 0 : validUntil_null = false;
389 : 0 : }
390 : : else
391 : : {
392 : 230 : validUntil_datum = (Datum) 0;
393 : 230 : validUntil_null = true;
394 : : }
395 : :
396 : : /*
397 : : * Call the password checking hook if there is one defined
398 : : */
399 [ - + # # ]: 230 : if (check_password_hook && password)
400 : 0 : (*check_password_hook) (stmt->role,
401 : 0 : password,
402 : 0 : get_password_type(password),
403 : 0 : validUntil_datum,
404 : 0 : validUntil_null);
405 : :
406 : : /*
407 : : * Build a tuple to insert
408 : : */
409 : 230 : new_record[Anum_pg_authid_rolname - 1] =
410 : 230 : DirectFunctionCall1(namein, CStringGetDatum(stmt->role));
411 : 230 : new_record[Anum_pg_authid_rolsuper - 1] = BoolGetDatum(issuper);
412 : 230 : new_record[Anum_pg_authid_rolinherit - 1] = BoolGetDatum(inherit);
413 : 230 : new_record[Anum_pg_authid_rolcreaterole - 1] = BoolGetDatum(createrole);
414 : 230 : new_record[Anum_pg_authid_rolcreatedb - 1] = BoolGetDatum(createdb);
415 : 230 : new_record[Anum_pg_authid_rolcanlogin - 1] = BoolGetDatum(canlogin);
416 : 230 : new_record[Anum_pg_authid_rolreplication - 1] = BoolGetDatum(isreplication);
417 : 230 : new_record[Anum_pg_authid_rolconnlimit - 1] = Int32GetDatum(connlimit);
418 : :
419 [ + + ]: 230 : if (password)
420 : : {
421 : 12 : char *shadow_pass;
422 : 12 : const char *logdetail = NULL;
423 : :
424 : : /*
425 : : * Don't allow an empty password. Libpq treats an empty password the
426 : : * same as no password at all, and won't even try to authenticate. But
427 : : * other clients might, so allowing it would be confusing. By clearing
428 : : * the password when an empty string is specified, the account is
429 : : * consistently locked for all clients.
430 : : *
431 : : * Note that this only covers passwords stored in the database itself.
432 : : * There are also checks in the authentication code, to forbid an
433 : : * empty password from being used with authentication methods that
434 : : * fetch the password from an external system, like LDAP or PAM.
435 : : */
436 [ + + - + ]: 12 : if (password[0] == '\0' ||
437 : 11 : plain_crypt_verify(stmt->role, password, "", &logdetail) == STATUS_OK)
438 : : {
439 [ - + + - ]: 1 : ereport(NOTICE,
440 : : (errmsg("empty string is not a valid password, clearing password")));
441 : 1 : new_record_nulls[Anum_pg_authid_rolpassword - 1] = true;
442 : 1 : }
443 : : else
444 : : {
445 : : /* Encrypt the password to the requested format. */
446 : 22 : shadow_pass = encrypt_password(Password_encryption, stmt->role,
447 : 11 : password);
448 : 11 : new_record[Anum_pg_authid_rolpassword - 1] =
449 : 11 : CStringGetTextDatum(shadow_pass);
450 : : }
451 : 12 : }
452 : : else
453 : 218 : new_record_nulls[Anum_pg_authid_rolpassword - 1] = true;
454 : :
455 : 230 : new_record[Anum_pg_authid_rolvaliduntil - 1] = validUntil_datum;
456 : 230 : new_record_nulls[Anum_pg_authid_rolvaliduntil - 1] = validUntil_null;
457 : :
458 : 230 : new_record[Anum_pg_authid_rolbypassrls - 1] = BoolGetDatum(bypassrls);
459 : :
460 : : /*
461 : : * pg_largeobject_metadata contains pg_authid.oid's, so we use the
462 : : * binary-upgrade override.
463 : : */
464 [ - + ]: 230 : if (IsBinaryUpgrade)
465 : : {
466 [ # # ]: 0 : if (!OidIsValid(binary_upgrade_next_pg_authid_oid))
467 [ # # # # ]: 0 : ereport(ERROR,
468 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
469 : : errmsg("pg_authid OID value not set when in binary upgrade mode")));
470 : :
471 : 0 : roleid = binary_upgrade_next_pg_authid_oid;
472 : 0 : binary_upgrade_next_pg_authid_oid = InvalidOid;
473 : 0 : }
474 : : else
475 : : {
476 : 230 : roleid = GetNewOidWithIndex(pg_authid_rel, AuthIdOidIndexId,
477 : : Anum_pg_authid_oid);
478 : : }
479 : :
480 : 230 : new_record[Anum_pg_authid_oid - 1] = ObjectIdGetDatum(roleid);
481 : :
482 : 230 : tuple = heap_form_tuple(pg_authid_dsc, new_record, new_record_nulls);
483 : :
484 : : /*
485 : : * Insert new record in the pg_authid table
486 : : */
487 : 230 : CatalogTupleInsert(pg_authid_rel, tuple);
488 : :
489 : : /*
490 : : * Advance command counter so we can see new record; else tests in
491 : : * AddRoleMems may fail.
492 : : */
493 [ + + + + : 230 : if (addroleto || adminmembers || rolemembers)
+ + ]
494 : 21 : CommandCounterIncrement();
495 : :
496 : : /* Default grant. */
497 : 230 : InitGrantRoleOptions(&popt);
498 : :
499 : : /*
500 : : * Add the new role to the specified existing roles.
501 : : */
502 [ + + ]: 230 : if (addroleto)
503 : : {
504 : 3 : RoleSpec *thisrole = makeNode(RoleSpec);
505 : 3 : List *thisrole_list = list_make1(thisrole);
506 : 3 : List *thisrole_oidlist = list_make1_oid(roleid);
507 : :
508 : 3 : thisrole->roletype = ROLESPEC_CSTRING;
509 : 3 : thisrole->rolename = stmt->role;
510 : 3 : thisrole->location = -1;
511 : :
512 [ + - + + : 18 : foreach(item, addroleto)
+ + ]
513 : : {
514 : 15 : RoleSpec *oldrole = lfirst(item);
515 : 15 : HeapTuple oldroletup = get_rolespec_tuple(oldrole);
516 : 15 : Form_pg_authid oldroleform = (Form_pg_authid) GETSTRUCT(oldroletup);
517 : 15 : Oid oldroleid = oldroleform->oid;
518 : 15 : char *oldrolename = NameStr(oldroleform->rolname);
519 : :
520 : : /* can only add this role to roles for which you have rights */
521 : 15 : check_role_membership_authorization(currentUserId, oldroleid, true);
522 : 30 : AddRoleMems(currentUserId, oldrolename, oldroleid,
523 : 15 : thisrole_list,
524 : 15 : thisrole_oidlist,
525 : : InvalidOid, &popt);
526 : :
527 : 15 : ReleaseSysCache(oldroletup);
528 : 15 : }
529 : 3 : }
530 : :
531 : : /*
532 : : * If the current user isn't a superuser, make them an admin of the new
533 : : * role so that they can administer the new object they just created.
534 : : * Superusers will be able to do that anyway.
535 : : *
536 : : * The grantor of record for this implicit grant is the bootstrap
537 : : * superuser, which means that the CREATEROLE user cannot revoke the
538 : : * grant. They can however grant the created role back to themselves with
539 : : * different options, since they enjoy ADMIN OPTION on it.
540 : : */
541 [ + + ]: 230 : if (!superuser())
542 : : {
543 : 20 : RoleSpec *current_role = makeNode(RoleSpec);
544 : 20 : GrantRoleOptions poptself;
545 : 20 : List *memberSpecs;
546 : 20 : List *memberIds = list_make1_oid(currentUserId);
547 : :
548 : 20 : current_role->roletype = ROLESPEC_CURRENT_ROLE;
549 : 20 : current_role->location = -1;
550 : 20 : memberSpecs = list_make1(current_role);
551 : :
552 : 20 : poptself.specified = GRANT_ROLE_SPECIFIED_ADMIN
553 : : | GRANT_ROLE_SPECIFIED_INHERIT
554 : : | GRANT_ROLE_SPECIFIED_SET;
555 : 20 : poptself.admin = true;
556 : 20 : poptself.inherit = false;
557 : 20 : poptself.set = false;
558 : :
559 : 40 : AddRoleMems(BOOTSTRAP_SUPERUSERID, stmt->role, roleid,
560 : 20 : memberSpecs, memberIds,
561 : : BOOTSTRAP_SUPERUSERID, &poptself);
562 : :
563 : : /*
564 : : * We must make the implicit grant visible to the code below, else the
565 : : * additional grants will fail.
566 : : */
567 : 20 : CommandCounterIncrement();
568 : :
569 : : /*
570 : : * Because of the implicit grant above, a CREATEROLE user who creates
571 : : * a role has the ability to grant that role back to themselves with
572 : : * the INHERIT or SET options, if they wish to inherit the role's
573 : : * privileges or be able to SET ROLE to it. The createrole_self_grant
574 : : * GUC can be used to make this happen automatically. This has no
575 : : * security implications since the same user is able to make the same
576 : : * grant using an explicit GRANT statement; it's just convenient.
577 : : */
578 [ + + ]: 20 : if (createrole_self_grant_enabled)
579 : 2 : AddRoleMems(currentUserId, stmt->role, roleid,
580 : 1 : memberSpecs, memberIds,
581 : 1 : currentUserId, &createrole_self_grant_options);
582 : 20 : }
583 : :
584 : : /*
585 : : * Add the specified members to this new role. adminmembers get the admin
586 : : * option, rolemembers don't.
587 : : *
588 : : * NB: No permissions check is required here. If you have enough rights to
589 : : * create a role, you can add any members you like.
590 : : */
591 : 460 : AddRoleMems(currentUserId, stmt->role, roleid,
592 : 230 : rolemembers, roleSpecsToIds(rolemembers),
593 : : InvalidOid, &popt);
594 : 230 : popt.specified |= GRANT_ROLE_SPECIFIED_ADMIN;
595 : 230 : popt.admin = true;
596 : 460 : AddRoleMems(currentUserId, stmt->role, roleid,
597 : 230 : adminmembers, roleSpecsToIds(adminmembers),
598 : : InvalidOid, &popt);
599 : :
600 : : /* Post creation hook for new role */
601 [ + - ]: 230 : InvokeObjectPostCreateHook(AuthIdRelationId, roleid, 0);
602 : :
603 : : /*
604 : : * Close pg_authid, but keep lock till commit.
605 : : */
606 : 230 : table_close(pg_authid_rel, NoLock);
607 : :
608 : 460 : return roleid;
609 : 230 : }
610 : :
611 : :
612 : : /*
613 : : * ALTER ROLE
614 : : *
615 : : * Note: the rolemembers option accepted here is intended to support the
616 : : * backwards-compatible ALTER GROUP syntax. Although it will work to say
617 : : * "ALTER ROLE role ROLE rolenames", we don't document it.
618 : : */
619 : : Oid
620 : 50 : AlterRole(ParseState *pstate, AlterRoleStmt *stmt)
621 : : {
622 : 50 : Datum new_record[Natts_pg_authid] = {0};
623 : 50 : bool new_record_nulls[Natts_pg_authid] = {0};
624 : 50 : bool new_record_repl[Natts_pg_authid] = {0};
625 : 50 : Relation pg_authid_rel;
626 : 50 : TupleDesc pg_authid_dsc;
627 : 50 : HeapTuple tuple,
628 : : new_tuple;
629 : 50 : Form_pg_authid authform;
630 : 50 : ListCell *option;
631 : 50 : char *rolename;
632 : 50 : char *password = NULL; /* user password */
633 : 50 : int connlimit = -1; /* maximum connections allowed */
634 : 50 : char *validUntil = NULL; /* time the login is valid until */
635 : 50 : Datum validUntil_datum; /* same, as timestamptz Datum */
636 : 50 : bool validUntil_null;
637 : 50 : DefElem *dpassword = NULL;
638 : 50 : DefElem *dissuper = NULL;
639 : 50 : DefElem *dinherit = NULL;
640 : 50 : DefElem *dcreaterole = NULL;
641 : 50 : DefElem *dcreatedb = NULL;
642 : 50 : DefElem *dcanlogin = NULL;
643 : 50 : DefElem *disreplication = NULL;
644 : 50 : DefElem *dconnlimit = NULL;
645 : 50 : DefElem *drolemembers = NULL;
646 : 50 : DefElem *dvalidUntil = NULL;
647 : 50 : DefElem *dbypassRLS = NULL;
648 : 50 : Oid roleid;
649 : 50 : Oid currentUserId = GetUserId();
650 : 50 : GrantRoleOptions popt;
651 : :
652 : 100 : check_rolespec_name(stmt->role,
653 : 50 : _("Cannot alter reserved roles."));
654 : :
655 : : /* Extract options from the statement node tree */
656 [ + - + + : 104 : foreach(option, stmt->options)
+ + ]
657 : : {
658 : 54 : DefElem *defel = (DefElem *) lfirst(option);
659 : :
660 [ + + ]: 54 : if (strcmp(defel->defname, "password") == 0)
661 : : {
662 [ + - ]: 10 : if (dpassword)
663 : 0 : errorConflictingDefElem(defel, pstate);
664 : 10 : dpassword = defel;
665 : 10 : }
666 [ + + ]: 44 : else if (strcmp(defel->defname, "superuser") == 0)
667 : : {
668 [ + - ]: 7 : if (dissuper)
669 : 0 : errorConflictingDefElem(defel, pstate);
670 : 7 : dissuper = defel;
671 : 7 : }
672 [ + + ]: 37 : else if (strcmp(defel->defname, "inherit") == 0)
673 : : {
674 [ + - ]: 5 : if (dinherit)
675 : 0 : errorConflictingDefElem(defel, pstate);
676 : 5 : dinherit = defel;
677 : 5 : }
678 [ + + ]: 32 : else if (strcmp(defel->defname, "createrole") == 0)
679 : : {
680 [ + - ]: 2 : if (dcreaterole)
681 : 0 : errorConflictingDefElem(defel, pstate);
682 : 2 : dcreaterole = defel;
683 : 2 : }
684 [ + + ]: 30 : else if (strcmp(defel->defname, "createdb") == 0)
685 : : {
686 [ + - ]: 5 : if (dcreatedb)
687 : 0 : errorConflictingDefElem(defel, pstate);
688 : 5 : dcreatedb = defel;
689 : 5 : }
690 [ + + ]: 25 : else if (strcmp(defel->defname, "canlogin") == 0)
691 : : {
692 [ + - ]: 6 : if (dcanlogin)
693 : 0 : errorConflictingDefElem(defel, pstate);
694 : 6 : dcanlogin = defel;
695 : 6 : }
696 [ + + ]: 19 : else if (strcmp(defel->defname, "isreplication") == 0)
697 : : {
698 [ - + ]: 5 : if (disreplication)
699 : 0 : errorConflictingDefElem(defel, pstate);
700 : 5 : disreplication = defel;
701 : 5 : }
702 [ + + ]: 14 : else if (strcmp(defel->defname, "connectionlimit") == 0)
703 : : {
704 [ + - ]: 2 : if (dconnlimit)
705 : 0 : errorConflictingDefElem(defel, pstate);
706 : 2 : dconnlimit = defel;
707 : 2 : }
708 [ + + - + ]: 12 : else if (strcmp(defel->defname, "rolemembers") == 0 &&
709 : 7 : stmt->action != 0)
710 : : {
711 [ + - ]: 7 : if (drolemembers)
712 : 0 : errorConflictingDefElem(defel, pstate);
713 : 7 : drolemembers = defel;
714 : 7 : }
715 [ + - ]: 5 : else if (strcmp(defel->defname, "validUntil") == 0)
716 : : {
717 [ # # ]: 0 : if (dvalidUntil)
718 : 0 : errorConflictingDefElem(defel, pstate);
719 : 0 : dvalidUntil = defel;
720 : 0 : }
721 [ - + ]: 5 : else if (strcmp(defel->defname, "bypassrls") == 0)
722 : : {
723 [ - + ]: 5 : if (dbypassRLS)
724 : 0 : errorConflictingDefElem(defel, pstate);
725 : 5 : dbypassRLS = defel;
726 : 5 : }
727 : : else
728 [ # # # # ]: 0 : elog(ERROR, "option \"%s\" not recognized",
729 : : defel->defname);
730 : 54 : }
731 : :
732 [ + + - + ]: 50 : if (dpassword && dpassword->arg)
733 : 10 : password = strVal(dpassword->arg);
734 [ + + ]: 50 : if (dconnlimit)
735 : : {
736 : 2 : connlimit = intVal(dconnlimit->arg);
737 [ + - ]: 2 : if (connlimit < -1)
738 [ # # # # ]: 0 : ereport(ERROR,
739 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
740 : : errmsg("invalid connection limit: %d", connlimit)));
741 : 2 : }
742 [ - + ]: 50 : if (dvalidUntil)
743 : 0 : validUntil = strVal(dvalidUntil->arg);
744 : :
745 : : /*
746 : : * Scan the pg_authid relation to be certain the user exists.
747 : : */
748 : 50 : pg_authid_rel = table_open(AuthIdRelationId, RowExclusiveLock);
749 : 50 : pg_authid_dsc = RelationGetDescr(pg_authid_rel);
750 : :
751 : 50 : tuple = get_rolespec_tuple(stmt->role);
752 : 50 : authform = (Form_pg_authid) GETSTRUCT(tuple);
753 : 50 : rolename = pstrdup(NameStr(authform->rolname));
754 : 50 : roleid = authform->oid;
755 : :
756 : : /* To mess with a superuser in any way you gotta be superuser. */
757 [ + + + - ]: 50 : if (!superuser() && authform->rolsuper)
758 [ # # # # ]: 0 : ereport(ERROR,
759 : : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
760 : : errmsg("permission denied to alter role"),
761 : : errdetail("Only roles with the %s attribute may alter roles with the %s attribute.",
762 : : "SUPERUSER", "SUPERUSER")));
763 [ + + + + ]: 50 : if (!superuser() && dissuper)
764 [ + - + - ]: 3 : ereport(ERROR,
765 : : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
766 : : errmsg("permission denied to alter role"),
767 : : errdetail("Only roles with the %s attribute may change the %s attribute.",
768 : : "SUPERUSER", "SUPERUSER")));
769 : :
770 : : /*
771 : : * Most changes to a role require that you both have CREATEROLE privileges
772 : : * and also ADMIN OPTION on the role.
773 : : */
774 [ + + + + ]: 47 : if (!have_createrole_privilege() ||
775 : 41 : !is_admin_of_role(GetUserId(), roleid))
776 : : {
777 : : /* things an unprivileged user certainly can't do */
778 [ + + ]: 7 : if (dinherit || dcreaterole || dcreatedb || dcanlogin || dconnlimit ||
779 : 6 : dvalidUntil || disreplication || dbypassRLS)
780 [ + - + - ]: 1 : ereport(ERROR,
781 : : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
782 : : errmsg("permission denied to alter role"),
783 : : errdetail("Only roles with the %s attribute and the %s option on role \"%s\" may alter this role.",
784 : : "CREATEROLE", "ADMIN", rolename)));
785 : :
786 : : /* an unprivileged user can change their own password */
787 [ + + - + ]: 6 : if (dpassword && roleid != currentUserId)
788 [ + - + - ]: 1 : ereport(ERROR,
789 : : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
790 : : errmsg("permission denied to alter role"),
791 : : errdetail("To change another role's password, the current user must have the %s attribute and the %s option on the role.",
792 : : "CREATEROLE", "ADMIN")));
793 : 5 : }
794 [ + + ]: 40 : else if (!superuser())
795 : : {
796 : : /*
797 : : * Even if you have both CREATEROLE and ADMIN OPTION on a role, you
798 : : * can only change the CREATEDB, REPLICATION, or BYPASSRLS attributes
799 : : * if they are set for your own role (or you are the superuser).
800 : : */
801 [ + + + + ]: 10 : if (dcreatedb && !have_createdb_privilege())
802 [ + - + - ]: 1 : ereport(ERROR,
803 : : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
804 : : errmsg("permission denied to alter role"),
805 : : errdetail("Only roles with the %s attribute may change the %s attribute.",
806 : : "CREATEDB", "CREATEDB")));
807 [ + + + + ]: 9 : if (disreplication && !has_rolreplication(currentUserId))
808 [ + - + - ]: 1 : ereport(ERROR,
809 : : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
810 : : errmsg("permission denied to alter role"),
811 : : errdetail("Only roles with the %s attribute may change the %s attribute.",
812 : : "REPLICATION", "REPLICATION")));
813 [ + + + + ]: 8 : if (dbypassRLS && !has_bypassrls_privilege(currentUserId))
814 [ + - + - ]: 1 : ereport(ERROR,
815 : : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
816 : : errmsg("permission denied to alter role"),
817 : : errdetail("Only roles with the %s attribute may change the %s attribute.",
818 : : "BYPASSRLS", "BYPASSRLS")));
819 : 7 : }
820 : :
821 : : /* To add or drop members, you need ADMIN OPTION. */
822 [ + + + + ]: 42 : if (drolemembers && !is_admin_of_role(currentUserId, roleid))
823 [ + - + - ]: 2 : ereport(ERROR,
824 : : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
825 : : errmsg("permission denied to alter role"),
826 : : errdetail("Only roles with the %s option on role \"%s\" may add or drop members.",
827 : : "ADMIN", rolename)));
828 : :
829 : : /* Convert validuntil to internal form */
830 [ - + ]: 40 : if (dvalidUntil)
831 : : {
832 : 0 : validUntil_datum = DirectFunctionCall3(timestamptz_in,
833 : : CStringGetDatum(validUntil),
834 : : ObjectIdGetDatum(InvalidOid),
835 : : Int32GetDatum(-1));
836 : 0 : validUntil_null = false;
837 : 0 : }
838 : : else
839 : : {
840 : : /* fetch existing setting in case hook needs it */
841 : 40 : validUntil_datum = SysCacheGetAttr(AUTHNAME, tuple,
842 : : Anum_pg_authid_rolvaliduntil,
843 : : &validUntil_null);
844 : : }
845 : :
846 : : /*
847 : : * Call the password checking hook if there is one defined
848 : : */
849 [ - + # # ]: 40 : if (check_password_hook && password)
850 : 0 : (*check_password_hook) (rolename,
851 : 0 : password,
852 : 0 : get_password_type(password),
853 : 0 : validUntil_datum,
854 : 0 : validUntil_null);
855 : :
856 : : /*
857 : : * Build an updated tuple, perusing the information just obtained
858 : : */
859 : :
860 : : /*
861 : : * issuper/createrole/etc
862 : : */
863 [ + + ]: 40 : if (dissuper)
864 : : {
865 : 4 : bool should_be_super = boolVal(dissuper->arg);
866 : :
867 [ + + + - ]: 4 : if (!should_be_super && roleid == BOOTSTRAP_SUPERUSERID)
868 [ # # # # ]: 0 : ereport(ERROR,
869 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
870 : : errmsg("permission denied to alter role"),
871 : : errdetail("The bootstrap superuser must have the %s attribute.",
872 : : "SUPERUSER")));
873 : :
874 : 4 : new_record[Anum_pg_authid_rolsuper - 1] = BoolGetDatum(should_be_super);
875 : 4 : new_record_repl[Anum_pg_authid_rolsuper - 1] = true;
876 : 4 : }
877 : :
878 [ + + ]: 40 : if (dinherit)
879 : : {
880 : 4 : new_record[Anum_pg_authid_rolinherit - 1] = BoolGetDatum(boolVal(dinherit->arg));
881 : 4 : new_record_repl[Anum_pg_authid_rolinherit - 1] = true;
882 : 4 : }
883 : :
884 [ + + ]: 40 : if (dcreaterole)
885 : : {
886 : 2 : new_record[Anum_pg_authid_rolcreaterole - 1] = BoolGetDatum(boolVal(dcreaterole->arg));
887 : 2 : new_record_repl[Anum_pg_authid_rolcreaterole - 1] = true;
888 : 2 : }
889 : :
890 [ + + ]: 40 : if (dcreatedb)
891 : : {
892 : 4 : new_record[Anum_pg_authid_rolcreatedb - 1] = BoolGetDatum(boolVal(dcreatedb->arg));
893 : 4 : new_record_repl[Anum_pg_authid_rolcreatedb - 1] = true;
894 : 4 : }
895 : :
896 [ + + ]: 40 : if (dcanlogin)
897 : : {
898 : 5 : new_record[Anum_pg_authid_rolcanlogin - 1] = BoolGetDatum(boolVal(dcanlogin->arg));
899 : 5 : new_record_repl[Anum_pg_authid_rolcanlogin - 1] = true;
900 : 5 : }
901 : :
902 [ + + ]: 40 : if (disreplication)
903 : : {
904 : 4 : new_record[Anum_pg_authid_rolreplication - 1] = BoolGetDatum(boolVal(disreplication->arg));
905 : 4 : new_record_repl[Anum_pg_authid_rolreplication - 1] = true;
906 : 4 : }
907 : :
908 [ + + ]: 40 : if (dconnlimit)
909 : : {
910 : 1 : new_record[Anum_pg_authid_rolconnlimit - 1] = Int32GetDatum(connlimit);
911 : 1 : new_record_repl[Anum_pg_authid_rolconnlimit - 1] = true;
912 : 1 : }
913 : :
914 : : /* password */
915 [ + + ]: 40 : if (password)
916 : : {
917 : 9 : char *shadow_pass;
918 : 9 : const char *logdetail = NULL;
919 : :
920 : : /* Like in CREATE USER, don't allow an empty password. */
921 [ + - + + ]: 9 : if (password[0] == '\0' ||
922 : 9 : plain_crypt_verify(rolename, password, "", &logdetail) == STATUS_OK)
923 : : {
924 [ - + + - ]: 2 : ereport(NOTICE,
925 : : (errmsg("empty string is not a valid password, clearing password")));
926 : 2 : new_record_nulls[Anum_pg_authid_rolpassword - 1] = true;
927 : 2 : }
928 : : else
929 : : {
930 : : /* Encrypt the password to the requested format. */
931 : 14 : shadow_pass = encrypt_password(Password_encryption, rolename,
932 : 7 : password);
933 : 7 : new_record[Anum_pg_authid_rolpassword - 1] =
934 : 7 : CStringGetTextDatum(shadow_pass);
935 : : }
936 : 9 : new_record_repl[Anum_pg_authid_rolpassword - 1] = true;
937 : 9 : }
938 : :
939 : : /* unset password */
940 [ + + + - ]: 40 : if (dpassword && dpassword->arg == NULL)
941 : : {
942 : 0 : new_record_repl[Anum_pg_authid_rolpassword - 1] = true;
943 : 0 : new_record_nulls[Anum_pg_authid_rolpassword - 1] = true;
944 : 0 : }
945 : :
946 : : /* valid until */
947 : 40 : new_record[Anum_pg_authid_rolvaliduntil - 1] = validUntil_datum;
948 : 40 : new_record_nulls[Anum_pg_authid_rolvaliduntil - 1] = validUntil_null;
949 : 40 : new_record_repl[Anum_pg_authid_rolvaliduntil - 1] = true;
950 : :
951 [ + + ]: 40 : if (dbypassRLS)
952 : : {
953 : 4 : new_record[Anum_pg_authid_rolbypassrls - 1] = BoolGetDatum(boolVal(dbypassRLS->arg));
954 : 4 : new_record_repl[Anum_pg_authid_rolbypassrls - 1] = true;
955 : 4 : }
956 : :
957 : 80 : new_tuple = heap_modify_tuple(tuple, pg_authid_dsc, new_record,
958 : 40 : new_record_nulls, new_record_repl);
959 : 40 : CatalogTupleUpdate(pg_authid_rel, &tuple->t_self, new_tuple);
960 : :
961 [ + - ]: 40 : InvokeObjectPostAlterHook(AuthIdRelationId, roleid, 0);
962 : :
963 : 40 : ReleaseSysCache(tuple);
964 : 40 : heap_freetuple(new_tuple);
965 : :
966 : 40 : InitGrantRoleOptions(&popt);
967 : :
968 : : /*
969 : : * Advance command counter so we can see new record; else tests in
970 : : * AddRoleMems may fail.
971 : : */
972 [ + + ]: 40 : if (drolemembers)
973 : : {
974 : 5 : List *rolemembers = (List *) drolemembers->arg;
975 : :
976 : 5 : CommandCounterIncrement();
977 : :
978 [ + + ]: 5 : if (stmt->action == +1) /* add members to role */
979 : 6 : AddRoleMems(currentUserId, rolename, roleid,
980 : 3 : rolemembers, roleSpecsToIds(rolemembers),
981 : : InvalidOid, &popt);
982 [ - + ]: 2 : else if (stmt->action == -1) /* drop members from role */
983 : 4 : DelRoleMems(currentUserId, rolename, roleid,
984 : 2 : rolemembers, roleSpecsToIds(rolemembers),
985 : : InvalidOid, &popt, DROP_RESTRICT);
986 : 5 : }
987 : :
988 : : /*
989 : : * Close pg_authid, but keep lock till commit.
990 : : */
991 : 40 : table_close(pg_authid_rel, NoLock);
992 : :
993 : 80 : return roleid;
994 : 40 : }
995 : :
996 : :
997 : : /*
998 : : * ALTER ROLE ... SET
999 : : */
1000 : : Oid
1001 : 1 : AlterRoleSet(AlterRoleSetStmt *stmt)
1002 : : {
1003 : 1 : HeapTuple roletuple;
1004 : 1 : Form_pg_authid roleform;
1005 : 1 : Oid databaseid = InvalidOid;
1006 : 1 : Oid roleid = InvalidOid;
1007 : :
1008 [ - + ]: 1 : if (stmt->role)
1009 : : {
1010 : 2 : check_rolespec_name(stmt->role,
1011 : 1 : _("Cannot alter reserved roles."));
1012 : :
1013 : 1 : roletuple = get_rolespec_tuple(stmt->role);
1014 : 1 : roleform = (Form_pg_authid) GETSTRUCT(roletuple);
1015 : 1 : roleid = roleform->oid;
1016 : :
1017 : : /*
1018 : : * Obtain a lock on the role and make sure it didn't go away in the
1019 : : * meantime.
1020 : : */
1021 : 1 : shdepLockAndCheckObject(AuthIdRelationId, roleid);
1022 : :
1023 : : /*
1024 : : * To mess with a superuser you gotta be superuser; otherwise you need
1025 : : * CREATEROLE plus admin option on the target role; unless you're just
1026 : : * trying to change your own settings
1027 : : */
1028 [ - + ]: 1 : if (roleform->rolsuper)
1029 : : {
1030 [ # # ]: 0 : if (!superuser())
1031 [ # # # # ]: 0 : ereport(ERROR,
1032 : : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1033 : : errmsg("permission denied to alter role"),
1034 : : errdetail("Only roles with the %s attribute may alter roles with the %s attribute.",
1035 : : "SUPERUSER", "SUPERUSER")));
1036 : 0 : }
1037 : : else
1038 : : {
1039 [ + - ]: 1 : if ((!have_createrole_privilege() ||
1040 : 1 : !is_admin_of_role(GetUserId(), roleid))
1041 [ # # ]: 1 : && roleid != GetUserId())
1042 [ # # # # ]: 0 : ereport(ERROR,
1043 : : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1044 : : errmsg("permission denied to alter role"),
1045 : : errdetail("Only roles with the %s attribute and the %s option on role \"%s\" may alter this role.",
1046 : : "CREATEROLE", "ADMIN", NameStr(roleform->rolname))));
1047 : : }
1048 : :
1049 : 1 : ReleaseSysCache(roletuple);
1050 : 1 : }
1051 : :
1052 : : /* look up and lock the database, if specified */
1053 [ + - ]: 1 : if (stmt->database != NULL)
1054 : : {
1055 : 0 : databaseid = get_database_oid(stmt->database, false);
1056 : 0 : shdepLockAndCheckObject(DatabaseRelationId, databaseid);
1057 : :
1058 [ # # ]: 0 : if (!stmt->role)
1059 : : {
1060 : : /*
1061 : : * If no role is specified, then this is effectively the same as
1062 : : * ALTER DATABASE ... SET, so use the same permission check.
1063 : : */
1064 [ # # ]: 0 : if (!object_ownercheck(DatabaseRelationId, databaseid, GetUserId()))
1065 : 0 : aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_DATABASE,
1066 : 0 : stmt->database);
1067 : 0 : }
1068 : 0 : }
1069 : :
1070 [ - + # # ]: 1 : if (!stmt->role && !stmt->database)
1071 : : {
1072 : : /* Must be superuser to alter settings globally. */
1073 [ # # ]: 0 : if (!superuser())
1074 [ # # # # ]: 0 : ereport(ERROR,
1075 : : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1076 : : errmsg("permission denied to alter setting"),
1077 : : errdetail("Only roles with the %s attribute may alter settings globally.",
1078 : : "SUPERUSER")));
1079 : 0 : }
1080 : :
1081 : 1 : AlterSetting(databaseid, roleid, stmt->setstmt);
1082 : :
1083 : 2 : return roleid;
1084 : 1 : }
1085 : :
1086 : :
1087 : : /*
1088 : : * DROP ROLE
1089 : : */
1090 : : void
1091 : 263 : DropRole(DropRoleStmt *stmt)
1092 : : {
1093 : 263 : Relation pg_authid_rel,
1094 : : pg_auth_members_rel;
1095 : 263 : ListCell *item;
1096 : 263 : List *role_oids = NIL;
1097 : :
1098 [ + - ]: 263 : if (!have_createrole_privilege())
1099 [ # # # # ]: 0 : ereport(ERROR,
1100 : : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1101 : : errmsg("permission denied to drop role"),
1102 : : errdetail("Only roles with the %s attribute and the %s option on the target roles may drop roles.",
1103 : : "CREATEROLE", "ADMIN")));
1104 : :
1105 : : /*
1106 : : * Scan the pg_authid relation to find the Oid of the role(s) to be
1107 : : * deleted and perform preliminary permissions and sanity checks.
1108 : : */
1109 : 263 : pg_authid_rel = table_open(AuthIdRelationId, RowExclusiveLock);
1110 : 263 : pg_auth_members_rel = table_open(AuthMemRelationId, RowExclusiveLock);
1111 : :
1112 [ + - + + : 522 : foreach(item, stmt->roles)
+ + ]
1113 : : {
1114 : 277 : RoleSpec *rolspec = lfirst(item);
1115 : 277 : char *role;
1116 : 277 : HeapTuple tuple,
1117 : : tmp_tuple;
1118 : 277 : Form_pg_authid roleform;
1119 : 277 : ScanKeyData scankey;
1120 : 277 : SysScanDesc sscan;
1121 : 277 : Oid roleid;
1122 : :
1123 [ + - ]: 277 : if (rolspec->roletype != ROLESPEC_CSTRING)
1124 [ # # # # ]: 0 : ereport(ERROR,
1125 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1126 : : errmsg("cannot use special role specifier in DROP ROLE")));
1127 : 277 : role = rolspec->rolename;
1128 : :
1129 : 277 : tuple = SearchSysCache1(AUTHNAME, PointerGetDatum(role));
1130 [ + + ]: 277 : if (!HeapTupleIsValid(tuple))
1131 : : {
1132 [ + + ]: 48 : if (!stmt->missing_ok)
1133 : : {
1134 [ + - + - ]: 15 : ereport(ERROR,
1135 : : (errcode(ERRCODE_UNDEFINED_OBJECT),
1136 : : errmsg("role \"%s\" does not exist", role)));
1137 : 0 : }
1138 : : else
1139 : : {
1140 [ - + + + ]: 33 : ereport(NOTICE,
1141 : : (errmsg("role \"%s\" does not exist, skipping",
1142 : : role)));
1143 : : }
1144 : :
1145 : 33 : continue;
1146 : : }
1147 : :
1148 : 229 : roleform = (Form_pg_authid) GETSTRUCT(tuple);
1149 : 229 : roleid = roleform->oid;
1150 : :
1151 [ + + ]: 229 : if (roleid == GetUserId())
1152 [ + - + - ]: 1 : ereport(ERROR,
1153 : : (errcode(ERRCODE_OBJECT_IN_USE),
1154 : : errmsg("current user cannot be dropped")));
1155 [ + - ]: 228 : if (roleid == GetOuterUserId())
1156 [ # # # # ]: 0 : ereport(ERROR,
1157 : : (errcode(ERRCODE_OBJECT_IN_USE),
1158 : : errmsg("current user cannot be dropped")));
1159 [ + - ]: 228 : if (roleid == GetSessionUserId())
1160 [ # # # # ]: 0 : ereport(ERROR,
1161 : : (errcode(ERRCODE_OBJECT_IN_USE),
1162 : : errmsg("session user cannot be dropped")));
1163 : :
1164 : : /*
1165 : : * For safety's sake, we allow createrole holders to drop ordinary
1166 : : * roles but not superuser roles, and only if they also have ADMIN
1167 : : * OPTION.
1168 : : */
1169 [ + + + + ]: 228 : if (roleform->rolsuper && !superuser())
1170 [ + - + - ]: 1 : ereport(ERROR,
1171 : : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1172 : : errmsg("permission denied to drop role"),
1173 : : errdetail("Only roles with the %s attribute may drop roles with the %s attribute.",
1174 : : "SUPERUSER", "SUPERUSER")));
1175 [ + + ]: 227 : if (!is_admin_of_role(GetUserId(), roleid))
1176 [ + - + - ]: 1 : ereport(ERROR,
1177 : : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1178 : : errmsg("permission denied to drop role"),
1179 : : errdetail("Only roles with the %s attribute and the %s option on role \"%s\" may drop this role.",
1180 : : "CREATEROLE", "ADMIN", NameStr(roleform->rolname))));
1181 : :
1182 : : /* DROP hook for the role being removed */
1183 [ + - ]: 226 : InvokeObjectDropHook(AuthIdRelationId, roleid, 0);
1184 : :
1185 : : /* Don't leak the syscache tuple */
1186 : 226 : ReleaseSysCache(tuple);
1187 : :
1188 : : /*
1189 : : * Lock the role, so nobody can add dependencies to her while we drop
1190 : : * her. We keep the lock until the end of transaction.
1191 : : */
1192 : 226 : LockSharedObject(AuthIdRelationId, roleid, 0, AccessExclusiveLock);
1193 : :
1194 : : /*
1195 : : * If there is a pg_auth_members entry that has one of the roles to be
1196 : : * dropped as the roleid or member, it should be silently removed, but
1197 : : * if there is a pg_auth_members entry that has one of the roles to be
1198 : : * dropped as the grantor, the operation should fail.
1199 : : *
1200 : : * It's possible, however, that a single pg_auth_members entry could
1201 : : * fall into multiple categories - e.g. the user could do "GRANT foo
1202 : : * TO bar GRANTED BY baz" and then "DROP ROLE baz, bar". We want such
1203 : : * an operation to succeed regardless of the order in which the
1204 : : * to-be-dropped roles are passed to DROP ROLE.
1205 : : *
1206 : : * To make that work, we remove all pg_auth_members entries that can
1207 : : * be silently removed in this loop, and then below we'll make a
1208 : : * second pass over the list of roles to be removed and check for any
1209 : : * remaining dependencies.
1210 : : */
1211 : 226 : ScanKeyInit(&scankey,
1212 : : Anum_pg_auth_members_roleid,
1213 : : BTEqualStrategyNumber, F_OIDEQ,
1214 : 226 : ObjectIdGetDatum(roleid));
1215 : :
1216 : 226 : sscan = systable_beginscan(pg_auth_members_rel, AuthMemRoleMemIndexId,
1217 : : true, NULL, 1, &scankey);
1218 : :
1219 [ + + ]: 266 : while (HeapTupleIsValid(tmp_tuple = systable_getnext(sscan)))
1220 : : {
1221 : 40 : Form_pg_auth_members authmem_form;
1222 : :
1223 : 40 : authmem_form = (Form_pg_auth_members) GETSTRUCT(tmp_tuple);
1224 : 40 : deleteSharedDependencyRecordsFor(AuthMemRelationId,
1225 : 40 : authmem_form->oid, 0);
1226 : 40 : CatalogTupleDelete(pg_auth_members_rel, &tmp_tuple->t_self);
1227 : 40 : }
1228 : :
1229 : 226 : systable_endscan(sscan);
1230 : :
1231 : 226 : ScanKeyInit(&scankey,
1232 : : Anum_pg_auth_members_member,
1233 : : BTEqualStrategyNumber, F_OIDEQ,
1234 : 226 : ObjectIdGetDatum(roleid));
1235 : :
1236 : 226 : sscan = systable_beginscan(pg_auth_members_rel, AuthMemMemRoleIndexId,
1237 : : true, NULL, 1, &scankey);
1238 : :
1239 [ + + ]: 269 : while (HeapTupleIsValid(tmp_tuple = systable_getnext(sscan)))
1240 : : {
1241 : 43 : Form_pg_auth_members authmem_form;
1242 : :
1243 : 43 : authmem_form = (Form_pg_auth_members) GETSTRUCT(tmp_tuple);
1244 : 43 : deleteSharedDependencyRecordsFor(AuthMemRelationId,
1245 : 43 : authmem_form->oid, 0);
1246 : 43 : CatalogTupleDelete(pg_auth_members_rel, &tmp_tuple->t_self);
1247 : 43 : }
1248 : :
1249 : 226 : systable_endscan(sscan);
1250 : :
1251 : : /*
1252 : : * Advance command counter so that later iterations of this loop will
1253 : : * see the changes already made. This is essential if, for example,
1254 : : * we are trying to drop both a role and one of its direct members ---
1255 : : * we'll get an error if we try to delete the linking pg_auth_members
1256 : : * tuple twice. (We do not need a CCI between the two delete loops
1257 : : * above, because it's not allowed for a role to directly contain
1258 : : * itself.)
1259 : : */
1260 : 226 : CommandCounterIncrement();
1261 : :
1262 : : /* Looks tentatively OK, add it to the list if not there yet. */
1263 : 226 : role_oids = list_append_unique_oid(role_oids, roleid);
1264 [ - + + ]: 259 : }
1265 : :
1266 : : /*
1267 : : * Second pass over the roles to be removed.
1268 : : */
1269 [ + + + + : 451 : foreach(item, role_oids)
+ + ]
1270 : : {
1271 : 225 : Oid roleid = lfirst_oid(item);
1272 : 225 : HeapTuple tuple;
1273 : 225 : Form_pg_authid roleform;
1274 : 225 : char *detail;
1275 : 225 : char *detail_log;
1276 : :
1277 : : /*
1278 : : * Re-find the pg_authid tuple.
1279 : : *
1280 : : * Since we've taken a lock on the role OID, it shouldn't be possible
1281 : : * for the tuple to have been deleted -- or for that matter updated --
1282 : : * unless the user is manually modifying the system catalogs.
1283 : : */
1284 : 225 : tuple = SearchSysCache1(AUTHOID, ObjectIdGetDatum(roleid));
1285 [ + - ]: 225 : if (!HeapTupleIsValid(tuple))
1286 [ # # # # ]: 0 : elog(ERROR, "could not find tuple for role %u", roleid);
1287 : 225 : roleform = (Form_pg_authid) GETSTRUCT(tuple);
1288 : :
1289 : : /*
1290 : : * Check for pg_shdepend entries depending on this role.
1291 : : *
1292 : : * This needs to happen after we've completed removing any
1293 : : * pg_auth_members entries that can be removed silently, in order to
1294 : : * avoid spurious failures. See notes above for more details.
1295 : : */
1296 [ + + ]: 225 : if (checkSharedDependencies(AuthIdRelationId, roleid,
1297 : : &detail, &detail_log))
1298 [ + - + - ]: 19 : ereport(ERROR,
1299 : : (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
1300 : : errmsg("role \"%s\" cannot be dropped because some objects depend on it",
1301 : : NameStr(roleform->rolname)),
1302 : : errdetail_internal("%s", detail),
1303 : : errdetail_log("%s", detail_log)));
1304 : :
1305 : : /*
1306 : : * Remove the role from the pg_authid table
1307 : : */
1308 : 206 : CatalogTupleDelete(pg_authid_rel, &tuple->t_self);
1309 : :
1310 : 206 : ReleaseSysCache(tuple);
1311 : :
1312 : : /*
1313 : : * Remove any comments or security labels on this role.
1314 : : */
1315 : 206 : DeleteSharedComments(roleid, AuthIdRelationId);
1316 : 206 : DeleteSharedSecurityLabel(roleid, AuthIdRelationId);
1317 : :
1318 : : /*
1319 : : * Remove settings for this role.
1320 : : */
1321 : 206 : DropSetting(InvalidOid, roleid);
1322 : 206 : }
1323 : :
1324 : : /*
1325 : : * Now we can clean up; but keep locks until commit.
1326 : : */
1327 : 226 : table_close(pg_auth_members_rel, NoLock);
1328 : 226 : table_close(pg_authid_rel, NoLock);
1329 : 226 : }
1330 : :
1331 : : /*
1332 : : * Rename role
1333 : : */
1334 : : ObjectAddress
1335 : 5 : RenameRole(const char *oldname, const char *newname)
1336 : : {
1337 : 5 : HeapTuple oldtuple,
1338 : : newtuple;
1339 : 5 : TupleDesc dsc;
1340 : 5 : Relation rel;
1341 : 5 : Datum datum;
1342 : 5 : bool isnull;
1343 : 5 : Datum repl_val[Natts_pg_authid];
1344 : 5 : bool repl_null[Natts_pg_authid];
1345 : 5 : bool repl_repl[Natts_pg_authid];
1346 : 5 : int i;
1347 : 5 : Oid roleid;
1348 : : ObjectAddress address;
1349 : 5 : Form_pg_authid authform;
1350 : :
1351 : 5 : rel = table_open(AuthIdRelationId, RowExclusiveLock);
1352 : 5 : dsc = RelationGetDescr(rel);
1353 : :
1354 : 5 : oldtuple = SearchSysCache1(AUTHNAME, CStringGetDatum(oldname));
1355 [ + - ]: 5 : if (!HeapTupleIsValid(oldtuple))
1356 [ # # # # ]: 0 : ereport(ERROR,
1357 : : (errcode(ERRCODE_UNDEFINED_OBJECT),
1358 : : errmsg("role \"%s\" does not exist", oldname)));
1359 : :
1360 : : /*
1361 : : * XXX Client applications probably store the session user somewhere, so
1362 : : * renaming it could cause confusion. On the other hand, there may not be
1363 : : * an actual problem besides a little confusion, so think about this and
1364 : : * decide. Same for SET ROLE ... we don't restrict renaming the current
1365 : : * effective userid, though.
1366 : : */
1367 : :
1368 : 5 : authform = (Form_pg_authid) GETSTRUCT(oldtuple);
1369 : 5 : roleid = authform->oid;
1370 : :
1371 [ + - ]: 5 : if (roleid == GetSessionUserId())
1372 [ # # # # ]: 0 : ereport(ERROR,
1373 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1374 : : errmsg("session user cannot be renamed")));
1375 [ + - ]: 5 : if (roleid == GetOuterUserId())
1376 [ # # # # ]: 0 : ereport(ERROR,
1377 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1378 : : errmsg("current user cannot be renamed")));
1379 : :
1380 : : /*
1381 : : * Check that the user is not trying to rename a system role and not
1382 : : * trying to rename a role into the reserved "pg_" namespace.
1383 : : */
1384 [ + - ]: 5 : if (IsReservedName(NameStr(authform->rolname)))
1385 [ # # # # ]: 0 : ereport(ERROR,
1386 : : (errcode(ERRCODE_RESERVED_NAME),
1387 : : errmsg("role name \"%s\" is reserved",
1388 : : NameStr(authform->rolname)),
1389 : : errdetail("Role names starting with \"pg_\" are reserved.")));
1390 : :
1391 [ + - ]: 5 : if (IsReservedName(newname))
1392 [ # # # # ]: 0 : ereport(ERROR,
1393 : : (errcode(ERRCODE_RESERVED_NAME),
1394 : : errmsg("role name \"%s\" is reserved",
1395 : : newname),
1396 : : errdetail("Role names starting with \"pg_\" are reserved.")));
1397 : :
1398 : : /*
1399 : : * If built with appropriate switch, whine when regression-testing
1400 : : * conventions for role names are violated.
1401 : : */
1402 : : #ifdef ENFORCE_REGRESSION_TEST_NAME_RESTRICTIONS
1403 : : if (strncmp(newname, "regress_", 8) != 0)
1404 : : elog(WARNING, "roles created by regression test cases should have names starting with \"regress_\"");
1405 : : #endif
1406 : :
1407 : : /* make sure the new name doesn't exist */
1408 [ + - ]: 5 : if (SearchSysCacheExists1(AUTHNAME, CStringGetDatum(newname)))
1409 [ # # # # ]: 0 : ereport(ERROR,
1410 : : (errcode(ERRCODE_DUPLICATE_OBJECT),
1411 : : errmsg("role \"%s\" already exists", newname)));
1412 : :
1413 : : /*
1414 : : * Only superusers can mess with superusers. Otherwise, a user with
1415 : : * CREATEROLE can rename a role for which they have ADMIN OPTION.
1416 : : */
1417 [ + + ]: 5 : if (authform->rolsuper)
1418 : : {
1419 [ + - ]: 1 : if (!superuser())
1420 [ # # # # ]: 0 : ereport(ERROR,
1421 : : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1422 : : errmsg("permission denied to rename role"),
1423 : : errdetail("Only roles with the %s attribute may rename roles with the %s attribute.",
1424 : : "SUPERUSER", "SUPERUSER")));
1425 : 1 : }
1426 : : else
1427 : : {
1428 [ + + ]: 4 : if (!have_createrole_privilege() ||
1429 : 3 : !is_admin_of_role(GetUserId(), roleid))
1430 [ + - + - ]: 1 : ereport(ERROR,
1431 : : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1432 : : errmsg("permission denied to rename role"),
1433 : : errdetail("Only roles with the %s attribute and the %s option on role \"%s\" may rename this role.",
1434 : : "CREATEROLE", "ADMIN", NameStr(authform->rolname))));
1435 : : }
1436 : :
1437 : : /* OK, construct the modified tuple */
1438 [ + + ]: 52 : for (i = 0; i < Natts_pg_authid; i++)
1439 : 48 : repl_repl[i] = false;
1440 : :
1441 : 4 : repl_repl[Anum_pg_authid_rolname - 1] = true;
1442 : 4 : repl_val[Anum_pg_authid_rolname - 1] = DirectFunctionCall1(namein,
1443 : : CStringGetDatum(newname));
1444 : 4 : repl_null[Anum_pg_authid_rolname - 1] = false;
1445 : :
1446 : 4 : datum = heap_getattr(oldtuple, Anum_pg_authid_rolpassword, dsc, &isnull);
1447 : :
1448 [ + + - + ]: 4 : if (!isnull && get_password_type(TextDatumGetCString(datum)) == PASSWORD_TYPE_MD5)
1449 : : {
1450 : : /* MD5 uses the username as salt, so just clear it on a rename */
1451 : 1 : repl_repl[Anum_pg_authid_rolpassword - 1] = true;
1452 : 1 : repl_null[Anum_pg_authid_rolpassword - 1] = true;
1453 : :
1454 [ - + + - ]: 1 : ereport(NOTICE,
1455 : : (errmsg("MD5 password cleared because of role rename")));
1456 : 1 : }
1457 : :
1458 : 4 : newtuple = heap_modify_tuple(oldtuple, dsc, repl_val, repl_null, repl_repl);
1459 : 4 : CatalogTupleUpdate(rel, &oldtuple->t_self, newtuple);
1460 : :
1461 [ + - ]: 4 : InvokeObjectPostAlterHook(AuthIdRelationId, roleid, 0);
1462 : :
1463 : 4 : ObjectAddressSet(address, AuthIdRelationId, roleid);
1464 : :
1465 : 4 : ReleaseSysCache(oldtuple);
1466 : :
1467 : : /*
1468 : : * Close pg_authid, but keep lock till commit.
1469 : : */
1470 : 4 : table_close(rel, NoLock);
1471 : :
1472 : : return address;
1473 : 4 : }
1474 : :
1475 : : /*
1476 : : * GrantRoleStmt
1477 : : *
1478 : : * Grant/Revoke roles to/from roles
1479 : : */
1480 : : void
1481 : 100 : GrantRole(ParseState *pstate, GrantRoleStmt *stmt)
1482 : : {
1483 : 100 : Relation pg_authid_rel;
1484 : 100 : Oid grantor;
1485 : 100 : List *grantee_ids;
1486 : 100 : ListCell *item;
1487 : 100 : GrantRoleOptions popt;
1488 : 100 : Oid currentUserId = GetUserId();
1489 : :
1490 : : /* Parse options list. */
1491 : 100 : InitGrantRoleOptions(&popt);
1492 [ + + + + : 157 : foreach(item, stmt->opt)
+ + ]
1493 : : {
1494 : 57 : DefElem *opt = (DefElem *) lfirst(item);
1495 : 57 : char *optval = defGetString(opt);
1496 : :
1497 [ + + ]: 57 : if (strcmp(opt->defname, "admin") == 0)
1498 : : {
1499 : 31 : popt.specified |= GRANT_ROLE_SPECIFIED_ADMIN;
1500 : :
1501 [ + - ]: 31 : if (parse_bool(optval, &popt.admin))
1502 : 31 : continue;
1503 : 0 : }
1504 [ + + ]: 26 : else if (strcmp(opt->defname, "inherit") == 0)
1505 : : {
1506 : 14 : popt.specified |= GRANT_ROLE_SPECIFIED_INHERIT;
1507 [ + - ]: 14 : if (parse_bool(optval, &popt.inherit))
1508 : 14 : continue;
1509 : 0 : }
1510 [ + - ]: 12 : else if (strcmp(opt->defname, "set") == 0)
1511 : : {
1512 : 12 : popt.specified |= GRANT_ROLE_SPECIFIED_SET;
1513 [ + - ]: 12 : if (parse_bool(optval, &popt.set))
1514 : 12 : continue;
1515 : 0 : }
1516 : : else
1517 [ # # # # ]: 0 : ereport(ERROR,
1518 : : errcode(ERRCODE_SYNTAX_ERROR),
1519 : : errmsg("unrecognized role option \"%s\"", opt->defname),
1520 : : parser_errposition(pstate, opt->location));
1521 : :
1522 [ # # # # ]: 0 : ereport(ERROR,
1523 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1524 : : errmsg("unrecognized value for role option \"%s\": \"%s\"",
1525 : : opt->defname, optval),
1526 : : parser_errposition(pstate, opt->location)));
1527 [ - + - ]: 57 : }
1528 : :
1529 : : /* Lookup OID of grantor, if specified. */
1530 [ + + ]: 100 : if (stmt->grantor)
1531 : 20 : grantor = get_rolespec_oid(stmt->grantor, false);
1532 : : else
1533 : 80 : grantor = InvalidOid;
1534 : :
1535 : 100 : grantee_ids = roleSpecsToIds(stmt->grantee_roles);
1536 : :
1537 : : /* AccessShareLock is enough since we aren't modifying pg_authid */
1538 : 100 : pg_authid_rel = table_open(AuthIdRelationId, AccessShareLock);
1539 : :
1540 : : /*
1541 : : * Step through all of the granted roles and add, update, or remove
1542 : : * entries in pg_auth_members as appropriate. If stmt->is_grant is true,
1543 : : * we are adding new grants or, if they already exist, updating options on
1544 : : * those grants. If stmt->is_grant is false, we are revoking grants or
1545 : : * removing options from them.
1546 : : */
1547 [ + + + + : 184 : foreach(item, stmt->granted_roles)
+ + ]
1548 : : {
1549 : 84 : AccessPriv *priv = (AccessPriv *) lfirst(item);
1550 : 84 : char *rolename = priv->priv_name;
1551 : 84 : Oid roleid;
1552 : :
1553 : : /* Must reject priv(columns) and ALL PRIVILEGES(columns) */
1554 [ + - ]: 84 : if (rolename == NULL || priv->cols != NIL)
1555 [ # # # # ]: 0 : ereport(ERROR,
1556 : : (errcode(ERRCODE_INVALID_GRANT_OPERATION),
1557 : : errmsg("column names cannot be included in GRANT/REVOKE ROLE")));
1558 : :
1559 : 84 : roleid = get_role_oid(rolename, false);
1560 : 168 : check_role_membership_authorization(currentUserId,
1561 : 84 : roleid, stmt->is_grant);
1562 [ + + ]: 84 : if (stmt->is_grant)
1563 : 120 : AddRoleMems(currentUserId, rolename, roleid,
1564 : 60 : stmt->grantee_roles, grantee_ids,
1565 : 60 : grantor, &popt);
1566 : : else
1567 : 48 : DelRoleMems(currentUserId, rolename, roleid,
1568 : 24 : stmt->grantee_roles, grantee_ids,
1569 : 24 : grantor, &popt, stmt->behavior);
1570 : 84 : }
1571 : :
1572 : : /*
1573 : : * Close pg_authid, but keep lock till commit.
1574 : : */
1575 : 100 : table_close(pg_authid_rel, NoLock);
1576 : 100 : }
1577 : :
1578 : : /*
1579 : : * DropOwnedObjects
1580 : : *
1581 : : * Drop the objects owned by a given list of roles.
1582 : : */
1583 : : void
1584 : 20 : DropOwnedObjects(DropOwnedStmt *stmt)
1585 : : {
1586 : 20 : List *role_ids = roleSpecsToIds(stmt->roles);
1587 : 20 : ListCell *cell;
1588 : :
1589 : : /* Check privileges */
1590 [ + - + + : 41 : foreach(cell, role_ids)
+ + ]
1591 : : {
1592 : 23 : Oid roleid = lfirst_oid(cell);
1593 : :
1594 [ + + ]: 23 : if (!has_privs_of_role(GetUserId(), roleid))
1595 [ + - + - ]: 2 : ereport(ERROR,
1596 : : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1597 : : errmsg("permission denied to drop objects"),
1598 : : errdetail("Only roles with privileges of role \"%s\" may drop objects owned by it.",
1599 : : GetUserNameFromId(roleid, false))));
1600 : 21 : }
1601 : :
1602 : : /* Ok, do it */
1603 : 18 : shdepDropOwned(role_ids, stmt->behavior);
1604 : 18 : }
1605 : :
1606 : : /*
1607 : : * ReassignOwnedObjects
1608 : : *
1609 : : * Give the objects owned by a given list of roles away to another user.
1610 : : */
1611 : : void
1612 : 7 : ReassignOwnedObjects(ReassignOwnedStmt *stmt)
1613 : : {
1614 : 7 : List *role_ids = roleSpecsToIds(stmt->roles);
1615 : 7 : ListCell *cell;
1616 : 7 : Oid newrole;
1617 : :
1618 : : /* Check privileges */
1619 [ + - + + : 12 : foreach(cell, role_ids)
+ + ]
1620 : : {
1621 : 7 : Oid roleid = lfirst_oid(cell);
1622 : :
1623 [ + + ]: 7 : if (!has_privs_of_role(GetUserId(), roleid))
1624 [ + - + - ]: 2 : ereport(ERROR,
1625 : : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1626 : : errmsg("permission denied to reassign objects"),
1627 : : errdetail("Only roles with privileges of role \"%s\" may reassign objects owned by it.",
1628 : : GetUserNameFromId(roleid, false))));
1629 : 5 : }
1630 : :
1631 : : /* Must have privileges on the receiving side too */
1632 : 5 : newrole = get_rolespec_oid(stmt->newrole, false);
1633 : :
1634 [ + + ]: 5 : if (!has_privs_of_role(GetUserId(), newrole))
1635 [ + - + - ]: 1 : ereport(ERROR,
1636 : : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1637 : : errmsg("permission denied to reassign objects"),
1638 : : errdetail("Only roles with privileges of role \"%s\" may reassign objects to it.",
1639 : : GetUserNameFromId(newrole, false))));
1640 : :
1641 : : /* Ok, do it */
1642 : 4 : shdepReassignOwned(role_ids, newrole);
1643 : 4 : }
1644 : :
1645 : : /*
1646 : : * roleSpecsToIds
1647 : : *
1648 : : * Given a list of RoleSpecs, generate a list of role OIDs in the same order.
1649 : : *
1650 : : * ROLESPEC_PUBLIC is not allowed.
1651 : : */
1652 : : List *
1653 : 569 : roleSpecsToIds(List *memberNames)
1654 : : {
1655 : 569 : List *result = NIL;
1656 : 569 : ListCell *l;
1657 : :
1658 [ + + + + : 724 : foreach(l, memberNames)
+ + ]
1659 : : {
1660 : 155 : RoleSpec *rolespec = lfirst_node(RoleSpec, l);
1661 : 155 : Oid roleid;
1662 : :
1663 : 155 : roleid = get_rolespec_oid(rolespec, false);
1664 : 155 : result = lappend_oid(result, roleid);
1665 : 155 : }
1666 : 1138 : return result;
1667 : 569 : }
1668 : :
1669 : : /*
1670 : : * AddRoleMems -- Add given members to the specified role
1671 : : *
1672 : : * currentUserId: OID of role performing the operation
1673 : : * rolename: name of role to add to (used only for error messages)
1674 : : * roleid: OID of role to add to
1675 : : * memberSpecs: list of RoleSpec of roles to add (used only for error messages)
1676 : : * memberIds: OIDs of roles to add
1677 : : * grantorId: OID that should be recorded as having granted the membership
1678 : : * (InvalidOid if not set explicitly)
1679 : : * popt: information about grant options
1680 : : */
1681 : : static void
1682 : 519 : AddRoleMems(Oid currentUserId, const char *rolename, Oid roleid,
1683 : : List *memberSpecs, List *memberIds,
1684 : : Oid grantorId, GrantRoleOptions *popt)
1685 : : {
1686 : 519 : Relation pg_authmem_rel;
1687 : 519 : TupleDesc pg_authmem_dsc;
1688 : 519 : ListCell *specitem;
1689 : 519 : ListCell *iditem;
1690 : :
1691 [ + - ]: 519 : Assert(list_length(memberSpecs) == list_length(memberIds));
1692 : :
1693 : : /* Validate grantor (and resolve implicit grantor if not specified). */
1694 : 519 : grantorId = check_role_grantor(currentUserId, roleid, grantorId, true);
1695 : :
1696 : 519 : pg_authmem_rel = table_open(AuthMemRelationId, RowExclusiveLock);
1697 : 519 : pg_authmem_dsc = RelationGetDescr(pg_authmem_rel);
1698 : :
1699 : : /*
1700 : : * Only allow changes to this role by one backend at a time, so that we
1701 : : * can check integrity constraints like the lack of circular ADMIN OPTION
1702 : : * grants without fear of race conditions.
1703 : : */
1704 : 519 : LockSharedObject(AuthIdRelationId, roleid, 0,
1705 : : ShareUpdateExclusiveLock);
1706 : :
1707 : : /* Preliminary sanity checks. */
1708 [ + + + + : 623 : forboth(specitem, memberSpecs, iditem, memberIds)
+ + + + +
+ + + ]
1709 : : {
1710 : 107 : RoleSpec *memberRole = lfirst_node(RoleSpec, specitem);
1711 : 107 : Oid memberid = lfirst_oid(iditem);
1712 : :
1713 : : /*
1714 : : * pg_database_owner is never a role member. Lifting this restriction
1715 : : * would require a policy decision about membership loops. One could
1716 : : * prevent loops, which would include making "ALTER DATABASE x OWNER
1717 : : * TO proposed_datdba" fail if is_member_of_role(pg_database_owner,
1718 : : * proposed_datdba). Hence, gaining a membership could reduce what a
1719 : : * role could do. Alternately, one could allow these memberships to
1720 : : * complete loops. A role could then have actual WITH ADMIN OPTION on
1721 : : * itself, prompting a decision about is_admin_of_role() treatment of
1722 : : * the case.
1723 : : *
1724 : : * Lifting this restriction also has policy implications for ownership
1725 : : * of shared objects (databases and tablespaces). We allow such
1726 : : * ownership, but we might find cause to ban it in the future.
1727 : : * Designing such a ban would more troublesome if the design had to
1728 : : * address pg_database_owner being a member of role FOO that owns a
1729 : : * shared object. (The effect of such ownership is that any owner of
1730 : : * another database can act as the owner of affected shared objects.)
1731 : : */
1732 [ + + ]: 107 : if (memberid == ROLE_PG_DATABASE_OWNER)
1733 [ + - + - ]: 1 : ereport(ERROR,
1734 : : errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1735 : : errmsg("role \"%s\" cannot be a member of any role",
1736 : : get_rolespec_name(memberRole)));
1737 : :
1738 : : /*
1739 : : * Refuse creation of membership loops, including the trivial case
1740 : : * where a role is made a member of itself. We do this by checking to
1741 : : * see if the target role is already a member of the proposed member
1742 : : * role. We have to ignore possible superuserness, however, else we
1743 : : * could never grant membership in a superuser-privileged role.
1744 : : */
1745 [ + + ]: 106 : if (is_member_of_role_nosuper(roleid, memberid))
1746 [ + - + - ]: 2 : ereport(ERROR,
1747 : : (errcode(ERRCODE_INVALID_GRANT_OPERATION),
1748 : : errmsg("role \"%s\" is a member of role \"%s\"",
1749 : : rolename, get_rolespec_name(memberRole))));
1750 : 104 : }
1751 : :
1752 : : /*
1753 : : * Disallow attempts to grant ADMIN OPTION back to a user who granted it
1754 : : * to you, similar to what check_circularity does for ACLs. We want the
1755 : : * chains of grants to remain acyclic, so that it's always possible to use
1756 : : * REVOKE .. CASCADE to clean up all grants that depend on the one being
1757 : : * revoked.
1758 : : *
1759 : : * NB: This check might look redundant with the check for membership loops
1760 : : * above, but it isn't. That's checking for role-member loop (e.g. A is a
1761 : : * member of B and B is a member of A) while this is checking for a
1762 : : * member-grantor loop (e.g. A gave ADMIN OPTION on X to B and now B, who
1763 : : * has no other source of ADMIN OPTION on X, tries to give ADMIN OPTION on
1764 : : * X back to A).
1765 : : */
1766 [ + + + + ]: 516 : if (popt->admin && grantorId != BOOTSTRAP_SUPERUSERID)
1767 : : {
1768 : 24 : CatCList *memlist;
1769 : 24 : RevokeRoleGrantAction *actions;
1770 : 24 : int i;
1771 : :
1772 : : /* Get the list of members for this role. */
1773 : 24 : memlist = SearchSysCacheList1(AUTHMEMROLEMEM,
1774 : : ObjectIdGetDatum(roleid));
1775 : :
1776 : : /*
1777 : : * Figure out what would happen if we removed all existing grants to
1778 : : * every role to which we've been asked to make a new grant.
1779 : : */
1780 : 24 : actions = initialize_revoke_actions(memlist);
1781 [ + + + + : 38 : foreach(iditem, memberIds)
+ + ]
1782 : : {
1783 : 14 : Oid memberid = lfirst_oid(iditem);
1784 : :
1785 [ + - ]: 14 : if (memberid == BOOTSTRAP_SUPERUSERID)
1786 [ # # # # ]: 0 : ereport(ERROR,
1787 : : (errcode(ERRCODE_INVALID_GRANT_OPERATION),
1788 : : errmsg("%s option cannot be granted back to your own grantor",
1789 : : "ADMIN")));
1790 : 14 : plan_member_revoke(memlist, actions, memberid);
1791 : 14 : }
1792 : :
1793 : : /*
1794 : : * If the result would be that the grantor role would no longer have
1795 : : * the ability to perform the grant, then the proposed grant would
1796 : : * create a circularity.
1797 : : */
1798 [ + + ]: 29 : for (i = 0; i < memlist->n_members; ++i)
1799 : : {
1800 : 28 : HeapTuple authmem_tuple;
1801 : 28 : Form_pg_auth_members authmem_form;
1802 : :
1803 : 28 : authmem_tuple = &memlist->members[i]->tuple;
1804 : 28 : authmem_form = (Form_pg_auth_members) GETSTRUCT(authmem_tuple);
1805 : :
1806 [ + + ]: 28 : if (actions[i] == RRG_NOOP &&
1807 [ + + - + ]: 26 : authmem_form->member == grantorId &&
1808 : 23 : authmem_form->admin_option)
1809 : 23 : break;
1810 [ + + ]: 28 : }
1811 [ + + ]: 24 : if (i >= memlist->n_members)
1812 [ + - + - ]: 1 : ereport(ERROR,
1813 : : (errcode(ERRCODE_INVALID_GRANT_OPERATION),
1814 : : errmsg("%s option cannot be granted back to your own grantor",
1815 : : "ADMIN")));
1816 : :
1817 : 23 : ReleaseSysCacheList(memlist);
1818 : 23 : }
1819 : :
1820 : : /* Now perform the catalog updates. */
1821 [ + + + + : 618 : forboth(specitem, memberSpecs, iditem, memberIds)
+ + + + +
+ + + ]
1822 : : {
1823 : 103 : RoleSpec *memberRole = lfirst_node(RoleSpec, specitem);
1824 : 103 : Oid memberid = lfirst_oid(iditem);
1825 : 103 : HeapTuple authmem_tuple;
1826 : 103 : HeapTuple tuple;
1827 : 103 : Datum new_record[Natts_pg_auth_members] = {0};
1828 : 103 : bool new_record_nulls[Natts_pg_auth_members] = {0};
1829 : 103 : bool new_record_repl[Natts_pg_auth_members] = {0};
1830 : :
1831 : : /* Common initialization for possible insert or update */
1832 : 103 : new_record[Anum_pg_auth_members_roleid - 1] =
1833 : 103 : ObjectIdGetDatum(roleid);
1834 : 103 : new_record[Anum_pg_auth_members_member - 1] =
1835 : 103 : ObjectIdGetDatum(memberid);
1836 : 103 : new_record[Anum_pg_auth_members_grantor - 1] =
1837 : 103 : ObjectIdGetDatum(grantorId);
1838 : :
1839 : : /* Find any existing tuple */
1840 : 103 : authmem_tuple = SearchSysCache3(AUTHMEMROLEMEM,
1841 : 103 : ObjectIdGetDatum(roleid),
1842 : 103 : ObjectIdGetDatum(memberid),
1843 : 103 : ObjectIdGetDatum(grantorId));
1844 : :
1845 : : /*
1846 : : * If we found a tuple, update it with new option values, unless there
1847 : : * are no changes, in which case issue a WARNING.
1848 : : *
1849 : : * If we didn't find a tuple, just insert one.
1850 : : */
1851 [ + + ]: 103 : if (HeapTupleIsValid(authmem_tuple))
1852 : : {
1853 : 5 : Form_pg_auth_members authmem_form;
1854 : 5 : bool at_least_one_change = false;
1855 : :
1856 : 5 : authmem_form = (Form_pg_auth_members) GETSTRUCT(authmem_tuple);
1857 : :
1858 : 5 : if ((popt->specified & GRANT_ROLE_SPECIFIED_ADMIN) != 0
1859 [ - + # # ]: 5 : && authmem_form->admin_option != popt->admin)
1860 : : {
1861 : 0 : new_record[Anum_pg_auth_members_admin_option - 1] =
1862 : 0 : BoolGetDatum(popt->admin);
1863 : 0 : new_record_repl[Anum_pg_auth_members_admin_option - 1] =
1864 : : true;
1865 : 0 : at_least_one_change = true;
1866 : 0 : }
1867 : :
1868 : 5 : if ((popt->specified & GRANT_ROLE_SPECIFIED_INHERIT) != 0
1869 [ + + - + ]: 5 : && authmem_form->inherit_option != popt->inherit)
1870 : : {
1871 : 2 : new_record[Anum_pg_auth_members_inherit_option - 1] =
1872 : 2 : BoolGetDatum(popt->inherit);
1873 : 2 : new_record_repl[Anum_pg_auth_members_inherit_option - 1] =
1874 : : true;
1875 : 2 : at_least_one_change = true;
1876 : 2 : }
1877 : :
1878 : 5 : if ((popt->specified & GRANT_ROLE_SPECIFIED_SET) != 0
1879 [ + + - + ]: 5 : && authmem_form->set_option != popt->set)
1880 : : {
1881 : 1 : new_record[Anum_pg_auth_members_set_option - 1] =
1882 : 1 : BoolGetDatum(popt->set);
1883 : 1 : new_record_repl[Anum_pg_auth_members_set_option - 1] =
1884 : : true;
1885 : 1 : at_least_one_change = true;
1886 : 1 : }
1887 : :
1888 [ + + ]: 5 : if (!at_least_one_change)
1889 : : {
1890 [ - + + - ]: 3 : ereport(NOTICE,
1891 : : (errmsg("role \"%s\" has already been granted membership in role \"%s\" by role \"%s\"",
1892 : : get_rolespec_name(memberRole), rolename,
1893 : : GetUserNameFromId(grantorId, false))));
1894 : 3 : ReleaseSysCache(authmem_tuple);
1895 : 3 : continue;
1896 : : }
1897 : :
1898 : 4 : tuple = heap_modify_tuple(authmem_tuple, pg_authmem_dsc,
1899 : 2 : new_record,
1900 : 2 : new_record_nulls, new_record_repl);
1901 : 2 : CatalogTupleUpdate(pg_authmem_rel, &tuple->t_self, tuple);
1902 : :
1903 : 2 : ReleaseSysCache(authmem_tuple);
1904 [ + + ]: 5 : }
1905 : : else
1906 : : {
1907 : 98 : Oid objectId;
1908 : 98 : Oid *newmembers = palloc_object(Oid);
1909 : :
1910 : : /*
1911 : : * The values for these options can be taken directly from 'popt'.
1912 : : * Either they were specified, or the defaults as set by
1913 : : * InitGrantRoleOptions are correct.
1914 : : */
1915 : 98 : new_record[Anum_pg_auth_members_admin_option - 1] =
1916 : 98 : BoolGetDatum(popt->admin);
1917 : 98 : new_record[Anum_pg_auth_members_set_option - 1] =
1918 : 98 : BoolGetDatum(popt->set);
1919 : :
1920 : : /*
1921 : : * If the user specified a value for the inherit option, use
1922 : : * whatever was specified. Otherwise, set the default value based
1923 : : * on the role-level property.
1924 : : */
1925 [ + + ]: 98 : if ((popt->specified & GRANT_ROLE_SPECIFIED_INHERIT) != 0)
1926 : 31 : new_record[Anum_pg_auth_members_inherit_option - 1] =
1927 : 31 : BoolGetDatum(popt->inherit);
1928 : : else
1929 : : {
1930 : 67 : HeapTuple mrtup;
1931 : 67 : Form_pg_authid mrform;
1932 : :
1933 : 67 : mrtup = SearchSysCache1(AUTHOID, ObjectIdGetDatum(memberid));
1934 [ + - ]: 67 : if (!HeapTupleIsValid(mrtup))
1935 [ # # # # ]: 0 : elog(ERROR, "cache lookup failed for role %u", memberid);
1936 : 67 : mrform = (Form_pg_authid) GETSTRUCT(mrtup);
1937 : 67 : new_record[Anum_pg_auth_members_inherit_option - 1] =
1938 : 67 : BoolGetDatum(mrform->rolinherit);
1939 : 67 : ReleaseSysCache(mrtup);
1940 : 67 : }
1941 : :
1942 : : /* get an OID for the new row and insert it */
1943 : 98 : objectId = GetNewOidWithIndex(pg_authmem_rel, AuthMemOidIndexId,
1944 : : Anum_pg_auth_members_oid);
1945 : 98 : new_record[Anum_pg_auth_members_oid - 1] = ObjectIdGetDatum(objectId);
1946 : 196 : tuple = heap_form_tuple(pg_authmem_dsc,
1947 : 98 : new_record, new_record_nulls);
1948 : 98 : CatalogTupleInsert(pg_authmem_rel, tuple);
1949 : :
1950 : : /* updateAclDependencies wants to pfree array inputs */
1951 : 98 : newmembers[0] = grantorId;
1952 : 196 : updateAclDependencies(AuthMemRelationId, objectId,
1953 : : 0, InvalidOid,
1954 : : 0, NULL,
1955 : 98 : 1, newmembers);
1956 : 98 : }
1957 : :
1958 : : /* CCI after each change, in case there are duplicates in list */
1959 : 100 : CommandCounterIncrement();
1960 [ + + ]: 103 : }
1961 : :
1962 : : /*
1963 : : * Close pg_authmem, but keep lock till commit.
1964 : : */
1965 : 515 : table_close(pg_authmem_rel, NoLock);
1966 : 515 : }
1967 : :
1968 : : /*
1969 : : * DelRoleMems -- Remove given members from the specified role
1970 : : *
1971 : : * rolename: name of role to del from (used only for error messages)
1972 : : * roleid: OID of role to del from
1973 : : * memberSpecs: list of RoleSpec of roles to del (used only for error messages)
1974 : : * memberIds: OIDs of roles to del
1975 : : * grantorId: who is revoking the membership
1976 : : * popt: information about grant options
1977 : : * behavior: RESTRICT or CASCADE behavior for recursive removal
1978 : : */
1979 : : static void
1980 : 26 : DelRoleMems(Oid currentUserId, const char *rolename, Oid roleid,
1981 : : List *memberSpecs, List *memberIds,
1982 : : Oid grantorId, GrantRoleOptions *popt, DropBehavior behavior)
1983 : : {
1984 : 26 : Relation pg_authmem_rel;
1985 : 26 : TupleDesc pg_authmem_dsc;
1986 : 26 : ListCell *specitem;
1987 : 26 : ListCell *iditem;
1988 : 26 : CatCList *memlist;
1989 : 26 : RevokeRoleGrantAction *actions;
1990 : 26 : int i;
1991 : :
1992 [ + - ]: 26 : Assert(list_length(memberSpecs) == list_length(memberIds));
1993 : :
1994 : : /* Validate grantor (and resolve implicit grantor if not specified). */
1995 : 26 : grantorId = check_role_grantor(currentUserId, roleid, grantorId, false);
1996 : :
1997 : 26 : pg_authmem_rel = table_open(AuthMemRelationId, RowExclusiveLock);
1998 : 26 : pg_authmem_dsc = RelationGetDescr(pg_authmem_rel);
1999 : :
2000 : : /*
2001 : : * Only allow changes to this role by one backend at a time, so that we
2002 : : * can check for things like dependent privileges without fear of race
2003 : : * conditions.
2004 : : */
2005 : 26 : LockSharedObject(AuthIdRelationId, roleid, 0,
2006 : : ShareUpdateExclusiveLock);
2007 : :
2008 : 26 : memlist = SearchSysCacheList1(AUTHMEMROLEMEM, ObjectIdGetDatum(roleid));
2009 : 26 : actions = initialize_revoke_actions(memlist);
2010 : :
2011 : : /*
2012 : : * We may need to recurse to dependent privileges if DROP_CASCADE was
2013 : : * specified, or refuse to perform the operation if dependent privileges
2014 : : * exist and DROP_RESTRICT was specified. plan_single_revoke() will figure
2015 : : * out what to do with each catalog tuple.
2016 : : */
2017 [ + - + + : 50 : forboth(specitem, memberSpecs, iditem, memberIds)
+ - + + +
+ + + ]
2018 : : {
2019 : 24 : RoleSpec *memberRole = lfirst(specitem);
2020 : 24 : Oid memberid = lfirst_oid(iditem);
2021 : :
2022 [ + + + + ]: 48 : if (!plan_single_revoke(memlist, actions, memberid, grantorId,
2023 : 24 : popt, behavior))
2024 : : {
2025 [ - + + - ]: 1 : ereport(WARNING,
2026 : : (errmsg("role \"%s\" has not been granted membership in role \"%s\" by role \"%s\"",
2027 : : get_rolespec_name(memberRole), rolename,
2028 : : GetUserNameFromId(grantorId, false))));
2029 : 1 : continue;
2030 : : }
2031 [ + + ]: 24 : }
2032 : :
2033 : : /*
2034 : : * We now know what to do with each catalog tuple: it should either be
2035 : : * left alone, deleted, or just have the admin_option flag cleared.
2036 : : * Perform the appropriate action in each case.
2037 : : */
2038 [ + + ]: 72 : for (i = 0; i < memlist->n_members; ++i)
2039 : : {
2040 : 46 : HeapTuple authmem_tuple;
2041 : 46 : Form_pg_auth_members authmem_form;
2042 : :
2043 [ + + ]: 46 : if (actions[i] == RRG_NOOP)
2044 : 20 : continue;
2045 : :
2046 : 26 : authmem_tuple = &memlist->members[i]->tuple;
2047 : 26 : authmem_form = (Form_pg_auth_members) GETSTRUCT(authmem_tuple);
2048 : :
2049 [ + + ]: 26 : if (actions[i] == RRG_DELETE_GRANT)
2050 : : {
2051 : : /*
2052 : : * Remove the entry altogether, after first removing its
2053 : : * dependencies
2054 : : */
2055 : 18 : deleteSharedDependencyRecordsFor(AuthMemRelationId,
2056 : 18 : authmem_form->oid, 0);
2057 : 18 : CatalogTupleDelete(pg_authmem_rel, &authmem_tuple->t_self);
2058 : 18 : }
2059 : : else
2060 : : {
2061 : : /* Just turn off the specified option */
2062 : 8 : HeapTuple tuple;
2063 : 8 : Datum new_record[Natts_pg_auth_members] = {0};
2064 : 8 : bool new_record_nulls[Natts_pg_auth_members] = {0};
2065 : 8 : bool new_record_repl[Natts_pg_auth_members] = {0};
2066 : :
2067 : : /* Build a tuple to update with */
2068 [ + + ]: 8 : if (actions[i] == RRG_REMOVE_ADMIN_OPTION)
2069 : : {
2070 : 5 : new_record[Anum_pg_auth_members_admin_option - 1] =
2071 : 5 : BoolGetDatum(false);
2072 : 5 : new_record_repl[Anum_pg_auth_members_admin_option - 1] =
2073 : : true;
2074 : 5 : }
2075 [ + + ]: 3 : else if (actions[i] == RRG_REMOVE_INHERIT_OPTION)
2076 : : {
2077 : 2 : new_record[Anum_pg_auth_members_inherit_option - 1] =
2078 : 2 : BoolGetDatum(false);
2079 : 2 : new_record_repl[Anum_pg_auth_members_inherit_option - 1] =
2080 : : true;
2081 : 2 : }
2082 [ + - ]: 1 : else if (actions[i] == RRG_REMOVE_SET_OPTION)
2083 : : {
2084 : 1 : new_record[Anum_pg_auth_members_set_option - 1] =
2085 : 1 : BoolGetDatum(false);
2086 : 1 : new_record_repl[Anum_pg_auth_members_set_option - 1] =
2087 : : true;
2088 : 1 : }
2089 : : else
2090 [ # # # # ]: 0 : elog(ERROR, "unknown role revoke action");
2091 : :
2092 : 16 : tuple = heap_modify_tuple(authmem_tuple, pg_authmem_dsc,
2093 : 8 : new_record,
2094 : 8 : new_record_nulls, new_record_repl);
2095 : 8 : CatalogTupleUpdate(pg_authmem_rel, &tuple->t_self, tuple);
2096 : 8 : }
2097 [ + + ]: 46 : }
2098 : :
2099 : 26 : ReleaseSysCacheList(memlist);
2100 : :
2101 : : /*
2102 : : * Close pg_authmem, but keep lock till commit.
2103 : : */
2104 : 26 : table_close(pg_authmem_rel, NoLock);
2105 : 26 : }
2106 : :
2107 : : /*
2108 : : * Check that currentUserId has permission to modify the membership list for
2109 : : * roleid. Throw an error if not.
2110 : : */
2111 : : static void
2112 : 114 : check_role_membership_authorization(Oid currentUserId, Oid roleid,
2113 : : bool is_grant)
2114 : : {
2115 : : /*
2116 : : * The charter of pg_database_owner is to have exactly one, implicit,
2117 : : * situation-dependent member. There's no technical need for this
2118 : : * restriction. (One could lift it and take the further step of making
2119 : : * object_ownercheck(DatabaseRelationId, ...) equivalent to
2120 : : * has_privs_of_role(roleid, ROLE_PG_DATABASE_OWNER), in which case
2121 : : * explicit, situation-independent members could act as the owner of any
2122 : : * database.)
2123 : : */
2124 [ + + + + ]: 114 : if (is_grant && roleid == ROLE_PG_DATABASE_OWNER)
2125 [ + - + - ]: 2 : ereport(ERROR,
2126 : : errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2127 : : errmsg("role \"%s\" cannot have explicit members",
2128 : : GetUserNameFromId(roleid, false)));
2129 : :
2130 : : /* To mess with a superuser role, you gotta be superuser. */
2131 [ + + ]: 112 : if (superuser_arg(roleid))
2132 : : {
2133 [ + + ]: 2 : if (!superuser_arg(currentUserId))
2134 : : {
2135 [ + - ]: 1 : if (is_grant)
2136 [ + - + - ]: 1 : ereport(ERROR,
2137 : : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
2138 : : errmsg("permission denied to grant role \"%s\"",
2139 : : GetUserNameFromId(roleid, false)),
2140 : : errdetail("Only roles with the %s attribute may grant roles with the %s attribute.",
2141 : : "SUPERUSER", "SUPERUSER")));
2142 : : else
2143 [ # # # # ]: 0 : ereport(ERROR,
2144 : : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
2145 : : errmsg("permission denied to revoke role \"%s\"",
2146 : : GetUserNameFromId(roleid, false)),
2147 : : errdetail("Only roles with the %s attribute may revoke roles with the %s attribute.",
2148 : : "SUPERUSER", "SUPERUSER")));
2149 : 0 : }
2150 : 1 : }
2151 : : else
2152 : : {
2153 : : /*
2154 : : * Otherwise, must have admin option on the role to be changed.
2155 : : */
2156 [ + + ]: 110 : if (!is_admin_of_role(currentUserId, roleid))
2157 : : {
2158 [ + - ]: 24 : if (is_grant)
2159 [ + - + - ]: 24 : ereport(ERROR,
2160 : : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
2161 : : errmsg("permission denied to grant role \"%s\"",
2162 : : GetUserNameFromId(roleid, false)),
2163 : : errdetail("Only roles with the %s option on role \"%s\" may grant this role.",
2164 : : "ADMIN", GetUserNameFromId(roleid, false))));
2165 : : else
2166 [ # # # # ]: 0 : ereport(ERROR,
2167 : : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
2168 : : errmsg("permission denied to revoke role \"%s\"",
2169 : : GetUserNameFromId(roleid, false)),
2170 : : errdetail("Only roles with the %s option on role \"%s\" may revoke this role.",
2171 : : "ADMIN", GetUserNameFromId(roleid, false))));
2172 : 0 : }
2173 : : }
2174 : 87 : }
2175 : :
2176 : : /*
2177 : : * Sanity-check, or infer, the grantor for a GRANT or REVOKE statement
2178 : : * targeting a role.
2179 : : *
2180 : : * The grantor must always be either a role with ADMIN OPTION on the role in
2181 : : * which membership is being granted, or the bootstrap superuser. This is
2182 : : * similar to the restriction enforced by select_best_grantor, except that
2183 : : * roles don't have owners, so we regard the bootstrap superuser as the
2184 : : * implicit owner.
2185 : : *
2186 : : * If the grantor was not explicitly specified by the user, grantorId should
2187 : : * be passed as InvalidOid, and this function will infer the user to be
2188 : : * recorded as the grantor. In many cases, this will be the current user, but
2189 : : * things get more complicated when the current user doesn't possess ADMIN
2190 : : * OPTION on the role but rather relies on having SUPERUSER privileges, or
2191 : : * on inheriting the privileges of a role which does have ADMIN OPTION. See
2192 : : * below for details.
2193 : : *
2194 : : * If the grantor was specified by the user, then it must be a user that
2195 : : * can legally be recorded as the grantor, as per the rule stated above.
2196 : : * This is an integrity constraint, not a permissions check, and thus even
2197 : : * superusers are subject to this restriction. However, there is also a
2198 : : * permissions check: to specify a role as the grantor, the current user
2199 : : * must possess the privileges of that role. Superusers will always pass
2200 : : * this check, but for non-superusers it may lead to an error.
2201 : : *
2202 : : * The return value is the OID to be regarded as the grantor when executing
2203 : : * the operation.
2204 : : */
2205 : : static Oid
2206 : 546 : check_role_grantor(Oid currentUserId, Oid roleid, Oid grantorId, bool is_grant)
2207 : : {
2208 : : /* If the grantor ID was not specified, pick one to use. */
2209 [ + + ]: 546 : if (!OidIsValid(grantorId))
2210 : : {
2211 : : /*
2212 : : * Grants where the grantor is recorded as the bootstrap superuser do
2213 : : * not depend on any other existing grants, so always default to this
2214 : : * interpretation when possible.
2215 : : */
2216 [ + + ]: 506 : if (superuser_arg(currentUserId))
2217 : 450 : return BOOTSTRAP_SUPERUSERID;
2218 : :
2219 : : /*
2220 : : * Otherwise, the grantor must either have ADMIN OPTION on the role or
2221 : : * inherit the privileges of a role which does. In the former case,
2222 : : * record the grantor as the current user; in the latter, pick one of
2223 : : * the roles that is "most directly" inherited by the current role
2224 : : * (i.e. fewest "hops").
2225 : : *
2226 : : * (We shouldn't fail to find a best grantor, because we've already
2227 : : * established that the current user has permission to perform the
2228 : : * operation.)
2229 : : */
2230 : 56 : grantorId = select_best_admin(currentUserId, roleid);
2231 [ + - ]: 56 : if (!OidIsValid(grantorId))
2232 [ # # # # ]: 0 : elog(ERROR, "no possible grantors");
2233 : 56 : return grantorId;
2234 : : }
2235 : :
2236 : : /*
2237 : : * If an explicit grantor is specified, it must be a role whose privileges
2238 : : * the current user possesses.
2239 : : *
2240 : : * It should also be a role that has ADMIN OPTION on the target role, but
2241 : : * we check this condition only in case of GRANT. For REVOKE, no matching
2242 : : * grant should exist anyway, but if it somehow does, let the user get rid
2243 : : * of it.
2244 : : */
2245 [ + + ]: 40 : if (is_grant)
2246 : : {
2247 [ + - ]: 36 : if (!has_privs_of_role(currentUserId, grantorId))
2248 [ # # # # ]: 0 : ereport(ERROR,
2249 : : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
2250 : : errmsg("permission denied to grant privileges as role \"%s\"",
2251 : : GetUserNameFromId(grantorId, false)),
2252 : : errdetail("Only roles with privileges of role \"%s\" may grant privileges as this role.",
2253 : : GetUserNameFromId(grantorId, false))));
2254 : :
2255 [ + + + + ]: 36 : if (grantorId != BOOTSTRAP_SUPERUSERID &&
2256 : 15 : select_best_admin(grantorId, roleid) != grantorId)
2257 [ + - + - ]: 1 : ereport(ERROR,
2258 : : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
2259 : : errmsg("permission denied to grant privileges as role \"%s\"",
2260 : : GetUserNameFromId(grantorId, false)),
2261 : : errdetail("The grantor must have the %s option on role \"%s\".",
2262 : : "ADMIN", GetUserNameFromId(roleid, false))));
2263 : 35 : }
2264 : : else
2265 : : {
2266 [ + - ]: 4 : if (!has_privs_of_role(currentUserId, grantorId))
2267 [ # # # # ]: 0 : ereport(ERROR,
2268 : : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
2269 : : errmsg("permission denied to revoke privileges granted by role \"%s\"",
2270 : : GetUserNameFromId(grantorId, false)),
2271 : : errdetail("Only roles with privileges of role \"%s\" may revoke privileges granted by this role.",
2272 : : GetUserNameFromId(grantorId, false))));
2273 : : }
2274 : :
2275 : : /*
2276 : : * If a grantor was specified explicitly, always attribute the grant to
2277 : : * that role (unless we error out above).
2278 : : */
2279 : 39 : return grantorId;
2280 : 545 : }
2281 : :
2282 : : /*
2283 : : * Initialize an array of RevokeRoleGrantAction objects.
2284 : : *
2285 : : * 'memlist' should be a list of all grants for the target role.
2286 : : *
2287 : : * This constructs an array indicating that no actions are to be performed;
2288 : : * that is, every element is initially RRG_NOOP.
2289 : : */
2290 : : static RevokeRoleGrantAction *
2291 : 50 : initialize_revoke_actions(CatCList *memlist)
2292 : : {
2293 : 50 : RevokeRoleGrantAction *result;
2294 : 50 : int i;
2295 : :
2296 [ + - ]: 50 : if (memlist->n_members == 0)
2297 : 0 : return NULL;
2298 : :
2299 : 50 : result = palloc_array(RevokeRoleGrantAction, memlist->n_members);
2300 [ + + ]: 136 : for (i = 0; i < memlist->n_members; i++)
2301 : 86 : result[i] = RRG_NOOP;
2302 : 50 : return result;
2303 : 50 : }
2304 : :
2305 : : /*
2306 : : * Figure out what we would need to do in order to revoke a grant, or just the
2307 : : * admin option on a grant, given that there might be dependent privileges.
2308 : : *
2309 : : * 'memlist' should be a list of all grants for the target role.
2310 : : *
2311 : : * Whatever actions prove to be necessary will be signalled by updating
2312 : : * 'actions'.
2313 : : *
2314 : : * If behavior is DROP_RESTRICT, an error will occur if there are dependent
2315 : : * role membership grants; if DROP_CASCADE, those grants will be scheduled
2316 : : * for deletion.
2317 : : *
2318 : : * The return value is true if the matching grant was found in the list,
2319 : : * and false if not.
2320 : : */
2321 : : static bool
2322 : 26 : plan_single_revoke(CatCList *memlist, RevokeRoleGrantAction *actions,
2323 : : Oid member, Oid grantor, GrantRoleOptions *popt,
2324 : : DropBehavior behavior)
2325 : : {
2326 : 26 : int i;
2327 : :
2328 : : /*
2329 : : * If popt.specified == 0, we're revoking the grant entirely; otherwise,
2330 : : * we expect just one bit to be set, and we're revoking the corresponding
2331 : : * option. As of this writing, there's no syntax that would allow for an
2332 : : * attempt to revoke multiple options at once, and the logic below
2333 : : * wouldn't work properly if such syntax were added, so assert that our
2334 : : * caller isn't trying to do that.
2335 : : */
2336 [ + - ]: 26 : Assert(pg_popcount32(popt->specified) <= 1);
2337 : :
2338 [ + + ]: 45 : for (i = 0; i < memlist->n_members; ++i)
2339 : : {
2340 : 44 : HeapTuple authmem_tuple;
2341 : 44 : Form_pg_auth_members authmem_form;
2342 : :
2343 : 44 : authmem_tuple = &memlist->members[i]->tuple;
2344 : 44 : authmem_form = (Form_pg_auth_members) GETSTRUCT(authmem_tuple);
2345 : :
2346 [ + + + + ]: 44 : if (authmem_form->member == member &&
2347 : 28 : authmem_form->grantor == grantor)
2348 : : {
2349 [ + + ]: 25 : if ((popt->specified & GRANT_ROLE_SPECIFIED_INHERIT) != 0)
2350 : : {
2351 : : /*
2352 : : * Revoking the INHERIT option doesn't change anything for
2353 : : * dependent privileges, so we don't need to recurse.
2354 : : */
2355 : 2 : actions[i] = RRG_REMOVE_INHERIT_OPTION;
2356 : 2 : }
2357 [ + + ]: 23 : else if ((popt->specified & GRANT_ROLE_SPECIFIED_SET) != 0)
2358 : : {
2359 : : /* Here too, no need to recurse. */
2360 : 1 : actions[i] = RRG_REMOVE_SET_OPTION;
2361 : 1 : }
2362 : : else
2363 : : {
2364 : 22 : bool revoke_admin_option_only;
2365 : :
2366 : : /*
2367 : : * Revoking the grant entirely, or ADMIN option on a grant,
2368 : : * implicates dependent privileges, so we may need to recurse.
2369 : : */
2370 : 22 : revoke_admin_option_only =
2371 : 22 : (popt->specified & GRANT_ROLE_SPECIFIED_ADMIN) != 0;
2372 : 44 : plan_recursive_revoke(memlist, actions, i,
2373 : 22 : revoke_admin_option_only, behavior);
2374 : 22 : }
2375 : 25 : return true;
2376 : : }
2377 [ + + ]: 44 : }
2378 : :
2379 : 1 : return false;
2380 : 26 : }
2381 : :
2382 : : /*
2383 : : * Figure out what we would need to do in order to revoke all grants to
2384 : : * a given member, given that there might be dependent privileges.
2385 : : *
2386 : : * 'memlist' should be a list of all grants for the target role.
2387 : : *
2388 : : * Whatever actions prove to be necessary will be signalled by updating
2389 : : * 'actions'.
2390 : : */
2391 : : static void
2392 : 14 : plan_member_revoke(CatCList *memlist, RevokeRoleGrantAction *actions,
2393 : : Oid member)
2394 : : {
2395 : 14 : int i;
2396 : :
2397 [ + + ]: 31 : for (i = 0; i < memlist->n_members; ++i)
2398 : : {
2399 : 17 : HeapTuple authmem_tuple;
2400 : 17 : Form_pg_auth_members authmem_form;
2401 : :
2402 : 17 : authmem_tuple = &memlist->members[i]->tuple;
2403 : 17 : authmem_form = (Form_pg_auth_members) GETSTRUCT(authmem_tuple);
2404 : :
2405 [ + + ]: 17 : if (authmem_form->member == member)
2406 : 1 : plan_recursive_revoke(memlist, actions, i, false, DROP_CASCADE);
2407 : 17 : }
2408 : 14 : }
2409 : :
2410 : : /*
2411 : : * Workhorse for figuring out recursive revocation of role grants.
2412 : : *
2413 : : * This is similar to what recursive_revoke() does for ACLs.
2414 : : */
2415 : : static void
2416 : 27 : plan_recursive_revoke(CatCList *memlist, RevokeRoleGrantAction *actions,
2417 : : int index,
2418 : : bool revoke_admin_option_only, DropBehavior behavior)
2419 : : {
2420 : 27 : bool would_still_have_admin_option = false;
2421 : 27 : HeapTuple authmem_tuple;
2422 : 27 : Form_pg_auth_members authmem_form;
2423 : 27 : int i;
2424 : :
2425 : : /* If it's already been done, we can just return. */
2426 [ - + ]: 27 : if (actions[index] == RRG_DELETE_GRANT)
2427 : 0 : return;
2428 [ - + # # ]: 27 : if (actions[index] == RRG_REMOVE_ADMIN_OPTION &&
2429 : 0 : revoke_admin_option_only)
2430 : 0 : return;
2431 : :
2432 : : /* Locate tuple data. */
2433 : 27 : authmem_tuple = &memlist->members[index]->tuple;
2434 : 27 : authmem_form = (Form_pg_auth_members) GETSTRUCT(authmem_tuple);
2435 : :
2436 : : /*
2437 : : * If the existing tuple does not have admin_option set, then we do not
2438 : : * need to recurse. If we're just supposed to clear that bit we don't need
2439 : : * to do anything at all; if we're supposed to remove the grant, we need
2440 : : * to do something, but only to the tuple, and not any others.
2441 : : */
2442 [ + + ]: 27 : if (!revoke_admin_option_only)
2443 : : {
2444 : 21 : actions[index] = RRG_DELETE_GRANT;
2445 [ + + ]: 21 : if (!authmem_form->admin_option)
2446 : 14 : return;
2447 : 7 : }
2448 : : else
2449 : : {
2450 [ + - ]: 6 : if (!authmem_form->admin_option)
2451 : 0 : return;
2452 : 6 : actions[index] = RRG_REMOVE_ADMIN_OPTION;
2453 : : }
2454 : :
2455 : : /* Determine whether the member would still have ADMIN OPTION. */
2456 [ + + ]: 38 : for (i = 0; i < memlist->n_members; ++i)
2457 : : {
2458 : 25 : HeapTuple am_cascade_tuple;
2459 : 25 : Form_pg_auth_members am_cascade_form;
2460 : :
2461 : 25 : am_cascade_tuple = &memlist->members[i]->tuple;
2462 : 25 : am_cascade_form = (Form_pg_auth_members) GETSTRUCT(am_cascade_tuple);
2463 : :
2464 [ + + ]: 25 : if (am_cascade_form->member == authmem_form->member &&
2465 [ + - + - ]: 13 : am_cascade_form->admin_option && actions[i] == RRG_NOOP)
2466 : : {
2467 : 0 : would_still_have_admin_option = true;
2468 : 0 : break;
2469 : : }
2470 [ - + ]: 25 : }
2471 : :
2472 : : /* If the member would still have ADMIN OPTION, we need not recurse. */
2473 [ - + ]: 13 : if (would_still_have_admin_option)
2474 : 0 : return;
2475 : :
2476 : : /*
2477 : : * Recurse to grants that are not yet slated for deletion which have this
2478 : : * member as the grantor.
2479 : : */
2480 [ + + ]: 36 : for (i = 0; i < memlist->n_members; ++i)
2481 : : {
2482 : 25 : HeapTuple am_cascade_tuple;
2483 : 25 : Form_pg_auth_members am_cascade_form;
2484 : :
2485 : 25 : am_cascade_tuple = &memlist->members[i]->tuple;
2486 : 25 : am_cascade_form = (Form_pg_auth_members) GETSTRUCT(am_cascade_tuple);
2487 : :
2488 [ + + - + ]: 25 : if (am_cascade_form->grantor == authmem_form->member &&
2489 : 6 : actions[i] != RRG_DELETE_GRANT)
2490 : : {
2491 [ + + ]: 6 : if (behavior == DROP_RESTRICT)
2492 [ + - + - ]: 2 : ereport(ERROR,
2493 : : (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
2494 : : errmsg("dependent privileges exist"),
2495 : : errhint("Use CASCADE to revoke them too.")));
2496 : :
2497 : 4 : plan_recursive_revoke(memlist, actions, i, false, behavior);
2498 : 4 : }
2499 : 23 : }
2500 : 25 : }
2501 : :
2502 : : /*
2503 : : * Initialize a GrantRoleOptions object with default values.
2504 : : */
2505 : : static void
2506 : 368 : InitGrantRoleOptions(GrantRoleOptions *popt)
2507 : : {
2508 : 368 : popt->specified = 0;
2509 : 368 : popt->admin = false;
2510 : 368 : popt->inherit = false;
2511 : 368 : popt->set = true;
2512 : 368 : }
2513 : :
2514 : : /*
2515 : : * GUC check_hook for createrole_self_grant
2516 : : */
2517 : : bool
2518 : 7 : check_createrole_self_grant(char **newval, void **extra, GucSource source)
2519 : : {
2520 : 7 : char *rawstring;
2521 : 7 : List *elemlist;
2522 : 7 : ListCell *l;
2523 : 7 : unsigned options = 0;
2524 : 7 : unsigned *result;
2525 : :
2526 : : /* Need a modifiable copy of string */
2527 : 7 : rawstring = pstrdup(*newval);
2528 : :
2529 [ + - ]: 7 : if (!SplitIdentifierString(rawstring, ',', &elemlist))
2530 : : {
2531 : : /* syntax error in list */
2532 : 0 : GUC_check_errdetail("List syntax is invalid.");
2533 : 0 : pfree(rawstring);
2534 : 0 : list_free(elemlist);
2535 : 0 : return false;
2536 : : }
2537 : :
2538 [ + + + + : 9 : foreach(l, elemlist)
+ + - + ]
2539 : : {
2540 : 2 : char *tok = (char *) lfirst(l);
2541 : :
2542 [ + + ]: 2 : if (pg_strcasecmp(tok, "SET") == 0)
2543 : 1 : options |= GRANT_ROLE_SPECIFIED_SET;
2544 [ - + ]: 1 : else if (pg_strcasecmp(tok, "INHERIT") == 0)
2545 : 1 : options |= GRANT_ROLE_SPECIFIED_INHERIT;
2546 : : else
2547 : : {
2548 : 0 : GUC_check_errdetail("Unrecognized key word: \"%s\".", tok);
2549 : 0 : pfree(rawstring);
2550 : 0 : list_free(elemlist);
2551 : 0 : return false;
2552 : : }
2553 [ - + ]: 2 : }
2554 : :
2555 : 7 : pfree(rawstring);
2556 : 7 : list_free(elemlist);
2557 : :
2558 : 7 : result = (unsigned *) guc_malloc(LOG, sizeof(unsigned));
2559 [ + - ]: 7 : if (!result)
2560 : 0 : return false;
2561 : 7 : *result = options;
2562 : 7 : *extra = result;
2563 : :
2564 : 7 : return true;
2565 : 7 : }
2566 : :
2567 : : /*
2568 : : * GUC assign_hook for createrole_self_grant
2569 : : */
2570 : : void
2571 : 7 : assign_createrole_self_grant(const char *newval, void *extra)
2572 : : {
2573 : 7 : unsigned options = *(unsigned *) extra;
2574 : :
2575 : 7 : createrole_self_grant_enabled = (options != 0);
2576 : 7 : createrole_self_grant_options.specified = GRANT_ROLE_SPECIFIED_ADMIN
2577 : : | GRANT_ROLE_SPECIFIED_INHERIT
2578 : : | GRANT_ROLE_SPECIFIED_SET;
2579 : 7 : createrole_self_grant_options.admin = false;
2580 : 7 : createrole_self_grant_options.inherit =
2581 : 7 : (options & GRANT_ROLE_SPECIFIED_INHERIT) != 0;
2582 : 7 : createrole_self_grant_options.set =
2583 : 7 : (options & GRANT_ROLE_SPECIFIED_SET) != 0;
2584 : 7 : }
|