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.159 2005/07/26 22:37:49 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 List *addroleto = NIL; /* roles to make this a member of */
90 List *rolemembers = NIL; /* roles to be members of this role */
91 List *adminmembers = NIL; /* roles to be admins of this role */
92 char *validUntil = NULL; /* time the login is valid until */
93 DefElem *dpassword = NULL;
94 DefElem *dissuper = NULL;
95 DefElem *dinherit = NULL;
96 DefElem *dcreaterole = NULL;
97 DefElem *dcreatedb = NULL;
98 DefElem *dcanlogin = NULL;
99 DefElem *daddroleto = NULL;
100 DefElem *drolemembers = NULL;
101 DefElem *dadminmembers = NULL;
102 DefElem *dvalidUntil = NULL;
104 /* The defaults can vary depending on the original statement type */
105 switch (stmt->stmt_type)
111 /* may eventually want inherit to default to false here */
117 /* Extract options from the statement node tree */
118 foreach(option, stmt->options)
120 DefElem *defel = (DefElem *) lfirst(option);
122 if (strcmp(defel->defname, "password") == 0 ||
123 strcmp(defel->defname, "encryptedPassword") == 0 ||
124 strcmp(defel->defname, "unencryptedPassword") == 0)
128 (errcode(ERRCODE_SYNTAX_ERROR),
129 errmsg("conflicting or redundant options")));
131 if (strcmp(defel->defname, "encryptedPassword") == 0)
132 encrypt_password = true;
133 else if (strcmp(defel->defname, "unencryptedPassword") == 0)
134 encrypt_password = false;
136 else if (strcmp(defel->defname, "sysid") == 0)
139 (errmsg("SYSID can no longer be specified")));
141 else if (strcmp(defel->defname, "superuser") == 0)
145 (errcode(ERRCODE_SYNTAX_ERROR),
146 errmsg("conflicting or redundant options")));
149 else if (strcmp(defel->defname, "inherit") == 0)
153 (errcode(ERRCODE_SYNTAX_ERROR),
154 errmsg("conflicting or redundant options")));
157 else if (strcmp(defel->defname, "createrole") == 0)
161 (errcode(ERRCODE_SYNTAX_ERROR),
162 errmsg("conflicting or redundant options")));
165 else if (strcmp(defel->defname, "createdb") == 0)
169 (errcode(ERRCODE_SYNTAX_ERROR),
170 errmsg("conflicting or redundant options")));
173 else if (strcmp(defel->defname, "canlogin") == 0)
177 (errcode(ERRCODE_SYNTAX_ERROR),
178 errmsg("conflicting or redundant options")));
181 else if (strcmp(defel->defname, "addroleto") == 0)
185 (errcode(ERRCODE_SYNTAX_ERROR),
186 errmsg("conflicting or redundant options")));
189 else if (strcmp(defel->defname, "rolemembers") == 0)
193 (errcode(ERRCODE_SYNTAX_ERROR),
194 errmsg("conflicting or redundant options")));
195 drolemembers = defel;
197 else if (strcmp(defel->defname, "adminmembers") == 0)
201 (errcode(ERRCODE_SYNTAX_ERROR),
202 errmsg("conflicting or redundant options")));
203 dadminmembers = defel;
205 else if (strcmp(defel->defname, "validUntil") == 0)
209 (errcode(ERRCODE_SYNTAX_ERROR),
210 errmsg("conflicting or redundant options")));
214 elog(ERROR, "option \"%s\" not recognized",
219 password = strVal(dpassword->arg);
221 issuper = intVal(dissuper->arg) != 0;
223 inherit = intVal(dinherit->arg) != 0;
225 createrole = intVal(dcreaterole->arg) != 0;
227 createdb = intVal(dcreatedb->arg) != 0;
229 canlogin = intVal(dcanlogin->arg) != 0;
231 addroleto = (List *) daddroleto->arg;
233 rolemembers = (List *) drolemembers->arg;
235 adminmembers = (List *) dadminmembers->arg;
237 validUntil = strVal(dvalidUntil->arg);
239 /* Check some permissions first */
244 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
245 errmsg("must be superuser to create superusers")));
249 if (!have_createrole_privilege())
251 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
252 errmsg("permission denied to create role")));
255 if (strcmp(stmt->role, "public") == 0 ||
256 strcmp(stmt->role, "none") == 0)
258 (errcode(ERRCODE_RESERVED_NAME),
259 errmsg("role name \"%s\" is reserved",
263 * Check the pg_authid relation to be certain the role doesn't
264 * already exist. Note we secure exclusive lock because
265 * we need to protect our eventual update of the flat auth file.
267 pg_authid_rel = heap_open(AuthIdRelationId, ExclusiveLock);
268 pg_authid_dsc = RelationGetDescr(pg_authid_rel);
270 tuple = SearchSysCache(AUTHNAME,
271 PointerGetDatum(stmt->role),
273 if (HeapTupleIsValid(tuple))
275 (errcode(ERRCODE_DUPLICATE_OBJECT),
276 errmsg("role \"%s\" already exists",
280 * Build a tuple to insert
282 MemSet(new_record, 0, sizeof(new_record));
283 MemSet(new_record_nulls, ' ', sizeof(new_record_nulls));
285 new_record[Anum_pg_authid_rolname - 1] =
286 DirectFunctionCall1(namein, CStringGetDatum(stmt->role));
288 new_record[Anum_pg_authid_rolsuper - 1] = BoolGetDatum(issuper);
289 new_record[Anum_pg_authid_rolinherit - 1] = BoolGetDatum(inherit);
290 new_record[Anum_pg_authid_rolcreaterole - 1] = BoolGetDatum(createrole);
291 new_record[Anum_pg_authid_rolcreatedb - 1] = BoolGetDatum(createdb);
292 /* superuser gets catupdate right by default */
293 new_record[Anum_pg_authid_rolcatupdate - 1] = BoolGetDatum(issuper);
294 new_record[Anum_pg_authid_rolcanlogin - 1] = BoolGetDatum(canlogin);
298 if (!encrypt_password || isMD5(password))
299 new_record[Anum_pg_authid_rolpassword - 1] =
300 DirectFunctionCall1(textin, CStringGetDatum(password));
303 if (!EncryptMD5(password, stmt->role, strlen(stmt->role),
305 elog(ERROR, "password encryption failed");
306 new_record[Anum_pg_authid_rolpassword - 1] =
307 DirectFunctionCall1(textin, CStringGetDatum(encrypted_password));
311 new_record_nulls[Anum_pg_authid_rolpassword - 1] = 'n';
314 new_record[Anum_pg_authid_rolvaliduntil - 1] =
315 DirectFunctionCall3(timestamptz_in,
316 CStringGetDatum(validUntil),
317 ObjectIdGetDatum(InvalidOid),
321 new_record_nulls[Anum_pg_authid_rolvaliduntil - 1] = 'n';
323 new_record_nulls[Anum_pg_authid_rolconfig - 1] = 'n';
325 tuple = heap_formtuple(pg_authid_dsc, new_record, new_record_nulls);
328 * Insert new record in the pg_authid table
330 roleid = simple_heap_insert(pg_authid_rel, tuple);
331 CatalogUpdateIndexes(pg_authid_rel, tuple);
334 * Advance command counter so we can see new record; else tests
335 * in AddRoleMems may fail.
337 if (addroleto || adminmembers || rolemembers)
338 CommandCounterIncrement();
341 * Add the new role to the specified existing roles.
343 foreach(item, addroleto)
345 char *oldrolename = strVal(lfirst(item));
346 Oid oldroleid = get_roleid_checked(oldrolename);
348 AddRoleMems(oldrolename, oldroleid,
349 list_make1(makeString(stmt->role)),
350 list_make1_oid(roleid),
355 * Add the specified members to this new role. adminmembers get the
356 * admin option, rolemembers don't.
358 AddRoleMems(stmt->role, roleid,
359 adminmembers, roleNamesToIds(adminmembers),
361 AddRoleMems(stmt->role, roleid,
362 rolemembers, roleNamesToIds(rolemembers),
366 * Now we can clean up; but keep lock until commit (to avoid possible
367 * deadlock when commit code tries to acquire lock).
369 heap_close(pg_authid_rel, NoLock);
372 * Set flag to update flat auth file at commit.
374 auth_file_update_needed();
381 * Note: the rolemembers option accepted here is intended to support the
382 * backwards-compatible ALTER GROUP syntax. Although it will work to say
383 * "ALTER ROLE role ROLE rolenames", we don't document it.
386 AlterRole(AlterRoleStmt *stmt)
388 Datum new_record[Natts_pg_authid];
389 char new_record_nulls[Natts_pg_authid];
390 char new_record_repl[Natts_pg_authid];
391 Relation pg_authid_rel;
392 TupleDesc pg_authid_dsc;
396 char *password = NULL; /* user password */
397 bool encrypt_password = Password_encryption; /* encrypt password? */
398 char encrypted_password[MD5_PASSWD_LEN + 1];
399 int issuper = -1; /* Make the user a superuser? */
400 int inherit = -1; /* Auto inherit privileges? */
401 int createrole = -1; /* Can this user create roles? */
402 int createdb = -1; /* Can the user create databases? */
403 int canlogin = -1; /* Can this user login? */
404 List *rolemembers = NIL; /* roles to be added/removed */
405 char *validUntil = NULL; /* time the login is valid until */
406 DefElem *dpassword = NULL;
407 DefElem *dissuper = NULL;
408 DefElem *dinherit = NULL;
409 DefElem *dcreaterole = NULL;
410 DefElem *dcreatedb = NULL;
411 DefElem *dcanlogin = NULL;
412 DefElem *drolemembers = NULL;
413 DefElem *dvalidUntil = NULL;
416 /* Extract options from the statement node tree */
417 foreach(option, stmt->options)
419 DefElem *defel = (DefElem *) lfirst(option);
421 if (strcmp(defel->defname, "password") == 0 ||
422 strcmp(defel->defname, "encryptedPassword") == 0 ||
423 strcmp(defel->defname, "unencryptedPassword") == 0)
427 (errcode(ERRCODE_SYNTAX_ERROR),
428 errmsg("conflicting or redundant options")));
430 if (strcmp(defel->defname, "encryptedPassword") == 0)
431 encrypt_password = true;
432 else if (strcmp(defel->defname, "unencryptedPassword") == 0)
433 encrypt_password = false;
435 else if (strcmp(defel->defname, "superuser") == 0)
439 (errcode(ERRCODE_SYNTAX_ERROR),
440 errmsg("conflicting or redundant options")));
443 else if (strcmp(defel->defname, "inherit") == 0)
447 (errcode(ERRCODE_SYNTAX_ERROR),
448 errmsg("conflicting or redundant options")));
451 else if (strcmp(defel->defname, "createrole") == 0)
455 (errcode(ERRCODE_SYNTAX_ERROR),
456 errmsg("conflicting or redundant options")));
459 else if (strcmp(defel->defname, "createdb") == 0)
463 (errcode(ERRCODE_SYNTAX_ERROR),
464 errmsg("conflicting or redundant options")));
467 else if (strcmp(defel->defname, "canlogin") == 0)
471 (errcode(ERRCODE_SYNTAX_ERROR),
472 errmsg("conflicting or redundant options")));
475 else if (strcmp(defel->defname, "rolemembers") == 0 &&
480 (errcode(ERRCODE_SYNTAX_ERROR),
481 errmsg("conflicting or redundant options")));
482 drolemembers = defel;
484 else if (strcmp(defel->defname, "validUntil") == 0)
488 (errcode(ERRCODE_SYNTAX_ERROR),
489 errmsg("conflicting or redundant options")));
493 elog(ERROR, "option \"%s\" not recognized",
498 password = strVal(dpassword->arg);
500 issuper = intVal(dissuper->arg);
502 inherit = intVal(dinherit->arg);
504 createrole = intVal(dcreaterole->arg);
506 createdb = intVal(dcreatedb->arg);
508 canlogin = intVal(dcanlogin->arg);
510 rolemembers = (List *) drolemembers->arg;
512 validUntil = strVal(dvalidUntil->arg);
515 * Scan the pg_authid relation to be certain the user exists. Note we
516 * secure exclusive lock to protect our update of the flat auth file.
518 pg_authid_rel = heap_open(AuthIdRelationId, ExclusiveLock);
519 pg_authid_dsc = RelationGetDescr(pg_authid_rel);
521 tuple = SearchSysCache(AUTHNAME,
522 PointerGetDatum(stmt->role),
524 if (!HeapTupleIsValid(tuple))
526 (errcode(ERRCODE_UNDEFINED_OBJECT),
527 errmsg("role \"%s\" does not exist", stmt->role)));
529 roleid = HeapTupleGetOid(tuple);
532 * To mess with a superuser you gotta be superuser; else you need
533 * createrole, or just want to change your own password
535 if (((Form_pg_authid) GETSTRUCT(tuple))->rolsuper || issuper >= 0)
539 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
540 errmsg("must be superuser to alter superusers")));
542 else if (!have_createrole_privilege())
551 roleid == GetUserId()))
553 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
554 errmsg("permission denied")));
558 * Build an updated tuple, perusing the information just obtained
560 MemSet(new_record, 0, sizeof(new_record));
561 MemSet(new_record_nulls, ' ', sizeof(new_record_nulls));
562 MemSet(new_record_repl, ' ', sizeof(new_record_repl));
565 * issuper/createrole/catupdate/etc
567 * XXX It's rather unclear how to handle catupdate. It's probably best to
568 * keep it equal to the superuser status, otherwise you could end up
569 * with a situation where no existing superuser can alter the
570 * catalogs, including pg_authid!
574 new_record[Anum_pg_authid_rolsuper - 1] = BoolGetDatum(issuper > 0);
575 new_record_repl[Anum_pg_authid_rolsuper - 1] = 'r';
577 new_record[Anum_pg_authid_rolcatupdate - 1] = BoolGetDatum(issuper > 0);
578 new_record_repl[Anum_pg_authid_rolcatupdate - 1] = 'r';
583 new_record[Anum_pg_authid_rolinherit - 1] = BoolGetDatum(inherit > 0);
584 new_record_repl[Anum_pg_authid_rolinherit - 1] = 'r';
589 new_record[Anum_pg_authid_rolcreaterole - 1] = BoolGetDatum(createrole > 0);
590 new_record_repl[Anum_pg_authid_rolcreaterole - 1] = 'r';
595 new_record[Anum_pg_authid_rolcreatedb - 1] = BoolGetDatum(createdb > 0);
596 new_record_repl[Anum_pg_authid_rolcreatedb - 1] = 'r';
601 new_record[Anum_pg_authid_rolcanlogin - 1] = BoolGetDatum(canlogin > 0);
602 new_record_repl[Anum_pg_authid_rolcanlogin - 1] = 'r';
608 if (!encrypt_password || isMD5(password))
609 new_record[Anum_pg_authid_rolpassword - 1] =
610 DirectFunctionCall1(textin, CStringGetDatum(password));
613 if (!EncryptMD5(password, stmt->role, strlen(stmt->role),
615 elog(ERROR, "password encryption failed");
616 new_record[Anum_pg_authid_rolpassword - 1] =
617 DirectFunctionCall1(textin, CStringGetDatum(encrypted_password));
619 new_record_repl[Anum_pg_authid_rolpassword - 1] = 'r';
625 new_record[Anum_pg_authid_rolvaliduntil - 1] =
626 DirectFunctionCall3(timestamptz_in,
627 CStringGetDatum(validUntil),
628 ObjectIdGetDatum(InvalidOid),
630 new_record_repl[Anum_pg_authid_rolvaliduntil - 1] = 'r';
633 new_tuple = heap_modifytuple(tuple, pg_authid_dsc, new_record,
634 new_record_nulls, new_record_repl);
635 simple_heap_update(pg_authid_rel, &tuple->t_self, new_tuple);
638 CatalogUpdateIndexes(pg_authid_rel, new_tuple);
640 ReleaseSysCache(tuple);
641 heap_freetuple(new_tuple);
644 * Advance command counter so we can see new record; else tests
645 * in AddRoleMems may fail.
648 CommandCounterIncrement();
650 if (stmt->action == +1) /* add members to role */
651 AddRoleMems(stmt->role, roleid,
652 rolemembers, roleNamesToIds(rolemembers),
654 else if (stmt->action == -1) /* drop members from role */
655 DelRoleMems(stmt->role, roleid,
656 rolemembers, roleNamesToIds(rolemembers),
660 * Now we can clean up; but keep lock until commit (to avoid possible
661 * deadlock when commit code tries to acquire lock).
663 heap_close(pg_authid_rel, NoLock);
666 * Set flag to update flat auth file at commit.
668 auth_file_update_needed();
676 AlterRoleSet(AlterRoleSetStmt *stmt)
682 Datum repl_val[Natts_pg_authid];
683 char repl_null[Natts_pg_authid];
684 char repl_repl[Natts_pg_authid];
687 valuestr = flatten_set_variable_args(stmt->variable, stmt->value);
690 * RowExclusiveLock is sufficient, because we don't need to update the
693 rel = heap_open(AuthIdRelationId, RowExclusiveLock);
694 oldtuple = SearchSysCache(AUTHNAME,
695 PointerGetDatum(stmt->role),
697 if (!HeapTupleIsValid(oldtuple))
699 (errcode(ERRCODE_UNDEFINED_OBJECT),
700 errmsg("role \"%s\" does not exist", stmt->role)));
703 * To mess with a superuser you gotta be superuser; else you need
704 * createrole, or just want to change your own settings
706 if (((Form_pg_authid) GETSTRUCT(oldtuple))->rolsuper)
710 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
711 errmsg("must be superuser to alter superusers")));
715 if (!have_createrole_privilege() &&
716 HeapTupleGetOid(oldtuple) != GetUserId())
718 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
719 errmsg("permission denied")));
722 for (i = 0; i < Natts_pg_authid; i++)
725 repl_repl[Anum_pg_authid_rolconfig - 1] = 'r';
726 if (strcmp(stmt->variable, "all") == 0 && valuestr == NULL)
729 repl_null[Anum_pg_authid_rolconfig - 1] = 'n';
737 repl_null[Anum_pg_authid_rolconfig - 1] = ' ';
739 datum = SysCacheGetAttr(AUTHNAME, oldtuple,
740 Anum_pg_authid_rolconfig, &isnull);
742 array = isnull ? NULL : DatumGetArrayTypeP(datum);
745 array = GUCArrayAdd(array, stmt->variable, valuestr);
747 array = GUCArrayDelete(array, stmt->variable);
750 repl_val[Anum_pg_authid_rolconfig - 1] = PointerGetDatum(array);
752 repl_null[Anum_pg_authid_rolconfig - 1] = 'n';
755 newtuple = heap_modifytuple(oldtuple, RelationGetDescr(rel),
756 repl_val, repl_null, repl_repl);
758 simple_heap_update(rel, &oldtuple->t_self, newtuple);
759 CatalogUpdateIndexes(rel, newtuple);
761 ReleaseSysCache(oldtuple);
762 heap_close(rel, RowExclusiveLock);
770 DropRole(DropRoleStmt *stmt)
772 Relation pg_authid_rel, pg_auth_members_rel;
775 if (!have_createrole_privilege())
777 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
778 errmsg("permission denied to drop role")));
781 * Scan the pg_authid relation to find the Oid of the role(s) to be
782 * deleted. Note we secure exclusive lock on pg_authid, because we
783 * need to protect our update of the flat auth file. A regular
784 * writer's lock on pg_auth_members is sufficient though.
786 pg_authid_rel = heap_open(AuthIdRelationId, ExclusiveLock);
787 pg_auth_members_rel = heap_open(AuthMemRelationId, RowExclusiveLock);
789 foreach(item, stmt->roles)
791 const char *role = strVal(lfirst(item));
799 tuple = SearchSysCache(AUTHNAME,
800 PointerGetDatum(role),
802 if (!HeapTupleIsValid(tuple))
804 (errcode(ERRCODE_UNDEFINED_OBJECT),
805 errmsg("role \"%s\" does not exist", role)));
807 roleid = HeapTupleGetOid(tuple);
809 if (roleid == GetUserId())
811 (errcode(ERRCODE_OBJECT_IN_USE),
812 errmsg("current user cannot be dropped")));
813 if (roleid == GetOuterUserId())
815 (errcode(ERRCODE_OBJECT_IN_USE),
816 errmsg("current user cannot be dropped")));
817 if (roleid == GetSessionUserId())
819 (errcode(ERRCODE_OBJECT_IN_USE),
820 errmsg("session user cannot be dropped")));
823 * For safety's sake, we allow createrole holders to drop ordinary
824 * roles but not superuser roles. This is mainly to avoid the
825 * scenario where you accidentally drop the last superuser.
827 if (((Form_pg_authid) GETSTRUCT(tuple))->rolsuper &&
830 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
831 errmsg("must be superuser to drop superusers")));
834 * Lock the role, so nobody can add dependencies to her while we drop
835 * her. We keep the lock until the end of transaction.
837 LockSharedObject(AuthIdRelationId, roleid, 0, AccessExclusiveLock);
839 /* Check for pg_shdepend entries depending on this role */
840 if ((detail = checkSharedDependencies(AuthIdRelationId, roleid)) != NULL)
842 (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
843 errmsg("role \"%s\" cannot be dropped because some objects depend on it",
845 errdetail("%s", detail)));
848 * Remove the role from the pg_authid table
850 simple_heap_delete(pg_authid_rel, &tuple->t_self);
852 ReleaseSysCache(tuple);
855 * Remove role from the pg_auth_members table. We have to remove
856 * all tuples that show it as either a role or a member.
858 * XXX what about grantor entries? Maybe we should do one heap scan.
860 ScanKeyInit(&scankey,
861 Anum_pg_auth_members_roleid,
862 BTEqualStrategyNumber, F_OIDEQ,
863 ObjectIdGetDatum(roleid));
865 sscan = systable_beginscan(pg_auth_members_rel, AuthMemRoleMemIndexId,
866 true, SnapshotNow, 1, &scankey);
868 while (HeapTupleIsValid(tmp_tuple = systable_getnext(sscan)))
870 simple_heap_delete(pg_auth_members_rel, &tmp_tuple->t_self);
873 systable_endscan(sscan);
875 ScanKeyInit(&scankey,
876 Anum_pg_auth_members_member,
877 BTEqualStrategyNumber, F_OIDEQ,
878 ObjectIdGetDatum(roleid));
880 sscan = systable_beginscan(pg_auth_members_rel, AuthMemMemRoleIndexId,
881 true, SnapshotNow, 1, &scankey);
883 while (HeapTupleIsValid(tmp_tuple = systable_getnext(sscan)))
885 simple_heap_delete(pg_auth_members_rel, &tmp_tuple->t_self);
888 systable_endscan(sscan);
891 * Advance command counter so that later iterations of this loop
892 * will see the changes already made. This is essential if, for
893 * example, we are trying to drop both a role and one of its
894 * direct members --- we'll get an error if we try to delete the
895 * linking pg_auth_members tuple twice. (We do not need a CCI
896 * between the two delete loops above, because it's not allowed
897 * for a role to directly contain itself.)
899 CommandCounterIncrement();
903 * Now we can clean up; but keep locks until commit (to avoid possible
904 * deadlock when commit code tries to acquire lock).
906 heap_close(pg_auth_members_rel, NoLock);
907 heap_close(pg_authid_rel, NoLock);
910 * Set flag to update flat auth file at commit.
912 auth_file_update_needed();
919 RenameRole(const char *oldname, const char *newname)
927 Datum repl_val[Natts_pg_authid];
928 char repl_null[Natts_pg_authid];
929 char repl_repl[Natts_pg_authid];
933 /* ExclusiveLock because we need to update the flat auth file */
934 rel = heap_open(AuthIdRelationId, ExclusiveLock);
935 dsc = RelationGetDescr(rel);
937 oldtuple = SearchSysCache(AUTHNAME,
938 CStringGetDatum(oldname),
940 if (!HeapTupleIsValid(oldtuple))
942 (errcode(ERRCODE_UNDEFINED_OBJECT),
943 errmsg("role \"%s\" does not exist", oldname)));
946 * XXX Client applications probably store the session user somewhere,
947 * so renaming it could cause confusion. On the other hand, there may
948 * not be an actual problem besides a little confusion, so think about
949 * this and decide. Same for SET ROLE ... we don't restrict renaming
950 * the current effective userid, though.
953 roleid = HeapTupleGetOid(oldtuple);
955 if (roleid == GetSessionUserId())
957 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
958 errmsg("session user may not be renamed")));
959 if (roleid == GetOuterUserId())
961 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
962 errmsg("current user may not be renamed")));
964 /* make sure the new name doesn't exist */
965 if (SearchSysCacheExists(AUTHNAME,
966 CStringGetDatum(newname),
969 (errcode(ERRCODE_DUPLICATE_OBJECT),
970 errmsg("role \"%s\" already exists", newname)));
972 if (strcmp(newname, "public") == 0 ||
973 strcmp(newname, "none") == 0)
975 (errcode(ERRCODE_RESERVED_NAME),
976 errmsg("role name \"%s\" is reserved",
980 * createrole is enough privilege unless you want to mess with a superuser
982 if (((Form_pg_authid) GETSTRUCT(oldtuple))->rolsuper)
986 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
987 errmsg("must be superuser to rename superusers")));
991 if (!have_createrole_privilege())
993 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
994 errmsg("permission denied to rename role")));
997 /* OK, construct the modified tuple */
998 for (i = 0; i < Natts_pg_authid; i++)
1001 repl_repl[Anum_pg_authid_rolname - 1] = 'r';
1002 repl_val[Anum_pg_authid_rolname - 1] = DirectFunctionCall1(namein,
1003 CStringGetDatum(newname));
1004 repl_null[Anum_pg_authid_rolname - 1] = ' ';
1006 datum = heap_getattr(oldtuple, Anum_pg_authid_rolpassword, dsc, &isnull);
1008 if (!isnull && isMD5(DatumGetCString(DirectFunctionCall1(textout, datum))))
1010 /* MD5 uses the username as salt, so just clear it on a rename */
1011 repl_repl[Anum_pg_authid_rolpassword - 1] = 'r';
1012 repl_null[Anum_pg_authid_rolpassword - 1] = 'n';
1015 (errmsg("MD5 password cleared because of role rename")));
1018 newtuple = heap_modifytuple(oldtuple, dsc, repl_val, repl_null, repl_repl);
1019 simple_heap_update(rel, &oldtuple->t_self, newtuple);
1021 CatalogUpdateIndexes(rel, newtuple);
1023 ReleaseSysCache(oldtuple);
1024 heap_close(rel, NoLock);
1027 * Set flag to update flat auth file at commit.
1029 auth_file_update_needed();
1035 * Grant/Revoke roles to/from roles
1038 GrantRole(GrantRoleStmt *stmt)
1040 Relation pg_authid_rel;
1046 grantor = get_roleid_checked(stmt->grantor);
1048 grantor = GetUserId();
1050 grantee_ids = roleNamesToIds(stmt->grantee_roles);
1053 * Even though this operation doesn't change pg_authid, we must
1054 * secure exclusive lock on it to protect our update of the flat
1057 pg_authid_rel = heap_open(AuthIdRelationId, ExclusiveLock);
1060 * Step through all of the granted roles and add/remove
1061 * entries for the grantees, or, if admin_opt is set, then
1062 * just add/remove the admin option.
1064 * Note: Permissions checking is done by AddRoleMems/DelRoleMems
1066 foreach(item, stmt->granted_roles)
1068 char *rolename = strVal(lfirst(item));
1069 Oid roleid = get_roleid_checked(rolename);
1072 AddRoleMems(rolename, roleid,
1073 stmt->grantee_roles, grantee_ids,
1074 grantor, stmt->admin_opt);
1076 DelRoleMems(rolename, roleid,
1077 stmt->grantee_roles, grantee_ids,
1081 heap_close(pg_authid_rel, NoLock);
1084 * Set flag to update flat auth file at commit.
1086 auth_file_update_needed();
1092 * Given a list of role names (as String nodes), generate a list of role OIDs
1093 * in the same order.
1096 roleNamesToIds(List *memberNames)
1101 foreach(l, memberNames)
1103 char *rolename = strVal(lfirst(l));
1104 Oid roleid = get_roleid_checked(rolename);
1106 result = lappend_oid(result, roleid);
1112 * AddRoleMems -- Add given members to the specified role
1114 * rolename: name of role to add to (used only for error messages)
1115 * roleid: OID of role to add to
1116 * memberNames: list of names of roles to add (used only for error messages)
1117 * memberIds: OIDs of roles to add
1118 * grantorId: who is granting the membership
1119 * admin_opt: granting admin option?
1121 * Note: caller is responsible for holding ExclusiveLock on pg_authid,
1122 * and for calling auth_file_update_needed().
1125 AddRoleMems(const char *rolename, Oid roleid,
1126 List *memberNames, List *memberIds,
1127 Oid grantorId, bool admin_opt)
1129 Relation pg_authmem_rel;
1130 TupleDesc pg_authmem_dsc;
1134 Assert(list_length(memberNames) == list_length(memberIds));
1136 /* Skip permission check if nothing to do */
1141 * Check permissions: must have createrole or admin option on the
1142 * role to be changed. To mess with a superuser role, you gotta
1145 if (superuser_arg(roleid))
1149 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1150 errmsg("must be superuser to alter superusers")));
1154 if (!have_createrole_privilege() &&
1155 !is_admin_of_role(grantorId, roleid))
1157 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1158 errmsg("must have admin option on role \"%s\"",
1162 /* XXX not sure about this check */
1163 if (grantorId != GetUserId() && !superuser())
1165 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1166 errmsg("must be superuser to set grantor ID")));
1168 /* We need only regular writer's lock on pg_auth_members */
1169 pg_authmem_rel = heap_open(AuthMemRelationId, RowExclusiveLock);
1170 pg_authmem_dsc = RelationGetDescr(pg_authmem_rel);
1172 forboth(nameitem, memberNames, iditem, memberIds)
1174 const char *membername = strVal(lfirst(nameitem));
1175 Oid memberid = lfirst_oid(iditem);
1176 HeapTuple authmem_tuple;
1178 Datum new_record[Natts_pg_auth_members];
1179 char new_record_nulls[Natts_pg_auth_members];
1180 char new_record_repl[Natts_pg_auth_members];
1183 * Refuse creation of membership loops, including the trivial case
1184 * where a role is made a member of itself. We do this by checking
1185 * to see if the target role is already a member of the proposed
1188 if (is_member_of_role(roleid, memberid))
1190 (errcode(ERRCODE_INVALID_GRANT_OPERATION),
1191 (errmsg("role \"%s\" is a member of role \"%s\"",
1192 rolename, membername))));
1195 * Check if entry for this role/member already exists;
1196 * if so, give warning unless we are adding admin option.
1198 authmem_tuple = SearchSysCache(AUTHMEMROLEMEM,
1199 ObjectIdGetDatum(roleid),
1200 ObjectIdGetDatum(memberid),
1202 if (HeapTupleIsValid(authmem_tuple) &&
1204 ((Form_pg_auth_members) GETSTRUCT(authmem_tuple))->admin_option))
1207 (errmsg("role \"%s\" is already a member of role \"%s\"",
1208 membername, rolename)));
1209 ReleaseSysCache(authmem_tuple);
1213 /* Build a tuple to insert or update */
1214 MemSet(new_record, 0, sizeof(new_record));
1215 MemSet(new_record_nulls, ' ', sizeof(new_record_nulls));
1216 MemSet(new_record_repl, ' ', sizeof(new_record_repl));
1218 new_record[Anum_pg_auth_members_roleid - 1] = ObjectIdGetDatum(roleid);
1219 new_record[Anum_pg_auth_members_member - 1] = ObjectIdGetDatum(memberid);
1220 new_record[Anum_pg_auth_members_grantor - 1] = ObjectIdGetDatum(grantorId);
1221 new_record[Anum_pg_auth_members_admin_option - 1] = BoolGetDatum(admin_opt);
1223 if (HeapTupleIsValid(authmem_tuple))
1225 new_record_repl[Anum_pg_auth_members_grantor - 1] = 'r';
1226 new_record_repl[Anum_pg_auth_members_admin_option - 1] = 'r';
1227 tuple = heap_modifytuple(authmem_tuple, pg_authmem_dsc,
1229 new_record_nulls, new_record_repl);
1230 simple_heap_update(pg_authmem_rel, &tuple->t_self, tuple);
1231 CatalogUpdateIndexes(pg_authmem_rel, tuple);
1232 ReleaseSysCache(authmem_tuple);
1236 tuple = heap_formtuple(pg_authmem_dsc,
1237 new_record, new_record_nulls);
1238 simple_heap_insert(pg_authmem_rel, tuple);
1239 CatalogUpdateIndexes(pg_authmem_rel, tuple);
1242 /* CCI after each change, in case there are duplicates in list */
1243 CommandCounterIncrement();
1247 * Now we can clean up; but keep lock until commit (to avoid possible
1248 * deadlock when commit code tries to acquire lock).
1250 heap_close(pg_authmem_rel, NoLock);
1254 * DelRoleMems -- Remove given members from the specified role
1256 * rolename: name of role to del from (used only for error messages)
1257 * roleid: OID of role to del from
1258 * memberNames: list of names of roles to del (used only for error messages)
1259 * memberIds: OIDs of roles to del
1260 * admin_opt: remove admin option only?
1262 * Note: caller is responsible for holding ExclusiveLock on pg_authid,
1263 * and for calling auth_file_update_needed().
1266 DelRoleMems(const char *rolename, Oid roleid,
1267 List *memberNames, List *memberIds,
1270 Relation pg_authmem_rel;
1271 TupleDesc pg_authmem_dsc;
1275 Assert(list_length(memberNames) == list_length(memberIds));
1277 /* Skip permission check if nothing to do */
1282 * Check permissions: must have createrole or admin option on the
1283 * role to be changed. To mess with a superuser role, you gotta
1286 if (superuser_arg(roleid))
1290 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1291 errmsg("must be superuser to alter superusers")));
1295 if (!have_createrole_privilege() &&
1296 !is_admin_of_role(GetUserId(), roleid))
1298 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1299 errmsg("must have admin option on role \"%s\"",
1303 /* We need only regular writer's lock on pg_auth_members */
1304 pg_authmem_rel = heap_open(AuthMemRelationId, RowExclusiveLock);
1305 pg_authmem_dsc = RelationGetDescr(pg_authmem_rel);
1307 forboth(nameitem, memberNames, iditem, memberIds)
1309 const char *membername = strVal(lfirst(nameitem));
1310 Oid memberid = lfirst_oid(iditem);
1311 HeapTuple authmem_tuple;
1314 * Find entry for this role/member
1316 authmem_tuple = SearchSysCache(AUTHMEMROLEMEM,
1317 ObjectIdGetDatum(roleid),
1318 ObjectIdGetDatum(memberid),
1320 if (!HeapTupleIsValid(authmem_tuple))
1323 (errmsg("role \"%s\" is not a member of role \"%s\"",
1324 membername, rolename)));
1330 /* Remove the entry altogether */
1331 simple_heap_delete(pg_authmem_rel, &authmem_tuple->t_self);
1335 /* Just turn off the admin option */
1337 Datum new_record[Natts_pg_auth_members];
1338 char new_record_nulls[Natts_pg_auth_members];
1339 char new_record_repl[Natts_pg_auth_members];
1341 /* Build a tuple to update with */
1342 MemSet(new_record, 0, sizeof(new_record));
1343 MemSet(new_record_nulls, ' ', sizeof(new_record_nulls));
1344 MemSet(new_record_repl, ' ', sizeof(new_record_repl));
1346 new_record[Anum_pg_auth_members_admin_option - 1] = BoolGetDatum(false);
1347 new_record_repl[Anum_pg_auth_members_admin_option - 1] = 'r';
1349 tuple = heap_modifytuple(authmem_tuple, pg_authmem_dsc,
1351 new_record_nulls, new_record_repl);
1352 simple_heap_update(pg_authmem_rel, &tuple->t_self, tuple);
1353 CatalogUpdateIndexes(pg_authmem_rel, tuple);
1356 ReleaseSysCache(authmem_tuple);
1358 /* CCI after each change, in case there are duplicates in list */
1359 CommandCounterIncrement();
1363 * Now we can clean up; but keep lock until commit (to avoid possible
1364 * deadlock when commit code tries to acquire lock).
1366 heap_close(pg_authmem_rel, NoLock);