1 /*-------------------------------------------------------------------------
4 * Commands for manipulating roles (formerly called users).
6 * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
9 * $PostgreSQL: pgsql/src/backend/commands/user.c,v 1.164 2005/11/04 17:25:15 tgl Exp $
11 *-------------------------------------------------------------------------
15 #include "access/genam.h"
16 #include "access/heapam.h"
17 #include "catalog/dependency.h"
18 #include "catalog/indexing.h"
19 #include "catalog/pg_auth_members.h"
20 #include "catalog/pg_authid.h"
21 #include "commands/user.h"
22 #include "libpq/crypt.h"
23 #include "miscadmin.h"
24 #include "utils/acl.h"
25 #include "utils/builtins.h"
26 #include "utils/flatfiles.h"
27 #include "utils/fmgroids.h"
28 #include "utils/guc.h"
29 #include "utils/lsyscache.h"
30 #include "utils/syscache.h"
33 extern bool Password_encryption;
35 static List *roleNamesToIds(List *memberNames);
36 static void AddRoleMems(const char *rolename, Oid roleid,
37 List *memberNames, List *memberIds,
38 Oid grantorId, bool admin_opt);
39 static void DelRoleMems(const char *rolename, Oid roleid,
40 List *memberNames, List *memberIds,
44 /* Check if current user has createrole privileges */
46 have_createrole_privilege(void)
51 /* Superusers can always do everything */
55 utup = SearchSysCache(AUTHOID,
56 ObjectIdGetDatum(GetUserId()),
58 if (HeapTupleIsValid(utup))
60 result = ((Form_pg_authid) GETSTRUCT(utup))->rolcreaterole;
61 ReleaseSysCache(utup);
71 CreateRole(CreateRoleStmt *stmt)
73 Relation pg_authid_rel;
74 TupleDesc pg_authid_dsc;
76 Datum new_record[Natts_pg_authid];
77 char new_record_nulls[Natts_pg_authid];
81 char *password = NULL; /* user password */
82 bool encrypt_password = Password_encryption; /* encrypt password? */
83 char encrypted_password[MD5_PASSWD_LEN + 1];
84 bool issuper = false; /* Make the user a superuser? */
85 bool inherit = true; /* Auto inherit privileges? */
86 bool createrole = false; /* Can this user create roles? */
87 bool createdb = false; /* Can the user create databases? */
88 bool canlogin = false; /* Can this user login? */
89 int connlimit = -1; /* maximum connections allowed */
90 List *addroleto = NIL; /* roles to make this a member of */
91 List *rolemembers = NIL; /* roles to be members of this role */
92 List *adminmembers = NIL; /* roles to be admins of this role */
93 char *validUntil = NULL; /* time the login is valid until */
94 DefElem *dpassword = NULL;
95 DefElem *dissuper = NULL;
96 DefElem *dinherit = NULL;
97 DefElem *dcreaterole = NULL;
98 DefElem *dcreatedb = NULL;
99 DefElem *dcanlogin = NULL;
100 DefElem *dconnlimit = NULL;
101 DefElem *daddroleto = NULL;
102 DefElem *drolemembers = NULL;
103 DefElem *dadminmembers = NULL;
104 DefElem *dvalidUntil = NULL;
106 /* The defaults can vary depending on the original statement type */
107 switch (stmt->stmt_type)
113 /* may eventually want inherit to default to false here */
119 /* Extract options from the statement node tree */
120 foreach(option, stmt->options)
122 DefElem *defel = (DefElem *) lfirst(option);
124 if (strcmp(defel->defname, "password") == 0 ||
125 strcmp(defel->defname, "encryptedPassword") == 0 ||
126 strcmp(defel->defname, "unencryptedPassword") == 0)
130 (errcode(ERRCODE_SYNTAX_ERROR),
131 errmsg("conflicting or redundant options")));
133 if (strcmp(defel->defname, "encryptedPassword") == 0)
134 encrypt_password = true;
135 else if (strcmp(defel->defname, "unencryptedPassword") == 0)
136 encrypt_password = false;
138 else if (strcmp(defel->defname, "sysid") == 0)
141 (errmsg("SYSID can no longer be specified")));
143 else if (strcmp(defel->defname, "superuser") == 0)
147 (errcode(ERRCODE_SYNTAX_ERROR),
148 errmsg("conflicting or redundant options")));
151 else if (strcmp(defel->defname, "inherit") == 0)
155 (errcode(ERRCODE_SYNTAX_ERROR),
156 errmsg("conflicting or redundant options")));
159 else if (strcmp(defel->defname, "createrole") == 0)
163 (errcode(ERRCODE_SYNTAX_ERROR),
164 errmsg("conflicting or redundant options")));
167 else if (strcmp(defel->defname, "createdb") == 0)
171 (errcode(ERRCODE_SYNTAX_ERROR),
172 errmsg("conflicting or redundant options")));
175 else if (strcmp(defel->defname, "canlogin") == 0)
179 (errcode(ERRCODE_SYNTAX_ERROR),
180 errmsg("conflicting or redundant options")));
183 else if (strcmp(defel->defname, "connectionlimit") == 0)
187 (errcode(ERRCODE_SYNTAX_ERROR),
188 errmsg("conflicting or redundant options")));
191 else if (strcmp(defel->defname, "addroleto") == 0)
195 (errcode(ERRCODE_SYNTAX_ERROR),
196 errmsg("conflicting or redundant options")));
199 else if (strcmp(defel->defname, "rolemembers") == 0)
203 (errcode(ERRCODE_SYNTAX_ERROR),
204 errmsg("conflicting or redundant options")));
205 drolemembers = defel;
207 else if (strcmp(defel->defname, "adminmembers") == 0)
211 (errcode(ERRCODE_SYNTAX_ERROR),
212 errmsg("conflicting or redundant options")));
213 dadminmembers = defel;
215 else if (strcmp(defel->defname, "validUntil") == 0)
219 (errcode(ERRCODE_SYNTAX_ERROR),
220 errmsg("conflicting or redundant options")));
224 elog(ERROR, "option \"%s\" not recognized",
229 password = strVal(dpassword->arg);
231 issuper = intVal(dissuper->arg) != 0;
233 inherit = intVal(dinherit->arg) != 0;
235 createrole = intVal(dcreaterole->arg) != 0;
237 createdb = intVal(dcreatedb->arg) != 0;
239 canlogin = intVal(dcanlogin->arg) != 0;
241 connlimit = intVal(dconnlimit->arg);
243 addroleto = (List *) daddroleto->arg;
245 rolemembers = (List *) drolemembers->arg;
247 adminmembers = (List *) dadminmembers->arg;
249 validUntil = strVal(dvalidUntil->arg);
251 /* Check some permissions first */
256 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
257 errmsg("must be superuser to create superusers")));
261 if (!have_createrole_privilege())
263 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
264 errmsg("permission denied to create role")));
267 if (strcmp(stmt->role, "public") == 0 ||
268 strcmp(stmt->role, "none") == 0)
270 (errcode(ERRCODE_RESERVED_NAME),
271 errmsg("role name \"%s\" is reserved",
275 * Check the pg_authid relation to be certain the role doesn't already
276 * exist. Note we secure exclusive lock because we need to protect our
277 * eventual update of the flat auth file.
279 pg_authid_rel = heap_open(AuthIdRelationId, ExclusiveLock);
280 pg_authid_dsc = RelationGetDescr(pg_authid_rel);
282 tuple = SearchSysCache(AUTHNAME,
283 PointerGetDatum(stmt->role),
285 if (HeapTupleIsValid(tuple))
287 (errcode(ERRCODE_DUPLICATE_OBJECT),
288 errmsg("role \"%s\" already exists",
292 * Build a tuple to insert
294 MemSet(new_record, 0, sizeof(new_record));
295 MemSet(new_record_nulls, ' ', sizeof(new_record_nulls));
297 new_record[Anum_pg_authid_rolname - 1] =
298 DirectFunctionCall1(namein, CStringGetDatum(stmt->role));
300 new_record[Anum_pg_authid_rolsuper - 1] = BoolGetDatum(issuper);
301 new_record[Anum_pg_authid_rolinherit - 1] = BoolGetDatum(inherit);
302 new_record[Anum_pg_authid_rolcreaterole - 1] = BoolGetDatum(createrole);
303 new_record[Anum_pg_authid_rolcreatedb - 1] = BoolGetDatum(createdb);
304 /* superuser gets catupdate right by default */
305 new_record[Anum_pg_authid_rolcatupdate - 1] = BoolGetDatum(issuper);
306 new_record[Anum_pg_authid_rolcanlogin - 1] = BoolGetDatum(canlogin);
307 new_record[Anum_pg_authid_rolconnlimit - 1] = Int32GetDatum(connlimit);
311 if (!encrypt_password || isMD5(password))
312 new_record[Anum_pg_authid_rolpassword - 1] =
313 DirectFunctionCall1(textin, CStringGetDatum(password));
316 if (!pg_md5_encrypt(password, stmt->role, strlen(stmt->role),
318 elog(ERROR, "password encryption failed");
319 new_record[Anum_pg_authid_rolpassword - 1] =
320 DirectFunctionCall1(textin, CStringGetDatum(encrypted_password));
324 new_record_nulls[Anum_pg_authid_rolpassword - 1] = 'n';
327 new_record[Anum_pg_authid_rolvaliduntil - 1] =
328 DirectFunctionCall3(timestamptz_in,
329 CStringGetDatum(validUntil),
330 ObjectIdGetDatum(InvalidOid),
334 new_record_nulls[Anum_pg_authid_rolvaliduntil - 1] = 'n';
336 new_record_nulls[Anum_pg_authid_rolconfig - 1] = 'n';
338 tuple = heap_formtuple(pg_authid_dsc, new_record, new_record_nulls);
341 * Insert new record in the pg_authid table
343 roleid = simple_heap_insert(pg_authid_rel, tuple);
344 CatalogUpdateIndexes(pg_authid_rel, tuple);
347 * Advance command counter so we can see new record; else tests in
348 * AddRoleMems may fail.
350 if (addroleto || adminmembers || rolemembers)
351 CommandCounterIncrement();
354 * Add the new role to the specified existing roles.
356 foreach(item, addroleto)
358 char *oldrolename = strVal(lfirst(item));
359 Oid oldroleid = get_roleid_checked(oldrolename);
361 AddRoleMems(oldrolename, oldroleid,
362 list_make1(makeString(stmt->role)),
363 list_make1_oid(roleid),
368 * Add the specified members to this new role. adminmembers get the admin
369 * option, rolemembers don't.
371 AddRoleMems(stmt->role, roleid,
372 adminmembers, roleNamesToIds(adminmembers),
374 AddRoleMems(stmt->role, roleid,
375 rolemembers, roleNamesToIds(rolemembers),
379 * Now we can clean up; but keep lock until commit (to avoid possible
380 * deadlock when commit code tries to acquire lock).
382 heap_close(pg_authid_rel, NoLock);
385 * Set flag to update flat auth file at commit.
387 auth_file_update_needed();
394 * Note: the rolemembers option accepted here is intended to support the
395 * backwards-compatible ALTER GROUP syntax. Although it will work to say
396 * "ALTER ROLE role ROLE rolenames", we don't document it.
399 AlterRole(AlterRoleStmt *stmt)
401 Datum new_record[Natts_pg_authid];
402 char new_record_nulls[Natts_pg_authid];
403 char new_record_repl[Natts_pg_authid];
404 Relation pg_authid_rel;
405 TupleDesc pg_authid_dsc;
409 char *password = NULL; /* user password */
410 bool encrypt_password = Password_encryption; /* encrypt password? */
411 char encrypted_password[MD5_PASSWD_LEN + 1];
412 int issuper = -1; /* Make the user a superuser? */
413 int inherit = -1; /* Auto inherit privileges? */
414 int createrole = -1; /* Can this user create roles? */
415 int createdb = -1; /* Can the user create databases? */
416 int canlogin = -1; /* Can this user login? */
417 int connlimit = -1; /* maximum connections allowed */
418 List *rolemembers = NIL; /* roles to be added/removed */
419 char *validUntil = NULL; /* time the login is valid until */
420 DefElem *dpassword = NULL;
421 DefElem *dissuper = NULL;
422 DefElem *dinherit = NULL;
423 DefElem *dcreaterole = NULL;
424 DefElem *dcreatedb = NULL;
425 DefElem *dcanlogin = NULL;
426 DefElem *dconnlimit = NULL;
427 DefElem *drolemembers = NULL;
428 DefElem *dvalidUntil = NULL;
431 /* Extract options from the statement node tree */
432 foreach(option, stmt->options)
434 DefElem *defel = (DefElem *) lfirst(option);
436 if (strcmp(defel->defname, "password") == 0 ||
437 strcmp(defel->defname, "encryptedPassword") == 0 ||
438 strcmp(defel->defname, "unencryptedPassword") == 0)
442 (errcode(ERRCODE_SYNTAX_ERROR),
443 errmsg("conflicting or redundant options")));
445 if (strcmp(defel->defname, "encryptedPassword") == 0)
446 encrypt_password = true;
447 else if (strcmp(defel->defname, "unencryptedPassword") == 0)
448 encrypt_password = false;
450 else if (strcmp(defel->defname, "superuser") == 0)
454 (errcode(ERRCODE_SYNTAX_ERROR),
455 errmsg("conflicting or redundant options")));
458 else if (strcmp(defel->defname, "inherit") == 0)
462 (errcode(ERRCODE_SYNTAX_ERROR),
463 errmsg("conflicting or redundant options")));
466 else if (strcmp(defel->defname, "createrole") == 0)
470 (errcode(ERRCODE_SYNTAX_ERROR),
471 errmsg("conflicting or redundant options")));
474 else if (strcmp(defel->defname, "createdb") == 0)
478 (errcode(ERRCODE_SYNTAX_ERROR),
479 errmsg("conflicting or redundant options")));
482 else if (strcmp(defel->defname, "canlogin") == 0)
486 (errcode(ERRCODE_SYNTAX_ERROR),
487 errmsg("conflicting or redundant options")));
490 else if (strcmp(defel->defname, "connectionlimit") == 0)
494 (errcode(ERRCODE_SYNTAX_ERROR),
495 errmsg("conflicting or redundant options")));
498 else if (strcmp(defel->defname, "rolemembers") == 0 &&
503 (errcode(ERRCODE_SYNTAX_ERROR),
504 errmsg("conflicting or redundant options")));
505 drolemembers = defel;
507 else if (strcmp(defel->defname, "validUntil") == 0)
511 (errcode(ERRCODE_SYNTAX_ERROR),
512 errmsg("conflicting or redundant options")));
516 elog(ERROR, "option \"%s\" not recognized",
521 password = strVal(dpassword->arg);
523 issuper = intVal(dissuper->arg);
525 inherit = intVal(dinherit->arg);
527 createrole = intVal(dcreaterole->arg);
529 createdb = intVal(dcreatedb->arg);
531 canlogin = intVal(dcanlogin->arg);
533 connlimit = intVal(dconnlimit->arg);
535 rolemembers = (List *) drolemembers->arg;
537 validUntil = strVal(dvalidUntil->arg);
540 * Scan the pg_authid relation to be certain the user exists. Note we
541 * secure exclusive lock to protect our update of the flat auth file.
543 pg_authid_rel = heap_open(AuthIdRelationId, ExclusiveLock);
544 pg_authid_dsc = RelationGetDescr(pg_authid_rel);
546 tuple = SearchSysCache(AUTHNAME,
547 PointerGetDatum(stmt->role),
549 if (!HeapTupleIsValid(tuple))
551 (errcode(ERRCODE_UNDEFINED_OBJECT),
552 errmsg("role \"%s\" does not exist", stmt->role)));
554 roleid = HeapTupleGetOid(tuple);
557 * To mess with a superuser you gotta be superuser; else you need
558 * createrole, or just want to change your own password
560 if (((Form_pg_authid) GETSTRUCT(tuple))->rolsuper || issuper >= 0)
564 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
565 errmsg("must be superuser to alter superusers")));
567 else if (!have_createrole_privilege())
577 roleid == GetUserId()))
579 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
580 errmsg("permission denied")));
584 * Build an updated tuple, perusing the information just obtained
586 MemSet(new_record, 0, sizeof(new_record));
587 MemSet(new_record_nulls, ' ', sizeof(new_record_nulls));
588 MemSet(new_record_repl, ' ', sizeof(new_record_repl));
591 * issuper/createrole/catupdate/etc
593 * XXX It's rather unclear how to handle catupdate. It's probably best to
594 * keep it equal to the superuser status, otherwise you could end up with
595 * a situation where no existing superuser can alter the catalogs,
596 * including pg_authid!
600 new_record[Anum_pg_authid_rolsuper - 1] = BoolGetDatum(issuper > 0);
601 new_record_repl[Anum_pg_authid_rolsuper - 1] = 'r';
603 new_record[Anum_pg_authid_rolcatupdate - 1] = BoolGetDatum(issuper > 0);
604 new_record_repl[Anum_pg_authid_rolcatupdate - 1] = 'r';
609 new_record[Anum_pg_authid_rolinherit - 1] = BoolGetDatum(inherit > 0);
610 new_record_repl[Anum_pg_authid_rolinherit - 1] = 'r';
615 new_record[Anum_pg_authid_rolcreaterole - 1] = BoolGetDatum(createrole > 0);
616 new_record_repl[Anum_pg_authid_rolcreaterole - 1] = 'r';
621 new_record[Anum_pg_authid_rolcreatedb - 1] = BoolGetDatum(createdb > 0);
622 new_record_repl[Anum_pg_authid_rolcreatedb - 1] = 'r';
627 new_record[Anum_pg_authid_rolcanlogin - 1] = BoolGetDatum(canlogin > 0);
628 new_record_repl[Anum_pg_authid_rolcanlogin - 1] = 'r';
633 new_record[Anum_pg_authid_rolconnlimit - 1] = Int32GetDatum(connlimit);
634 new_record_repl[Anum_pg_authid_rolconnlimit - 1] = 'r';
640 if (!encrypt_password || isMD5(password))
641 new_record[Anum_pg_authid_rolpassword - 1] =
642 DirectFunctionCall1(textin, CStringGetDatum(password));
645 if (!pg_md5_encrypt(password, stmt->role, strlen(stmt->role),
647 elog(ERROR, "password encryption failed");
648 new_record[Anum_pg_authid_rolpassword - 1] =
649 DirectFunctionCall1(textin, CStringGetDatum(encrypted_password));
651 new_record_repl[Anum_pg_authid_rolpassword - 1] = 'r';
657 new_record[Anum_pg_authid_rolvaliduntil - 1] =
658 DirectFunctionCall3(timestamptz_in,
659 CStringGetDatum(validUntil),
660 ObjectIdGetDatum(InvalidOid),
662 new_record_repl[Anum_pg_authid_rolvaliduntil - 1] = 'r';
665 new_tuple = heap_modifytuple(tuple, pg_authid_dsc, new_record,
666 new_record_nulls, new_record_repl);
667 simple_heap_update(pg_authid_rel, &tuple->t_self, new_tuple);
670 CatalogUpdateIndexes(pg_authid_rel, new_tuple);
672 ReleaseSysCache(tuple);
673 heap_freetuple(new_tuple);
676 * Advance command counter so we can see new record; else tests in
677 * AddRoleMems may fail.
680 CommandCounterIncrement();
682 if (stmt->action == +1) /* add members to role */
683 AddRoleMems(stmt->role, roleid,
684 rolemembers, roleNamesToIds(rolemembers),
686 else if (stmt->action == -1) /* drop members from role */
687 DelRoleMems(stmt->role, roleid,
688 rolemembers, roleNamesToIds(rolemembers),
692 * Now we can clean up; but keep lock until commit (to avoid possible
693 * deadlock when commit code tries to acquire lock).
695 heap_close(pg_authid_rel, NoLock);
698 * Set flag to update flat auth file at commit.
700 auth_file_update_needed();
708 AlterRoleSet(AlterRoleSetStmt *stmt)
714 Datum repl_val[Natts_pg_authid];
715 char repl_null[Natts_pg_authid];
716 char repl_repl[Natts_pg_authid];
719 valuestr = flatten_set_variable_args(stmt->variable, stmt->value);
722 * RowExclusiveLock is sufficient, because we don't need to update the
725 rel = heap_open(AuthIdRelationId, RowExclusiveLock);
726 oldtuple = SearchSysCache(AUTHNAME,
727 PointerGetDatum(stmt->role),
729 if (!HeapTupleIsValid(oldtuple))
731 (errcode(ERRCODE_UNDEFINED_OBJECT),
732 errmsg("role \"%s\" does not exist", stmt->role)));
735 * To mess with a superuser you gotta be superuser; else you need
736 * createrole, or just want to change your own settings
738 if (((Form_pg_authid) GETSTRUCT(oldtuple))->rolsuper)
742 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
743 errmsg("must be superuser to alter superusers")));
747 if (!have_createrole_privilege() &&
748 HeapTupleGetOid(oldtuple) != GetUserId())
750 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
751 errmsg("permission denied")));
754 for (i = 0; i < Natts_pg_authid; i++)
757 repl_repl[Anum_pg_authid_rolconfig - 1] = 'r';
758 if (strcmp(stmt->variable, "all") == 0 && valuestr == NULL)
761 repl_null[Anum_pg_authid_rolconfig - 1] = 'n';
769 repl_null[Anum_pg_authid_rolconfig - 1] = ' ';
771 datum = SysCacheGetAttr(AUTHNAME, oldtuple,
772 Anum_pg_authid_rolconfig, &isnull);
774 array = isnull ? NULL : DatumGetArrayTypeP(datum);
777 array = GUCArrayAdd(array, stmt->variable, valuestr);
779 array = GUCArrayDelete(array, stmt->variable);
782 repl_val[Anum_pg_authid_rolconfig - 1] = PointerGetDatum(array);
784 repl_null[Anum_pg_authid_rolconfig - 1] = 'n';
787 newtuple = heap_modifytuple(oldtuple, RelationGetDescr(rel),
788 repl_val, repl_null, repl_repl);
790 simple_heap_update(rel, &oldtuple->t_self, newtuple);
791 CatalogUpdateIndexes(rel, newtuple);
793 ReleaseSysCache(oldtuple);
794 heap_close(rel, RowExclusiveLock);
802 DropRole(DropRoleStmt *stmt)
804 Relation pg_authid_rel,
808 if (!have_createrole_privilege())
810 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
811 errmsg("permission denied to drop role")));
814 * Scan the pg_authid relation to find the Oid of the role(s) to be
815 * deleted. Note we secure exclusive lock on pg_authid, because we need
816 * to protect our update of the flat auth file. A regular writer's lock
817 * on pg_auth_members is sufficient though.
819 pg_authid_rel = heap_open(AuthIdRelationId, ExclusiveLock);
820 pg_auth_members_rel = heap_open(AuthMemRelationId, RowExclusiveLock);
822 foreach(item, stmt->roles)
824 const char *role = strVal(lfirst(item));
832 tuple = SearchSysCache(AUTHNAME,
833 PointerGetDatum(role),
835 if (!HeapTupleIsValid(tuple))
837 (errcode(ERRCODE_UNDEFINED_OBJECT),
838 errmsg("role \"%s\" does not exist", role)));
840 roleid = HeapTupleGetOid(tuple);
842 if (roleid == GetUserId())
844 (errcode(ERRCODE_OBJECT_IN_USE),
845 errmsg("current user cannot be dropped")));
846 if (roleid == GetOuterUserId())
848 (errcode(ERRCODE_OBJECT_IN_USE),
849 errmsg("current user cannot be dropped")));
850 if (roleid == GetSessionUserId())
852 (errcode(ERRCODE_OBJECT_IN_USE),
853 errmsg("session user cannot be dropped")));
856 * For safety's sake, we allow createrole holders to drop ordinary
857 * roles but not superuser roles. This is mainly to avoid the
858 * scenario where you accidentally drop the last superuser.
860 if (((Form_pg_authid) GETSTRUCT(tuple))->rolsuper &&
863 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
864 errmsg("must be superuser to drop superusers")));
867 * Lock the role, so nobody can add dependencies to her while we drop
868 * her. We keep the lock until the end of transaction.
870 LockSharedObject(AuthIdRelationId, roleid, 0, AccessExclusiveLock);
872 /* Check for pg_shdepend entries depending on this role */
873 if ((detail = checkSharedDependencies(AuthIdRelationId, roleid)) != NULL)
875 (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
876 errmsg("role \"%s\" cannot be dropped because some objects depend on it",
878 errdetail("%s", detail)));
881 * Remove the role from the pg_authid table
883 simple_heap_delete(pg_authid_rel, &tuple->t_self);
885 ReleaseSysCache(tuple);
888 * Remove role from the pg_auth_members table. We have to remove all
889 * tuples that show it as either a role or a member.
891 * XXX what about grantor entries? Maybe we should do one heap scan.
893 ScanKeyInit(&scankey,
894 Anum_pg_auth_members_roleid,
895 BTEqualStrategyNumber, F_OIDEQ,
896 ObjectIdGetDatum(roleid));
898 sscan = systable_beginscan(pg_auth_members_rel, AuthMemRoleMemIndexId,
899 true, SnapshotNow, 1, &scankey);
901 while (HeapTupleIsValid(tmp_tuple = systable_getnext(sscan)))
903 simple_heap_delete(pg_auth_members_rel, &tmp_tuple->t_self);
906 systable_endscan(sscan);
908 ScanKeyInit(&scankey,
909 Anum_pg_auth_members_member,
910 BTEqualStrategyNumber, F_OIDEQ,
911 ObjectIdGetDatum(roleid));
913 sscan = systable_beginscan(pg_auth_members_rel, AuthMemMemRoleIndexId,
914 true, SnapshotNow, 1, &scankey);
916 while (HeapTupleIsValid(tmp_tuple = systable_getnext(sscan)))
918 simple_heap_delete(pg_auth_members_rel, &tmp_tuple->t_self);
921 systable_endscan(sscan);
924 * Advance command counter so that later iterations of this loop will
925 * see the changes already made. This is essential if, for example,
926 * we are trying to drop both a role and one of its direct members ---
927 * we'll get an error if we try to delete the linking pg_auth_members
928 * tuple twice. (We do not need a CCI between the two delete loops
929 * above, because it's not allowed for a role to directly contain
932 CommandCounterIncrement();
936 * Now we can clean up; but keep locks until commit (to avoid possible
937 * deadlock when commit code tries to acquire lock).
939 heap_close(pg_auth_members_rel, NoLock);
940 heap_close(pg_authid_rel, NoLock);
943 * Set flag to update flat auth file at commit.
945 auth_file_update_needed();
952 RenameRole(const char *oldname, const char *newname)
960 Datum repl_val[Natts_pg_authid];
961 char repl_null[Natts_pg_authid];
962 char repl_repl[Natts_pg_authid];
966 /* ExclusiveLock because we need to update the flat auth file */
967 rel = heap_open(AuthIdRelationId, ExclusiveLock);
968 dsc = RelationGetDescr(rel);
970 oldtuple = SearchSysCache(AUTHNAME,
971 CStringGetDatum(oldname),
973 if (!HeapTupleIsValid(oldtuple))
975 (errcode(ERRCODE_UNDEFINED_OBJECT),
976 errmsg("role \"%s\" does not exist", oldname)));
979 * XXX Client applications probably store the session user somewhere, so
980 * renaming it could cause confusion. On the other hand, there may not be
981 * an actual problem besides a little confusion, so think about this and
982 * decide. Same for SET ROLE ... we don't restrict renaming the current
983 * effective userid, though.
986 roleid = HeapTupleGetOid(oldtuple);
988 if (roleid == GetSessionUserId())
990 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
991 errmsg("session user may not be renamed")));
992 if (roleid == GetOuterUserId())
994 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
995 errmsg("current user may not be renamed")));
997 /* make sure the new name doesn't exist */
998 if (SearchSysCacheExists(AUTHNAME,
999 CStringGetDatum(newname),
1002 (errcode(ERRCODE_DUPLICATE_OBJECT),
1003 errmsg("role \"%s\" already exists", newname)));
1005 if (strcmp(newname, "public") == 0 ||
1006 strcmp(newname, "none") == 0)
1008 (errcode(ERRCODE_RESERVED_NAME),
1009 errmsg("role name \"%s\" is reserved",
1013 * createrole is enough privilege unless you want to mess with a superuser
1015 if (((Form_pg_authid) GETSTRUCT(oldtuple))->rolsuper)
1019 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1020 errmsg("must be superuser to rename superusers")));
1024 if (!have_createrole_privilege())
1026 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1027 errmsg("permission denied to rename role")));
1030 /* OK, construct the modified tuple */
1031 for (i = 0; i < Natts_pg_authid; i++)
1034 repl_repl[Anum_pg_authid_rolname - 1] = 'r';
1035 repl_val[Anum_pg_authid_rolname - 1] = DirectFunctionCall1(namein,
1036 CStringGetDatum(newname));
1037 repl_null[Anum_pg_authid_rolname - 1] = ' ';
1039 datum = heap_getattr(oldtuple, Anum_pg_authid_rolpassword, dsc, &isnull);
1041 if (!isnull && isMD5(DatumGetCString(DirectFunctionCall1(textout, datum))))
1043 /* MD5 uses the username as salt, so just clear it on a rename */
1044 repl_repl[Anum_pg_authid_rolpassword - 1] = 'r';
1045 repl_null[Anum_pg_authid_rolpassword - 1] = 'n';
1048 (errmsg("MD5 password cleared because of role rename")));
1051 newtuple = heap_modifytuple(oldtuple, dsc, repl_val, repl_null, repl_repl);
1052 simple_heap_update(rel, &oldtuple->t_self, newtuple);
1054 CatalogUpdateIndexes(rel, newtuple);
1056 ReleaseSysCache(oldtuple);
1057 heap_close(rel, NoLock);
1060 * Set flag to update flat auth file at commit.
1062 auth_file_update_needed();
1068 * Grant/Revoke roles to/from roles
1071 GrantRole(GrantRoleStmt *stmt)
1073 Relation pg_authid_rel;
1079 grantor = get_roleid_checked(stmt->grantor);
1081 grantor = GetUserId();
1083 grantee_ids = roleNamesToIds(stmt->grantee_roles);
1086 * Even though this operation doesn't change pg_authid, we must secure
1087 * exclusive lock on it to protect our update of the flat auth file.
1089 pg_authid_rel = heap_open(AuthIdRelationId, ExclusiveLock);
1092 * Step through all of the granted roles and add/remove entries for the
1093 * grantees, or, if admin_opt is set, then just add/remove the admin
1096 * Note: Permissions checking is done by AddRoleMems/DelRoleMems
1098 foreach(item, stmt->granted_roles)
1100 char *rolename = strVal(lfirst(item));
1101 Oid roleid = get_roleid_checked(rolename);
1104 AddRoleMems(rolename, roleid,
1105 stmt->grantee_roles, grantee_ids,
1106 grantor, stmt->admin_opt);
1108 DelRoleMems(rolename, roleid,
1109 stmt->grantee_roles, grantee_ids,
1113 heap_close(pg_authid_rel, NoLock);
1116 * Set flag to update flat auth file at commit.
1118 auth_file_update_needed();
1124 * Given a list of role names (as String nodes), generate a list of role OIDs
1125 * in the same order.
1128 roleNamesToIds(List *memberNames)
1133 foreach(l, memberNames)
1135 char *rolename = strVal(lfirst(l));
1136 Oid roleid = get_roleid_checked(rolename);
1138 result = lappend_oid(result, roleid);
1144 * AddRoleMems -- Add given members to the specified role
1146 * rolename: name of role to add to (used only for error messages)
1147 * roleid: OID of role to add to
1148 * memberNames: list of names of roles to add (used only for error messages)
1149 * memberIds: OIDs of roles to add
1150 * grantorId: who is granting the membership
1151 * admin_opt: granting admin option?
1153 * Note: caller is responsible for holding ExclusiveLock on pg_authid,
1154 * and for calling auth_file_update_needed().
1157 AddRoleMems(const char *rolename, Oid roleid,
1158 List *memberNames, List *memberIds,
1159 Oid grantorId, bool admin_opt)
1161 Relation pg_authmem_rel;
1162 TupleDesc pg_authmem_dsc;
1166 Assert(list_length(memberNames) == list_length(memberIds));
1168 /* Skip permission check if nothing to do */
1173 * Check permissions: must have createrole or admin option on the role to
1174 * be changed. To mess with a superuser role, you gotta be superuser.
1176 if (superuser_arg(roleid))
1180 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1181 errmsg("must be superuser to alter superusers")));
1185 if (!have_createrole_privilege() &&
1186 !is_admin_of_role(grantorId, roleid))
1188 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1189 errmsg("must have admin option on role \"%s\"",
1193 /* XXX not sure about this check */
1194 if (grantorId != GetUserId() && !superuser())
1196 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1197 errmsg("must be superuser to set grantor")));
1199 /* We need only regular writer's lock on pg_auth_members */
1200 pg_authmem_rel = heap_open(AuthMemRelationId, RowExclusiveLock);
1201 pg_authmem_dsc = RelationGetDescr(pg_authmem_rel);
1203 forboth(nameitem, memberNames, iditem, memberIds)
1205 const char *membername = strVal(lfirst(nameitem));
1206 Oid memberid = lfirst_oid(iditem);
1207 HeapTuple authmem_tuple;
1209 Datum new_record[Natts_pg_auth_members];
1210 char new_record_nulls[Natts_pg_auth_members];
1211 char new_record_repl[Natts_pg_auth_members];
1214 * Refuse creation of membership loops, including the trivial case
1215 * where a role is made a member of itself. We do this by checking to
1216 * see if the target role is already a member of the proposed member
1217 * role. We have to ignore possible superuserness, however, else we
1218 * could never grant membership in a superuser-privileged role.
1220 if (is_member_of_role_nosuper(roleid, memberid))
1222 (errcode(ERRCODE_INVALID_GRANT_OPERATION),
1223 (errmsg("role \"%s\" is a member of role \"%s\"",
1224 rolename, membername))));
1227 * Check if entry for this role/member already exists; if so, give
1228 * warning unless we are adding admin option.
1230 authmem_tuple = SearchSysCache(AUTHMEMROLEMEM,
1231 ObjectIdGetDatum(roleid),
1232 ObjectIdGetDatum(memberid),
1234 if (HeapTupleIsValid(authmem_tuple) &&
1236 ((Form_pg_auth_members) GETSTRUCT(authmem_tuple))->admin_option))
1239 (errmsg("role \"%s\" is already a member of role \"%s\"",
1240 membername, rolename)));
1241 ReleaseSysCache(authmem_tuple);
1245 /* Build a tuple to insert or update */
1246 MemSet(new_record, 0, sizeof(new_record));
1247 MemSet(new_record_nulls, ' ', sizeof(new_record_nulls));
1248 MemSet(new_record_repl, ' ', sizeof(new_record_repl));
1250 new_record[Anum_pg_auth_members_roleid - 1] = ObjectIdGetDatum(roleid);
1251 new_record[Anum_pg_auth_members_member - 1] = ObjectIdGetDatum(memberid);
1252 new_record[Anum_pg_auth_members_grantor - 1] = ObjectIdGetDatum(grantorId);
1253 new_record[Anum_pg_auth_members_admin_option - 1] = BoolGetDatum(admin_opt);
1255 if (HeapTupleIsValid(authmem_tuple))
1257 new_record_repl[Anum_pg_auth_members_grantor - 1] = 'r';
1258 new_record_repl[Anum_pg_auth_members_admin_option - 1] = 'r';
1259 tuple = heap_modifytuple(authmem_tuple, pg_authmem_dsc,
1261 new_record_nulls, new_record_repl);
1262 simple_heap_update(pg_authmem_rel, &tuple->t_self, tuple);
1263 CatalogUpdateIndexes(pg_authmem_rel, tuple);
1264 ReleaseSysCache(authmem_tuple);
1268 tuple = heap_formtuple(pg_authmem_dsc,
1269 new_record, new_record_nulls);
1270 simple_heap_insert(pg_authmem_rel, tuple);
1271 CatalogUpdateIndexes(pg_authmem_rel, tuple);
1274 /* CCI after each change, in case there are duplicates in list */
1275 CommandCounterIncrement();
1279 * Now we can clean up; but keep lock until commit (to avoid possible
1280 * deadlock when commit code tries to acquire lock).
1282 heap_close(pg_authmem_rel, NoLock);
1286 * DelRoleMems -- Remove given members from the specified role
1288 * rolename: name of role to del from (used only for error messages)
1289 * roleid: OID of role to del from
1290 * memberNames: list of names of roles to del (used only for error messages)
1291 * memberIds: OIDs of roles to del
1292 * admin_opt: remove admin option only?
1294 * Note: caller is responsible for holding ExclusiveLock on pg_authid,
1295 * and for calling auth_file_update_needed().
1298 DelRoleMems(const char *rolename, Oid roleid,
1299 List *memberNames, List *memberIds,
1302 Relation pg_authmem_rel;
1303 TupleDesc pg_authmem_dsc;
1307 Assert(list_length(memberNames) == list_length(memberIds));
1309 /* Skip permission check if nothing to do */
1314 * Check permissions: must have createrole or admin option on the role to
1315 * be changed. To mess with a superuser role, you gotta be superuser.
1317 if (superuser_arg(roleid))
1321 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1322 errmsg("must be superuser to alter superusers")));
1326 if (!have_createrole_privilege() &&
1327 !is_admin_of_role(GetUserId(), roleid))
1329 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1330 errmsg("must have admin option on role \"%s\"",
1334 /* We need only regular writer's lock on pg_auth_members */
1335 pg_authmem_rel = heap_open(AuthMemRelationId, RowExclusiveLock);
1336 pg_authmem_dsc = RelationGetDescr(pg_authmem_rel);
1338 forboth(nameitem, memberNames, iditem, memberIds)
1340 const char *membername = strVal(lfirst(nameitem));
1341 Oid memberid = lfirst_oid(iditem);
1342 HeapTuple authmem_tuple;
1345 * Find entry for this role/member
1347 authmem_tuple = SearchSysCache(AUTHMEMROLEMEM,
1348 ObjectIdGetDatum(roleid),
1349 ObjectIdGetDatum(memberid),
1351 if (!HeapTupleIsValid(authmem_tuple))
1354 (errmsg("role \"%s\" is not a member of role \"%s\"",
1355 membername, rolename)));
1361 /* Remove the entry altogether */
1362 simple_heap_delete(pg_authmem_rel, &authmem_tuple->t_self);
1366 /* Just turn off the admin option */
1368 Datum new_record[Natts_pg_auth_members];
1369 char new_record_nulls[Natts_pg_auth_members];
1370 char new_record_repl[Natts_pg_auth_members];
1372 /* Build a tuple to update with */
1373 MemSet(new_record, 0, sizeof(new_record));
1374 MemSet(new_record_nulls, ' ', sizeof(new_record_nulls));
1375 MemSet(new_record_repl, ' ', sizeof(new_record_repl));
1377 new_record[Anum_pg_auth_members_admin_option - 1] = BoolGetDatum(false);
1378 new_record_repl[Anum_pg_auth_members_admin_option - 1] = 'r';
1380 tuple = heap_modifytuple(authmem_tuple, pg_authmem_dsc,
1382 new_record_nulls, new_record_repl);
1383 simple_heap_update(pg_authmem_rel, &tuple->t_self, tuple);
1384 CatalogUpdateIndexes(pg_authmem_rel, tuple);
1387 ReleaseSysCache(authmem_tuple);
1389 /* CCI after each change, in case there are duplicates in list */
1390 CommandCounterIncrement();
1394 * Now we can clean up; but keep lock until commit (to avoid possible
1395 * deadlock when commit code tries to acquire lock).
1397 heap_close(pg_authmem_rel, NoLock);