Header And Logo

PostgreSQL
| The world's most advanced open source database.

user.c

Go to the documentation of this file.
00001 /*-------------------------------------------------------------------------
00002  *
00003  * user.c
00004  *    Commands for manipulating roles (formerly called users).
00005  *
00006  * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
00007  * Portions Copyright (c) 1994, Regents of the University of California
00008  *
00009  * src/backend/commands/user.c
00010  *
00011  *-------------------------------------------------------------------------
00012  */
00013 #include "postgres.h"
00014 
00015 #include "access/genam.h"
00016 #include "access/heapam.h"
00017 #include "access/htup_details.h"
00018 #include "access/xact.h"
00019 #include "catalog/dependency.h"
00020 #include "catalog/indexing.h"
00021 #include "catalog/objectaccess.h"
00022 #include "catalog/pg_auth_members.h"
00023 #include "catalog/pg_authid.h"
00024 #include "catalog/pg_database.h"
00025 #include "catalog/pg_db_role_setting.h"
00026 #include "commands/comment.h"
00027 #include "commands/dbcommands.h"
00028 #include "commands/seclabel.h"
00029 #include "commands/user.h"
00030 #include "libpq/md5.h"
00031 #include "miscadmin.h"
00032 #include "storage/lmgr.h"
00033 #include "utils/acl.h"
00034 #include "utils/builtins.h"
00035 #include "utils/fmgroids.h"
00036 #include "utils/syscache.h"
00037 #include "utils/timestamp.h"
00038 #include "utils/tqual.h"
00039 
00040 /* Potentially set by contrib/pg_upgrade_support functions */
00041 Oid         binary_upgrade_next_pg_authid_oid = InvalidOid;
00042 
00043 
00044 /* GUC parameter */
00045 extern bool Password_encryption;
00046 
00047 /* Hook to check passwords in CreateRole() and AlterRole() */
00048 check_password_hook_type check_password_hook = NULL;
00049 
00050 static List *roleNamesToIds(List *memberNames);
00051 static void AddRoleMems(const char *rolename, Oid roleid,
00052             List *memberNames, List *memberIds,
00053             Oid grantorId, bool admin_opt);
00054 static void DelRoleMems(const char *rolename, Oid roleid,
00055             List *memberNames, List *memberIds,
00056             bool admin_opt);
00057 
00058 
00059 /* Check if current user has createrole privileges */
00060 static bool
00061 have_createrole_privilege(void)
00062 {
00063     return has_createrole_privilege(GetUserId());
00064 }
00065 
00066 
00067 /*
00068  * CREATE ROLE
00069  */
00070 Oid
00071 CreateRole(CreateRoleStmt *stmt)
00072 {
00073     Relation    pg_authid_rel;
00074     TupleDesc   pg_authid_dsc;
00075     HeapTuple   tuple;
00076     Datum       new_record[Natts_pg_authid];
00077     bool        new_record_nulls[Natts_pg_authid];
00078     Oid         roleid;
00079     ListCell   *item;
00080     ListCell   *option;
00081     char       *password = NULL;    /* user password */
00082     bool        encrypt_password = Password_encryption; /* encrypt password? */
00083     char        encrypted_password[MD5_PASSWD_LEN + 1];
00084     bool        issuper = false;    /* Make the user a superuser? */
00085     bool        inherit = true; /* Auto inherit privileges? */
00086     bool        createrole = false;     /* Can this user create roles? */
00087     bool        createdb = false;       /* Can the user create databases? */
00088     bool        canlogin = false;       /* Can this user login? */
00089     bool        isreplication = false;  /* Is this a replication role? */
00090     int         connlimit = -1; /* maximum connections allowed */
00091     List       *addroleto = NIL;    /* roles to make this a member of */
00092     List       *rolemembers = NIL;      /* roles to be members of this role */
00093     List       *adminmembers = NIL;     /* roles to be admins of this role */
00094     char       *validUntil = NULL;      /* time the login is valid until */
00095     Datum       validUntil_datum;       /* same, as timestamptz Datum */
00096     bool        validUntil_null;
00097     DefElem    *dpassword = NULL;
00098     DefElem    *dissuper = NULL;
00099     DefElem    *dinherit = NULL;
00100     DefElem    *dcreaterole = NULL;
00101     DefElem    *dcreatedb = NULL;
00102     DefElem    *dcanlogin = NULL;
00103     DefElem    *disreplication = NULL;
00104     DefElem    *dconnlimit = NULL;
00105     DefElem    *daddroleto = NULL;
00106     DefElem    *drolemembers = NULL;
00107     DefElem    *dadminmembers = NULL;
00108     DefElem    *dvalidUntil = NULL;
00109 
00110     /* The defaults can vary depending on the original statement type */
00111     switch (stmt->stmt_type)
00112     {
00113         case ROLESTMT_ROLE:
00114             break;
00115         case ROLESTMT_USER:
00116             canlogin = true;
00117             /* may eventually want inherit to default to false here */
00118             break;
00119         case ROLESTMT_GROUP:
00120             break;
00121     }
00122 
00123     /* Extract options from the statement node tree */
00124     foreach(option, stmt->options)
00125     {
00126         DefElem    *defel = (DefElem *) lfirst(option);
00127 
00128         if (strcmp(defel->defname, "password") == 0 ||
00129             strcmp(defel->defname, "encryptedPassword") == 0 ||
00130             strcmp(defel->defname, "unencryptedPassword") == 0)
00131         {
00132             if (dpassword)
00133                 ereport(ERROR,
00134                         (errcode(ERRCODE_SYNTAX_ERROR),
00135                          errmsg("conflicting or redundant options")));
00136             dpassword = defel;
00137             if (strcmp(defel->defname, "encryptedPassword") == 0)
00138                 encrypt_password = true;
00139             else if (strcmp(defel->defname, "unencryptedPassword") == 0)
00140                 encrypt_password = false;
00141         }
00142         else if (strcmp(defel->defname, "sysid") == 0)
00143         {
00144             ereport(NOTICE,
00145                     (errmsg("SYSID can no longer be specified")));
00146         }
00147         else if (strcmp(defel->defname, "superuser") == 0)
00148         {
00149             if (dissuper)
00150                 ereport(ERROR,
00151                         (errcode(ERRCODE_SYNTAX_ERROR),
00152                          errmsg("conflicting or redundant options")));
00153             dissuper = defel;
00154         }
00155         else if (strcmp(defel->defname, "inherit") == 0)
00156         {
00157             if (dinherit)
00158                 ereport(ERROR,
00159                         (errcode(ERRCODE_SYNTAX_ERROR),
00160                          errmsg("conflicting or redundant options")));
00161             dinherit = defel;
00162         }
00163         else if (strcmp(defel->defname, "createrole") == 0)
00164         {
00165             if (dcreaterole)
00166                 ereport(ERROR,
00167                         (errcode(ERRCODE_SYNTAX_ERROR),
00168                          errmsg("conflicting or redundant options")));
00169             dcreaterole = defel;
00170         }
00171         else if (strcmp(defel->defname, "createdb") == 0)
00172         {
00173             if (dcreatedb)
00174                 ereport(ERROR,
00175                         (errcode(ERRCODE_SYNTAX_ERROR),
00176                          errmsg("conflicting or redundant options")));
00177             dcreatedb = defel;
00178         }
00179         else if (strcmp(defel->defname, "canlogin") == 0)
00180         {
00181             if (dcanlogin)
00182                 ereport(ERROR,
00183                         (errcode(ERRCODE_SYNTAX_ERROR),
00184                          errmsg("conflicting or redundant options")));
00185             dcanlogin = defel;
00186         }
00187         else if (strcmp(defel->defname, "isreplication") == 0)
00188         {
00189             if (disreplication)
00190                 ereport(ERROR,
00191                         (errcode(ERRCODE_SYNTAX_ERROR),
00192                          errmsg("conflicting or redundant options")));
00193             disreplication = defel;
00194         }
00195         else if (strcmp(defel->defname, "connectionlimit") == 0)
00196         {
00197             if (dconnlimit)
00198                 ereport(ERROR,
00199                         (errcode(ERRCODE_SYNTAX_ERROR),
00200                          errmsg("conflicting or redundant options")));
00201             dconnlimit = defel;
00202         }
00203         else if (strcmp(defel->defname, "addroleto") == 0)
00204         {
00205             if (daddroleto)
00206                 ereport(ERROR,
00207                         (errcode(ERRCODE_SYNTAX_ERROR),
00208                          errmsg("conflicting or redundant options")));
00209             daddroleto = defel;
00210         }
00211         else if (strcmp(defel->defname, "rolemembers") == 0)
00212         {
00213             if (drolemembers)
00214                 ereport(ERROR,
00215                         (errcode(ERRCODE_SYNTAX_ERROR),
00216                          errmsg("conflicting or redundant options")));
00217             drolemembers = defel;
00218         }
00219         else if (strcmp(defel->defname, "adminmembers") == 0)
00220         {
00221             if (dadminmembers)
00222                 ereport(ERROR,
00223                         (errcode(ERRCODE_SYNTAX_ERROR),
00224                          errmsg("conflicting or redundant options")));
00225             dadminmembers = defel;
00226         }
00227         else if (strcmp(defel->defname, "validUntil") == 0)
00228         {
00229             if (dvalidUntil)
00230                 ereport(ERROR,
00231                         (errcode(ERRCODE_SYNTAX_ERROR),
00232                          errmsg("conflicting or redundant options")));
00233             dvalidUntil = defel;
00234         }
00235         else
00236             elog(ERROR, "option \"%s\" not recognized",
00237                  defel->defname);
00238     }
00239 
00240     if (dpassword && dpassword->arg)
00241         password = strVal(dpassword->arg);
00242     if (dissuper)
00243         issuper = intVal(dissuper->arg) != 0;
00244     if (dinherit)
00245         inherit = intVal(dinherit->arg) != 0;
00246     if (dcreaterole)
00247         createrole = intVal(dcreaterole->arg) != 0;
00248     if (dcreatedb)
00249         createdb = intVal(dcreatedb->arg) != 0;
00250     if (dcanlogin)
00251         canlogin = intVal(dcanlogin->arg) != 0;
00252     if (disreplication)
00253         isreplication = intVal(disreplication->arg) != 0;
00254     if (dconnlimit)
00255     {
00256         connlimit = intVal(dconnlimit->arg);
00257         if (connlimit < -1)
00258             ereport(ERROR,
00259                     (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
00260                      errmsg("invalid connection limit: %d", connlimit)));
00261     }
00262     if (daddroleto)
00263         addroleto = (List *) daddroleto->arg;
00264     if (drolemembers)
00265         rolemembers = (List *) drolemembers->arg;
00266     if (dadminmembers)
00267         adminmembers = (List *) dadminmembers->arg;
00268     if (dvalidUntil)
00269         validUntil = strVal(dvalidUntil->arg);
00270 
00271     /* Check some permissions first */
00272     if (issuper)
00273     {
00274         if (!superuser())
00275             ereport(ERROR,
00276                     (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
00277                      errmsg("must be superuser to create superusers")));
00278     }
00279     else if (isreplication)
00280     {
00281         if (!superuser())
00282             ereport(ERROR,
00283                     (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
00284                    errmsg("must be superuser to create replication users")));
00285     }
00286     else
00287     {
00288         if (!have_createrole_privilege())
00289             ereport(ERROR,
00290                     (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
00291                      errmsg("permission denied to create role")));
00292     }
00293 
00294     if (strcmp(stmt->role, "public") == 0 ||
00295         strcmp(stmt->role, "none") == 0)
00296         ereport(ERROR,
00297                 (errcode(ERRCODE_RESERVED_NAME),
00298                  errmsg("role name \"%s\" is reserved",
00299                         stmt->role)));
00300 
00301     /*
00302      * Check the pg_authid relation to be certain the role doesn't already
00303      * exist.
00304      */
00305     pg_authid_rel = heap_open(AuthIdRelationId, RowExclusiveLock);
00306     pg_authid_dsc = RelationGetDescr(pg_authid_rel);
00307 
00308     if (OidIsValid(get_role_oid(stmt->role, true)))
00309         ereport(ERROR,
00310                 (errcode(ERRCODE_DUPLICATE_OBJECT),
00311                  errmsg("role \"%s\" already exists",
00312                         stmt->role)));
00313 
00314     /* Convert validuntil to internal form */
00315     if (validUntil)
00316     {
00317         validUntil_datum = DirectFunctionCall3(timestamptz_in,
00318                                                CStringGetDatum(validUntil),
00319                                                ObjectIdGetDatum(InvalidOid),
00320                                                Int32GetDatum(-1));
00321         validUntil_null = false;
00322     }
00323     else
00324     {
00325         validUntil_datum = (Datum) 0;
00326         validUntil_null = true;
00327     }
00328 
00329     /*
00330      * Call the password checking hook if there is one defined
00331      */
00332     if (check_password_hook && password)
00333         (*check_password_hook) (stmt->role,
00334                                 password,
00335                isMD5(password) ? PASSWORD_TYPE_MD5 : PASSWORD_TYPE_PLAINTEXT,
00336                                 validUntil_datum,
00337                                 validUntil_null);
00338 
00339     /*
00340      * Build a tuple to insert
00341      */
00342     MemSet(new_record, 0, sizeof(new_record));
00343     MemSet(new_record_nulls, false, sizeof(new_record_nulls));
00344 
00345     new_record[Anum_pg_authid_rolname - 1] =
00346         DirectFunctionCall1(namein, CStringGetDatum(stmt->role));
00347 
00348     new_record[Anum_pg_authid_rolsuper - 1] = BoolGetDatum(issuper);
00349     new_record[Anum_pg_authid_rolinherit - 1] = BoolGetDatum(inherit);
00350     new_record[Anum_pg_authid_rolcreaterole - 1] = BoolGetDatum(createrole);
00351     new_record[Anum_pg_authid_rolcreatedb - 1] = BoolGetDatum(createdb);
00352     /* superuser gets catupdate right by default */
00353     new_record[Anum_pg_authid_rolcatupdate - 1] = BoolGetDatum(issuper);
00354     new_record[Anum_pg_authid_rolcanlogin - 1] = BoolGetDatum(canlogin);
00355     new_record[Anum_pg_authid_rolreplication - 1] = BoolGetDatum(isreplication);
00356     new_record[Anum_pg_authid_rolconnlimit - 1] = Int32GetDatum(connlimit);
00357 
00358     if (password)
00359     {
00360         if (!encrypt_password || isMD5(password))
00361             new_record[Anum_pg_authid_rolpassword - 1] =
00362                 CStringGetTextDatum(password);
00363         else
00364         {
00365             if (!pg_md5_encrypt(password, stmt->role, strlen(stmt->role),
00366                                 encrypted_password))
00367                 elog(ERROR, "password encryption failed");
00368             new_record[Anum_pg_authid_rolpassword - 1] =
00369                 CStringGetTextDatum(encrypted_password);
00370         }
00371     }
00372     else
00373         new_record_nulls[Anum_pg_authid_rolpassword - 1] = true;
00374 
00375     new_record[Anum_pg_authid_rolvaliduntil - 1] = validUntil_datum;
00376     new_record_nulls[Anum_pg_authid_rolvaliduntil - 1] = validUntil_null;
00377 
00378     tuple = heap_form_tuple(pg_authid_dsc, new_record, new_record_nulls);
00379 
00380     /*
00381      * pg_largeobject_metadata contains pg_authid.oid's, so we use the
00382      * binary-upgrade override, if specified.
00383      */
00384     if (IsBinaryUpgrade && OidIsValid(binary_upgrade_next_pg_authid_oid))
00385     {
00386         HeapTupleSetOid(tuple, binary_upgrade_next_pg_authid_oid);
00387         binary_upgrade_next_pg_authid_oid = InvalidOid;
00388     }
00389 
00390     /*
00391      * Insert new record in the pg_authid table
00392      */
00393     roleid = simple_heap_insert(pg_authid_rel, tuple);
00394     CatalogUpdateIndexes(pg_authid_rel, tuple);
00395 
00396     /*
00397      * Advance command counter so we can see new record; else tests in
00398      * AddRoleMems may fail.
00399      */
00400     if (addroleto || adminmembers || rolemembers)
00401         CommandCounterIncrement();
00402 
00403     /*
00404      * Add the new role to the specified existing roles.
00405      */
00406     foreach(item, addroleto)
00407     {
00408         char       *oldrolename = strVal(lfirst(item));
00409         Oid         oldroleid = get_role_oid(oldrolename, false);
00410 
00411         AddRoleMems(oldrolename, oldroleid,
00412                     list_make1(makeString(stmt->role)),
00413                     list_make1_oid(roleid),
00414                     GetUserId(), false);
00415     }
00416 
00417     /*
00418      * Add the specified members to this new role. adminmembers get the admin
00419      * option, rolemembers don't.
00420      */
00421     AddRoleMems(stmt->role, roleid,
00422                 adminmembers, roleNamesToIds(adminmembers),
00423                 GetUserId(), true);
00424     AddRoleMems(stmt->role, roleid,
00425                 rolemembers, roleNamesToIds(rolemembers),
00426                 GetUserId(), false);
00427 
00428     /* Post creation hook for new role */
00429     InvokeObjectPostCreateHook(AuthIdRelationId, roleid, 0);
00430 
00431     /*
00432      * Close pg_authid, but keep lock till commit.
00433      */
00434     heap_close(pg_authid_rel, NoLock);
00435 
00436     return roleid;
00437 }
00438 
00439 
00440 /*
00441  * ALTER ROLE
00442  *
00443  * Note: the rolemembers option accepted here is intended to support the
00444  * backwards-compatible ALTER GROUP syntax.  Although it will work to say
00445  * "ALTER ROLE role ROLE rolenames", we don't document it.
00446  */
00447 Oid
00448 AlterRole(AlterRoleStmt *stmt)
00449 {
00450     Datum       new_record[Natts_pg_authid];
00451     bool        new_record_nulls[Natts_pg_authid];
00452     bool        new_record_repl[Natts_pg_authid];
00453     Relation    pg_authid_rel;
00454     TupleDesc   pg_authid_dsc;
00455     HeapTuple   tuple,
00456                 new_tuple;
00457     ListCell   *option;
00458     char       *password = NULL;    /* user password */
00459     bool        encrypt_password = Password_encryption; /* encrypt password? */
00460     char        encrypted_password[MD5_PASSWD_LEN + 1];
00461     int         issuper = -1;   /* Make the user a superuser? */
00462     int         inherit = -1;   /* Auto inherit privileges? */
00463     int         createrole = -1;    /* Can this user create roles? */
00464     int         createdb = -1;  /* Can the user create databases? */
00465     int         canlogin = -1;  /* Can this user login? */
00466     int         isreplication = -1;     /* Is this a replication role? */
00467     int         connlimit = -1; /* maximum connections allowed */
00468     List       *rolemembers = NIL;      /* roles to be added/removed */
00469     char       *validUntil = NULL;      /* time the login is valid until */
00470     Datum       validUntil_datum;       /* same, as timestamptz Datum */
00471     bool        validUntil_null;
00472     DefElem    *dpassword = NULL;
00473     DefElem    *dissuper = NULL;
00474     DefElem    *dinherit = NULL;
00475     DefElem    *dcreaterole = NULL;
00476     DefElem    *dcreatedb = NULL;
00477     DefElem    *dcanlogin = NULL;
00478     DefElem    *disreplication = NULL;
00479     DefElem    *dconnlimit = NULL;
00480     DefElem    *drolemembers = NULL;
00481     DefElem    *dvalidUntil = NULL;
00482     Oid         roleid;
00483 
00484     /* Extract options from the statement node tree */
00485     foreach(option, stmt->options)
00486     {
00487         DefElem    *defel = (DefElem *) lfirst(option);
00488 
00489         if (strcmp(defel->defname, "password") == 0 ||
00490             strcmp(defel->defname, "encryptedPassword") == 0 ||
00491             strcmp(defel->defname, "unencryptedPassword") == 0)
00492         {
00493             if (dpassword)
00494                 ereport(ERROR,
00495                         (errcode(ERRCODE_SYNTAX_ERROR),
00496                          errmsg("conflicting or redundant options")));
00497             dpassword = defel;
00498             if (strcmp(defel->defname, "encryptedPassword") == 0)
00499                 encrypt_password = true;
00500             else if (strcmp(defel->defname, "unencryptedPassword") == 0)
00501                 encrypt_password = false;
00502         }
00503         else if (strcmp(defel->defname, "superuser") == 0)
00504         {
00505             if (dissuper)
00506                 ereport(ERROR,
00507                         (errcode(ERRCODE_SYNTAX_ERROR),
00508                          errmsg("conflicting or redundant options")));
00509             dissuper = defel;
00510         }
00511         else if (strcmp(defel->defname, "inherit") == 0)
00512         {
00513             if (dinherit)
00514                 ereport(ERROR,
00515                         (errcode(ERRCODE_SYNTAX_ERROR),
00516                          errmsg("conflicting or redundant options")));
00517             dinherit = defel;
00518         }
00519         else if (strcmp(defel->defname, "createrole") == 0)
00520         {
00521             if (dcreaterole)
00522                 ereport(ERROR,
00523                         (errcode(ERRCODE_SYNTAX_ERROR),
00524                          errmsg("conflicting or redundant options")));
00525             dcreaterole = defel;
00526         }
00527         else if (strcmp(defel->defname, "createdb") == 0)
00528         {
00529             if (dcreatedb)
00530                 ereport(ERROR,
00531                         (errcode(ERRCODE_SYNTAX_ERROR),
00532                          errmsg("conflicting or redundant options")));
00533             dcreatedb = defel;
00534         }
00535         else if (strcmp(defel->defname, "canlogin") == 0)
00536         {
00537             if (dcanlogin)
00538                 ereport(ERROR,
00539                         (errcode(ERRCODE_SYNTAX_ERROR),
00540                          errmsg("conflicting or redundant options")));
00541             dcanlogin = defel;
00542         }
00543         else if (strcmp(defel->defname, "isreplication") == 0)
00544         {
00545             if (disreplication)
00546                 ereport(ERROR,
00547                         (errcode(ERRCODE_SYNTAX_ERROR),
00548                          errmsg("conflicting or redundant options")));
00549             disreplication = defel;
00550         }
00551         else if (strcmp(defel->defname, "connectionlimit") == 0)
00552         {
00553             if (dconnlimit)
00554                 ereport(ERROR,
00555                         (errcode(ERRCODE_SYNTAX_ERROR),
00556                          errmsg("conflicting or redundant options")));
00557             dconnlimit = defel;
00558         }
00559         else if (strcmp(defel->defname, "rolemembers") == 0 &&
00560                  stmt->action != 0)
00561         {
00562             if (drolemembers)
00563                 ereport(ERROR,
00564                         (errcode(ERRCODE_SYNTAX_ERROR),
00565                          errmsg("conflicting or redundant options")));
00566             drolemembers = defel;
00567         }
00568         else if (strcmp(defel->defname, "validUntil") == 0)
00569         {
00570             if (dvalidUntil)
00571                 ereport(ERROR,
00572                         (errcode(ERRCODE_SYNTAX_ERROR),
00573                          errmsg("conflicting or redundant options")));
00574             dvalidUntil = defel;
00575         }
00576         else
00577             elog(ERROR, "option \"%s\" not recognized",
00578                  defel->defname);
00579     }
00580 
00581     if (dpassword && dpassword->arg)
00582         password = strVal(dpassword->arg);
00583     if (dissuper)
00584         issuper = intVal(dissuper->arg);
00585     if (dinherit)
00586         inherit = intVal(dinherit->arg);
00587     if (dcreaterole)
00588         createrole = intVal(dcreaterole->arg);
00589     if (dcreatedb)
00590         createdb = intVal(dcreatedb->arg);
00591     if (dcanlogin)
00592         canlogin = intVal(dcanlogin->arg);
00593     if (disreplication)
00594         isreplication = intVal(disreplication->arg);
00595     if (dconnlimit)
00596     {
00597         connlimit = intVal(dconnlimit->arg);
00598         if (connlimit < -1)
00599             ereport(ERROR,
00600                     (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
00601                      errmsg("invalid connection limit: %d", connlimit)));
00602     }
00603     if (drolemembers)
00604         rolemembers = (List *) drolemembers->arg;
00605     if (dvalidUntil)
00606         validUntil = strVal(dvalidUntil->arg);
00607 
00608     /*
00609      * Scan the pg_authid relation to be certain the user exists.
00610      */
00611     pg_authid_rel = heap_open(AuthIdRelationId, RowExclusiveLock);
00612     pg_authid_dsc = RelationGetDescr(pg_authid_rel);
00613 
00614     tuple = SearchSysCache1(AUTHNAME, PointerGetDatum(stmt->role));
00615     if (!HeapTupleIsValid(tuple))
00616         ereport(ERROR,
00617                 (errcode(ERRCODE_UNDEFINED_OBJECT),
00618                  errmsg("role \"%s\" does not exist", stmt->role)));
00619 
00620     roleid = HeapTupleGetOid(tuple);
00621 
00622     /*
00623      * To mess with a superuser you gotta be superuser; else you need
00624      * createrole, or just want to change your own password
00625      */
00626     if (((Form_pg_authid) GETSTRUCT(tuple))->rolsuper || issuper >= 0)
00627     {
00628         if (!superuser())
00629             ereport(ERROR,
00630                     (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
00631                      errmsg("must be superuser to alter superusers")));
00632     }
00633     else if (((Form_pg_authid) GETSTRUCT(tuple))->rolreplication || isreplication >= 0)
00634     {
00635         if (!superuser())
00636             ereport(ERROR,
00637                     (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
00638                      errmsg("must be superuser to alter replication users")));
00639     }
00640     else if (!have_createrole_privilege())
00641     {
00642         if (!(inherit < 0 &&
00643               createrole < 0 &&
00644               createdb < 0 &&
00645               canlogin < 0 &&
00646               isreplication < 0 &&
00647               !dconnlimit &&
00648               !rolemembers &&
00649               !validUntil &&
00650               dpassword &&
00651               roleid == GetUserId()))
00652             ereport(ERROR,
00653                     (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
00654                      errmsg("permission denied")));
00655     }
00656 
00657     /* Convert validuntil to internal form */
00658     if (validUntil)
00659     {
00660         validUntil_datum = DirectFunctionCall3(timestamptz_in,
00661                                                CStringGetDatum(validUntil),
00662                                                ObjectIdGetDatum(InvalidOid),
00663                                                Int32GetDatum(-1));
00664         validUntil_null = false;
00665     }
00666     else
00667     {
00668         /* fetch existing setting in case hook needs it */
00669         validUntil_datum = SysCacheGetAttr(AUTHNAME, tuple,
00670                                            Anum_pg_authid_rolvaliduntil,
00671                                            &validUntil_null);
00672     }
00673 
00674     /*
00675      * Call the password checking hook if there is one defined
00676      */
00677     if (check_password_hook && password)
00678         (*check_password_hook) (stmt->role,
00679                                 password,
00680                isMD5(password) ? PASSWORD_TYPE_MD5 : PASSWORD_TYPE_PLAINTEXT,
00681                                 validUntil_datum,
00682                                 validUntil_null);
00683 
00684     /*
00685      * Build an updated tuple, perusing the information just obtained
00686      */
00687     MemSet(new_record, 0, sizeof(new_record));
00688     MemSet(new_record_nulls, false, sizeof(new_record_nulls));
00689     MemSet(new_record_repl, false, sizeof(new_record_repl));
00690 
00691     /*
00692      * issuper/createrole/catupdate/etc
00693      *
00694      * XXX It's rather unclear how to handle catupdate.  It's probably best to
00695      * keep it equal to the superuser status, otherwise you could end up with
00696      * a situation where no existing superuser can alter the catalogs,
00697      * including pg_authid!
00698      */
00699     if (issuper >= 0)
00700     {
00701         new_record[Anum_pg_authid_rolsuper - 1] = BoolGetDatum(issuper > 0);
00702         new_record_repl[Anum_pg_authid_rolsuper - 1] = true;
00703 
00704         new_record[Anum_pg_authid_rolcatupdate - 1] = BoolGetDatum(issuper > 0);
00705         new_record_repl[Anum_pg_authid_rolcatupdate - 1] = true;
00706     }
00707 
00708     if (inherit >= 0)
00709     {
00710         new_record[Anum_pg_authid_rolinherit - 1] = BoolGetDatum(inherit > 0);
00711         new_record_repl[Anum_pg_authid_rolinherit - 1] = true;
00712     }
00713 
00714     if (createrole >= 0)
00715     {
00716         new_record[Anum_pg_authid_rolcreaterole - 1] = BoolGetDatum(createrole > 0);
00717         new_record_repl[Anum_pg_authid_rolcreaterole - 1] = true;
00718     }
00719 
00720     if (createdb >= 0)
00721     {
00722         new_record[Anum_pg_authid_rolcreatedb - 1] = BoolGetDatum(createdb > 0);
00723         new_record_repl[Anum_pg_authid_rolcreatedb - 1] = true;
00724     }
00725 
00726     if (canlogin >= 0)
00727     {
00728         new_record[Anum_pg_authid_rolcanlogin - 1] = BoolGetDatum(canlogin > 0);
00729         new_record_repl[Anum_pg_authid_rolcanlogin - 1] = true;
00730     }
00731 
00732     if (isreplication >= 0)
00733     {
00734         new_record[Anum_pg_authid_rolreplication - 1] = BoolGetDatum(isreplication > 0);
00735         new_record_repl[Anum_pg_authid_rolreplication - 1] = true;
00736     }
00737 
00738     if (dconnlimit)
00739     {
00740         new_record[Anum_pg_authid_rolconnlimit - 1] = Int32GetDatum(connlimit);
00741         new_record_repl[Anum_pg_authid_rolconnlimit - 1] = true;
00742     }
00743 
00744     /* password */
00745     if (password)
00746     {
00747         if (!encrypt_password || isMD5(password))
00748             new_record[Anum_pg_authid_rolpassword - 1] =
00749                 CStringGetTextDatum(password);
00750         else
00751         {
00752             if (!pg_md5_encrypt(password, stmt->role, strlen(stmt->role),
00753                                 encrypted_password))
00754                 elog(ERROR, "password encryption failed");
00755             new_record[Anum_pg_authid_rolpassword - 1] =
00756                 CStringGetTextDatum(encrypted_password);
00757         }
00758         new_record_repl[Anum_pg_authid_rolpassword - 1] = true;
00759     }
00760 
00761     /* unset password */
00762     if (dpassword && dpassword->arg == NULL)
00763     {
00764         new_record_repl[Anum_pg_authid_rolpassword - 1] = true;
00765         new_record_nulls[Anum_pg_authid_rolpassword - 1] = true;
00766     }
00767 
00768     /* valid until */
00769     new_record[Anum_pg_authid_rolvaliduntil - 1] = validUntil_datum;
00770     new_record_nulls[Anum_pg_authid_rolvaliduntil - 1] = validUntil_null;
00771     new_record_repl[Anum_pg_authid_rolvaliduntil - 1] = true;
00772 
00773     new_tuple = heap_modify_tuple(tuple, pg_authid_dsc, new_record,
00774                                   new_record_nulls, new_record_repl);
00775     simple_heap_update(pg_authid_rel, &tuple->t_self, new_tuple);
00776 
00777     /* Update indexes */
00778     CatalogUpdateIndexes(pg_authid_rel, new_tuple);
00779 
00780     InvokeObjectPostAlterHook(AuthIdRelationId, roleid, 0);
00781 
00782     ReleaseSysCache(tuple);
00783     heap_freetuple(new_tuple);
00784 
00785     /*
00786      * Advance command counter so we can see new record; else tests in
00787      * AddRoleMems may fail.
00788      */
00789     if (rolemembers)
00790         CommandCounterIncrement();
00791 
00792     if (stmt->action == +1)     /* add members to role */
00793         AddRoleMems(stmt->role, roleid,
00794                     rolemembers, roleNamesToIds(rolemembers),
00795                     GetUserId(), false);
00796     else if (stmt->action == -1)    /* drop members from role */
00797         DelRoleMems(stmt->role, roleid,
00798                     rolemembers, roleNamesToIds(rolemembers),
00799                     false);
00800 
00801     /*
00802      * Close pg_authid, but keep lock till commit.
00803      */
00804     heap_close(pg_authid_rel, NoLock);
00805 
00806     return roleid;
00807 }
00808 
00809 
00810 /*
00811  * ALTER ROLE ... SET
00812  */
00813 Oid
00814 AlterRoleSet(AlterRoleSetStmt *stmt)
00815 {
00816     HeapTuple   roletuple;
00817     Oid         databaseid = InvalidOid;
00818     Oid         roleid = InvalidOid;
00819 
00820     if (stmt->role)
00821     {
00822         roletuple = SearchSysCache1(AUTHNAME, PointerGetDatum(stmt->role));
00823 
00824         if (!HeapTupleIsValid(roletuple))
00825             ereport(ERROR,
00826                     (errcode(ERRCODE_UNDEFINED_OBJECT),
00827                      errmsg("role \"%s\" does not exist", stmt->role)));
00828 
00829         roleid = HeapTupleGetOid(roletuple);
00830 
00831         /*
00832          * Obtain a lock on the role and make sure it didn't go away in the
00833          * meantime.
00834          */
00835         shdepLockAndCheckObject(AuthIdRelationId, HeapTupleGetOid(roletuple));
00836 
00837         /*
00838          * To mess with a superuser you gotta be superuser; else you need
00839          * createrole, or just want to change your own settings
00840          */
00841         if (((Form_pg_authid) GETSTRUCT(roletuple))->rolsuper)
00842         {
00843             if (!superuser())
00844                 ereport(ERROR,
00845                         (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
00846                          errmsg("must be superuser to alter superusers")));
00847         }
00848         else
00849         {
00850             if (!have_createrole_privilege() &&
00851                 HeapTupleGetOid(roletuple) != GetUserId())
00852                 ereport(ERROR,
00853                         (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
00854                          errmsg("permission denied")));
00855         }
00856 
00857         ReleaseSysCache(roletuple);
00858     }
00859 
00860     /* look up and lock the database, if specified */
00861     if (stmt->database != NULL)
00862     {
00863         databaseid = get_database_oid(stmt->database, false);
00864         shdepLockAndCheckObject(DatabaseRelationId, databaseid);
00865 
00866         if (!stmt->role)
00867         {
00868             /*
00869              * If no role is specified, then this is effectively the same as
00870              * ALTER DATABASE ... SET, so use the same permission check.
00871              */
00872             if (!pg_database_ownercheck(databaseid, GetUserId()))
00873                 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_DATABASE,
00874                                stmt->database);
00875         }
00876     }
00877 
00878     if (!stmt->role && !stmt->database)
00879     {
00880         /* Must be superuser to alter settings globally. */
00881         if (!superuser())
00882             ereport(ERROR,
00883                     (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
00884                      errmsg("must be superuser to alter settings globally")));
00885     }
00886 
00887     AlterSetting(databaseid, roleid, stmt->setstmt);
00888 
00889     return roleid;
00890 }
00891 
00892 
00893 /*
00894  * DROP ROLE
00895  */
00896 void
00897 DropRole(DropRoleStmt *stmt)
00898 {
00899     Relation    pg_authid_rel,
00900                 pg_auth_members_rel;
00901     ListCell   *item;
00902 
00903     if (!have_createrole_privilege())
00904         ereport(ERROR,
00905                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
00906                  errmsg("permission denied to drop role")));
00907 
00908     /*
00909      * Scan the pg_authid relation to find the Oid of the role(s) to be
00910      * deleted.
00911      */
00912     pg_authid_rel = heap_open(AuthIdRelationId, RowExclusiveLock);
00913     pg_auth_members_rel = heap_open(AuthMemRelationId, RowExclusiveLock);
00914 
00915     foreach(item, stmt->roles)
00916     {
00917         const char *role = strVal(lfirst(item));
00918         HeapTuple   tuple,
00919                     tmp_tuple;
00920         ScanKeyData scankey;
00921         char       *detail;
00922         char       *detail_log;
00923         SysScanDesc sscan;
00924         Oid         roleid;
00925 
00926         tuple = SearchSysCache1(AUTHNAME, PointerGetDatum(role));
00927         if (!HeapTupleIsValid(tuple))
00928         {
00929             if (!stmt->missing_ok)
00930             {
00931                 ereport(ERROR,
00932                         (errcode(ERRCODE_UNDEFINED_OBJECT),
00933                          errmsg("role \"%s\" does not exist", role)));
00934             }
00935             else
00936             {
00937                 ereport(NOTICE,
00938                         (errmsg("role \"%s\" does not exist, skipping",
00939                                 role)));
00940             }
00941 
00942             continue;
00943         }
00944 
00945         roleid = HeapTupleGetOid(tuple);
00946 
00947         if (roleid == GetUserId())
00948             ereport(ERROR,
00949                     (errcode(ERRCODE_OBJECT_IN_USE),
00950                      errmsg("current user cannot be dropped")));
00951         if (roleid == GetOuterUserId())
00952             ereport(ERROR,
00953                     (errcode(ERRCODE_OBJECT_IN_USE),
00954                      errmsg("current user cannot be dropped")));
00955         if (roleid == GetSessionUserId())
00956             ereport(ERROR,
00957                     (errcode(ERRCODE_OBJECT_IN_USE),
00958                      errmsg("session user cannot be dropped")));
00959 
00960         /*
00961          * For safety's sake, we allow createrole holders to drop ordinary
00962          * roles but not superuser roles.  This is mainly to avoid the
00963          * scenario where you accidentally drop the last superuser.
00964          */
00965         if (((Form_pg_authid) GETSTRUCT(tuple))->rolsuper &&
00966             !superuser())
00967             ereport(ERROR,
00968                     (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
00969                      errmsg("must be superuser to drop superusers")));
00970 
00971         /* DROP hook for the role being removed */
00972         InvokeObjectDropHook(AuthIdRelationId, roleid, 0);
00973 
00974         /*
00975          * Lock the role, so nobody can add dependencies to her while we drop
00976          * her.  We keep the lock until the end of transaction.
00977          */
00978         LockSharedObject(AuthIdRelationId, roleid, 0, AccessExclusiveLock);
00979 
00980         /* Check for pg_shdepend entries depending on this role */
00981         if (checkSharedDependencies(AuthIdRelationId, roleid,
00982                                     &detail, &detail_log))
00983             ereport(ERROR,
00984                     (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
00985                      errmsg("role \"%s\" cannot be dropped because some objects depend on it",
00986                             role),
00987                      errdetail_internal("%s", detail),
00988                      errdetail_log("%s", detail_log)));
00989 
00990         /*
00991          * Remove the role from the pg_authid table
00992          */
00993         simple_heap_delete(pg_authid_rel, &tuple->t_self);
00994 
00995         ReleaseSysCache(tuple);
00996 
00997         /*
00998          * Remove role from the pg_auth_members table.  We have to remove all
00999          * tuples that show it as either a role or a member.
01000          *
01001          * XXX what about grantor entries?  Maybe we should do one heap scan.
01002          */
01003         ScanKeyInit(&scankey,
01004                     Anum_pg_auth_members_roleid,
01005                     BTEqualStrategyNumber, F_OIDEQ,
01006                     ObjectIdGetDatum(roleid));
01007 
01008         sscan = systable_beginscan(pg_auth_members_rel, AuthMemRoleMemIndexId,
01009                                    true, SnapshotNow, 1, &scankey);
01010 
01011         while (HeapTupleIsValid(tmp_tuple = systable_getnext(sscan)))
01012         {
01013             simple_heap_delete(pg_auth_members_rel, &tmp_tuple->t_self);
01014         }
01015 
01016         systable_endscan(sscan);
01017 
01018         ScanKeyInit(&scankey,
01019                     Anum_pg_auth_members_member,
01020                     BTEqualStrategyNumber, F_OIDEQ,
01021                     ObjectIdGetDatum(roleid));
01022 
01023         sscan = systable_beginscan(pg_auth_members_rel, AuthMemMemRoleIndexId,
01024                                    true, SnapshotNow, 1, &scankey);
01025 
01026         while (HeapTupleIsValid(tmp_tuple = systable_getnext(sscan)))
01027         {
01028             simple_heap_delete(pg_auth_members_rel, &tmp_tuple->t_self);
01029         }
01030 
01031         systable_endscan(sscan);
01032 
01033         /*
01034          * Remove any comments or security labels on this role.
01035          */
01036         DeleteSharedComments(roleid, AuthIdRelationId);
01037         DeleteSharedSecurityLabel(roleid, AuthIdRelationId);
01038 
01039         /*
01040          * Remove settings for this role.
01041          */
01042         DropSetting(InvalidOid, roleid);
01043 
01044         /*
01045          * Advance command counter so that later iterations of this loop will
01046          * see the changes already made.  This is essential if, for example,
01047          * we are trying to drop both a role and one of its direct members ---
01048          * we'll get an error if we try to delete the linking pg_auth_members
01049          * tuple twice.  (We do not need a CCI between the two delete loops
01050          * above, because it's not allowed for a role to directly contain
01051          * itself.)
01052          */
01053         CommandCounterIncrement();
01054     }
01055 
01056     /*
01057      * Now we can clean up; but keep locks until commit.
01058      */
01059     heap_close(pg_auth_members_rel, NoLock);
01060     heap_close(pg_authid_rel, NoLock);
01061 }
01062 
01063 /*
01064  * Rename role
01065  */
01066 Oid
01067 RenameRole(const char *oldname, const char *newname)
01068 {
01069     HeapTuple   oldtuple,
01070                 newtuple;
01071     TupleDesc   dsc;
01072     Relation    rel;
01073     Datum       datum;
01074     bool        isnull;
01075     Datum       repl_val[Natts_pg_authid];
01076     bool        repl_null[Natts_pg_authid];
01077     bool        repl_repl[Natts_pg_authid];
01078     int         i;
01079     Oid         roleid;
01080 
01081     rel = heap_open(AuthIdRelationId, RowExclusiveLock);
01082     dsc = RelationGetDescr(rel);
01083 
01084     oldtuple = SearchSysCache1(AUTHNAME, CStringGetDatum(oldname));
01085     if (!HeapTupleIsValid(oldtuple))
01086         ereport(ERROR,
01087                 (errcode(ERRCODE_UNDEFINED_OBJECT),
01088                  errmsg("role \"%s\" does not exist", oldname)));
01089 
01090     /*
01091      * XXX Client applications probably store the session user somewhere, so
01092      * renaming it could cause confusion.  On the other hand, there may not be
01093      * an actual problem besides a little confusion, so think about this and
01094      * decide.  Same for SET ROLE ... we don't restrict renaming the current
01095      * effective userid, though.
01096      */
01097 
01098     roleid = HeapTupleGetOid(oldtuple);
01099 
01100     if (roleid == GetSessionUserId())
01101         ereport(ERROR,
01102                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
01103                  errmsg("session user cannot be renamed")));
01104     if (roleid == GetOuterUserId())
01105         ereport(ERROR,
01106                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
01107                  errmsg("current user cannot be renamed")));
01108 
01109     /* make sure the new name doesn't exist */
01110     if (SearchSysCacheExists1(AUTHNAME, CStringGetDatum(newname)))
01111         ereport(ERROR,
01112                 (errcode(ERRCODE_DUPLICATE_OBJECT),
01113                  errmsg("role \"%s\" already exists", newname)));
01114 
01115     if (strcmp(newname, "public") == 0 ||
01116         strcmp(newname, "none") == 0)
01117         ereport(ERROR,
01118                 (errcode(ERRCODE_RESERVED_NAME),
01119                  errmsg("role name \"%s\" is reserved",
01120                         newname)));
01121 
01122     /*
01123      * createrole is enough privilege unless you want to mess with a superuser
01124      */
01125     if (((Form_pg_authid) GETSTRUCT(oldtuple))->rolsuper)
01126     {
01127         if (!superuser())
01128             ereport(ERROR,
01129                     (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
01130                      errmsg("must be superuser to rename superusers")));
01131     }
01132     else
01133     {
01134         if (!have_createrole_privilege())
01135             ereport(ERROR,
01136                     (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
01137                      errmsg("permission denied to rename role")));
01138     }
01139 
01140     /* OK, construct the modified tuple */
01141     for (i = 0; i < Natts_pg_authid; i++)
01142         repl_repl[i] = false;
01143 
01144     repl_repl[Anum_pg_authid_rolname - 1] = true;
01145     repl_val[Anum_pg_authid_rolname - 1] = DirectFunctionCall1(namein,
01146                                                    CStringGetDatum(newname));
01147     repl_null[Anum_pg_authid_rolname - 1] = false;
01148 
01149     datum = heap_getattr(oldtuple, Anum_pg_authid_rolpassword, dsc, &isnull);
01150 
01151     if (!isnull && isMD5(TextDatumGetCString(datum)))
01152     {
01153         /* MD5 uses the username as salt, so just clear it on a rename */
01154         repl_repl[Anum_pg_authid_rolpassword - 1] = true;
01155         repl_null[Anum_pg_authid_rolpassword - 1] = true;
01156 
01157         ereport(NOTICE,
01158                 (errmsg("MD5 password cleared because of role rename")));
01159     }
01160 
01161     newtuple = heap_modify_tuple(oldtuple, dsc, repl_val, repl_null, repl_repl);
01162     simple_heap_update(rel, &oldtuple->t_self, newtuple);
01163 
01164     CatalogUpdateIndexes(rel, newtuple);
01165 
01166     InvokeObjectPostAlterHook(AuthIdRelationId, roleid, 0);
01167 
01168     ReleaseSysCache(oldtuple);
01169 
01170     /*
01171      * Close pg_authid, but keep lock till commit.
01172      */
01173     heap_close(rel, NoLock);
01174 
01175     return roleid;
01176 }
01177 
01178 /*
01179  * GrantRoleStmt
01180  *
01181  * Grant/Revoke roles to/from roles
01182  */
01183 void
01184 GrantRole(GrantRoleStmt *stmt)
01185 {
01186     Relation    pg_authid_rel;
01187     Oid         grantor;
01188     List       *grantee_ids;
01189     ListCell   *item;
01190 
01191     if (stmt->grantor)
01192         grantor = get_role_oid(stmt->grantor, false);
01193     else
01194         grantor = GetUserId();
01195 
01196     grantee_ids = roleNamesToIds(stmt->grantee_roles);
01197 
01198     /* AccessShareLock is enough since we aren't modifying pg_authid */
01199     pg_authid_rel = heap_open(AuthIdRelationId, AccessShareLock);
01200 
01201     /*
01202      * Step through all of the granted roles and add/remove entries for the
01203      * grantees, or, if admin_opt is set, then just add/remove the admin
01204      * option.
01205      *
01206      * Note: Permissions checking is done by AddRoleMems/DelRoleMems
01207      */
01208     foreach(item, stmt->granted_roles)
01209     {
01210         AccessPriv *priv = (AccessPriv *) lfirst(item);
01211         char       *rolename = priv->priv_name;
01212         Oid         roleid;
01213 
01214         /* Must reject priv(columns) and ALL PRIVILEGES(columns) */
01215         if (rolename == NULL || priv->cols != NIL)
01216             ereport(ERROR,
01217                     (errcode(ERRCODE_INVALID_GRANT_OPERATION),
01218             errmsg("column names cannot be included in GRANT/REVOKE ROLE")));
01219 
01220         roleid = get_role_oid(rolename, false);
01221         if (stmt->is_grant)
01222             AddRoleMems(rolename, roleid,
01223                         stmt->grantee_roles, grantee_ids,
01224                         grantor, stmt->admin_opt);
01225         else
01226             DelRoleMems(rolename, roleid,
01227                         stmt->grantee_roles, grantee_ids,
01228                         stmt->admin_opt);
01229     }
01230 
01231     /*
01232      * Close pg_authid, but keep lock till commit.
01233      */
01234     heap_close(pg_authid_rel, NoLock);
01235 }
01236 
01237 /*
01238  * DropOwnedObjects
01239  *
01240  * Drop the objects owned by a given list of roles.
01241  */
01242 void
01243 DropOwnedObjects(DropOwnedStmt *stmt)
01244 {
01245     List       *role_ids = roleNamesToIds(stmt->roles);
01246     ListCell   *cell;
01247 
01248     /* Check privileges */
01249     foreach(cell, role_ids)
01250     {
01251         Oid         roleid = lfirst_oid(cell);
01252 
01253         if (!has_privs_of_role(GetUserId(), roleid))
01254             ereport(ERROR,
01255                     (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
01256                      errmsg("permission denied to drop objects")));
01257     }
01258 
01259     /* Ok, do it */
01260     shdepDropOwned(role_ids, stmt->behavior);
01261 }
01262 
01263 /*
01264  * ReassignOwnedObjects
01265  *
01266  * Give the objects owned by a given list of roles away to another user.
01267  */
01268 void
01269 ReassignOwnedObjects(ReassignOwnedStmt *stmt)
01270 {
01271     List       *role_ids = roleNamesToIds(stmt->roles);
01272     ListCell   *cell;
01273     Oid         newrole;
01274 
01275     /* Check privileges */
01276     foreach(cell, role_ids)
01277     {
01278         Oid         roleid = lfirst_oid(cell);
01279 
01280         if (!has_privs_of_role(GetUserId(), roleid))
01281             ereport(ERROR,
01282                     (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
01283                      errmsg("permission denied to reassign objects")));
01284     }
01285 
01286     /* Must have privileges on the receiving side too */
01287     newrole = get_role_oid(stmt->newrole, false);
01288 
01289     if (!has_privs_of_role(GetUserId(), newrole))
01290         ereport(ERROR,
01291                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
01292                  errmsg("permission denied to reassign objects")));
01293 
01294     /* Ok, do it */
01295     shdepReassignOwned(role_ids, newrole);
01296 }
01297 
01298 /*
01299  * roleNamesToIds
01300  *
01301  * Given a list of role names (as String nodes), generate a list of role OIDs
01302  * in the same order.
01303  */
01304 static List *
01305 roleNamesToIds(List *memberNames)
01306 {
01307     List       *result = NIL;
01308     ListCell   *l;
01309 
01310     foreach(l, memberNames)
01311     {
01312         char       *rolename = strVal(lfirst(l));
01313         Oid         roleid = get_role_oid(rolename, false);
01314 
01315         result = lappend_oid(result, roleid);
01316     }
01317     return result;
01318 }
01319 
01320 /*
01321  * AddRoleMems -- Add given members to the specified role
01322  *
01323  * rolename: name of role to add to (used only for error messages)
01324  * roleid: OID of role to add to
01325  * memberNames: list of names of roles to add (used only for error messages)
01326  * memberIds: OIDs of roles to add
01327  * grantorId: who is granting the membership
01328  * admin_opt: granting admin option?
01329  *
01330  * Note: caller is responsible for calling auth_file_update_needed().
01331  */
01332 static void
01333 AddRoleMems(const char *rolename, Oid roleid,
01334             List *memberNames, List *memberIds,
01335             Oid grantorId, bool admin_opt)
01336 {
01337     Relation    pg_authmem_rel;
01338     TupleDesc   pg_authmem_dsc;
01339     ListCell   *nameitem;
01340     ListCell   *iditem;
01341 
01342     Assert(list_length(memberNames) == list_length(memberIds));
01343 
01344     /* Skip permission check if nothing to do */
01345     if (!memberIds)
01346         return;
01347 
01348     /*
01349      * Check permissions: must have createrole or admin option on the role to
01350      * be changed.  To mess with a superuser role, you gotta be superuser.
01351      */
01352     if (superuser_arg(roleid))
01353     {
01354         if (!superuser())
01355             ereport(ERROR,
01356                     (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
01357                      errmsg("must be superuser to alter superusers")));
01358     }
01359     else
01360     {
01361         if (!have_createrole_privilege() &&
01362             !is_admin_of_role(grantorId, roleid))
01363             ereport(ERROR,
01364                     (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
01365                      errmsg("must have admin option on role \"%s\"",
01366                             rolename)));
01367     }
01368 
01369     /* XXX not sure about this check */
01370     if (grantorId != GetUserId() && !superuser())
01371         ereport(ERROR,
01372                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
01373                  errmsg("must be superuser to set grantor")));
01374 
01375     pg_authmem_rel = heap_open(AuthMemRelationId, RowExclusiveLock);
01376     pg_authmem_dsc = RelationGetDescr(pg_authmem_rel);
01377 
01378     forboth(nameitem, memberNames, iditem, memberIds)
01379     {
01380         const char *membername = strVal(lfirst(nameitem));
01381         Oid         memberid = lfirst_oid(iditem);
01382         HeapTuple   authmem_tuple;
01383         HeapTuple   tuple;
01384         Datum       new_record[Natts_pg_auth_members];
01385         bool        new_record_nulls[Natts_pg_auth_members];
01386         bool        new_record_repl[Natts_pg_auth_members];
01387 
01388         /*
01389          * Refuse creation of membership loops, including the trivial case
01390          * where a role is made a member of itself.  We do this by checking to
01391          * see if the target role is already a member of the proposed member
01392          * role.  We have to ignore possible superuserness, however, else we
01393          * could never grant membership in a superuser-privileged role.
01394          */
01395         if (is_member_of_role_nosuper(roleid, memberid))
01396             ereport(ERROR,
01397                     (errcode(ERRCODE_INVALID_GRANT_OPERATION),
01398                      (errmsg("role \"%s\" is a member of role \"%s\"",
01399                              rolename, membername))));
01400 
01401         /*
01402          * Check if entry for this role/member already exists; if so, give
01403          * warning unless we are adding admin option.
01404          */
01405         authmem_tuple = SearchSysCache2(AUTHMEMROLEMEM,
01406                                         ObjectIdGetDatum(roleid),
01407                                         ObjectIdGetDatum(memberid));
01408         if (HeapTupleIsValid(authmem_tuple) &&
01409             (!admin_opt ||
01410              ((Form_pg_auth_members) GETSTRUCT(authmem_tuple))->admin_option))
01411         {
01412             ereport(NOTICE,
01413                     (errmsg("role \"%s\" is already a member of role \"%s\"",
01414                             membername, rolename)));
01415             ReleaseSysCache(authmem_tuple);
01416             continue;
01417         }
01418 
01419         /* Build a tuple to insert or update */
01420         MemSet(new_record, 0, sizeof(new_record));
01421         MemSet(new_record_nulls, false, sizeof(new_record_nulls));
01422         MemSet(new_record_repl, false, sizeof(new_record_repl));
01423 
01424         new_record[Anum_pg_auth_members_roleid - 1] = ObjectIdGetDatum(roleid);
01425         new_record[Anum_pg_auth_members_member - 1] = ObjectIdGetDatum(memberid);
01426         new_record[Anum_pg_auth_members_grantor - 1] = ObjectIdGetDatum(grantorId);
01427         new_record[Anum_pg_auth_members_admin_option - 1] = BoolGetDatum(admin_opt);
01428 
01429         if (HeapTupleIsValid(authmem_tuple))
01430         {
01431             new_record_repl[Anum_pg_auth_members_grantor - 1] = true;
01432             new_record_repl[Anum_pg_auth_members_admin_option - 1] = true;
01433             tuple = heap_modify_tuple(authmem_tuple, pg_authmem_dsc,
01434                                       new_record,
01435                                       new_record_nulls, new_record_repl);
01436             simple_heap_update(pg_authmem_rel, &tuple->t_self, tuple);
01437             CatalogUpdateIndexes(pg_authmem_rel, tuple);
01438             ReleaseSysCache(authmem_tuple);
01439         }
01440         else
01441         {
01442             tuple = heap_form_tuple(pg_authmem_dsc,
01443                                     new_record, new_record_nulls);
01444             simple_heap_insert(pg_authmem_rel, tuple);
01445             CatalogUpdateIndexes(pg_authmem_rel, tuple);
01446         }
01447 
01448         /* CCI after each change, in case there are duplicates in list */
01449         CommandCounterIncrement();
01450     }
01451 
01452     /*
01453      * Close pg_authmem, but keep lock till commit.
01454      */
01455     heap_close(pg_authmem_rel, NoLock);
01456 }
01457 
01458 /*
01459  * DelRoleMems -- Remove given members from the specified role
01460  *
01461  * rolename: name of role to del from (used only for error messages)
01462  * roleid: OID of role to del from
01463  * memberNames: list of names of roles to del (used only for error messages)
01464  * memberIds: OIDs of roles to del
01465  * admin_opt: remove admin option only?
01466  *
01467  * Note: caller is responsible for calling auth_file_update_needed().
01468  */
01469 static void
01470 DelRoleMems(const char *rolename, Oid roleid,
01471             List *memberNames, List *memberIds,
01472             bool admin_opt)
01473 {
01474     Relation    pg_authmem_rel;
01475     TupleDesc   pg_authmem_dsc;
01476     ListCell   *nameitem;
01477     ListCell   *iditem;
01478 
01479     Assert(list_length(memberNames) == list_length(memberIds));
01480 
01481     /* Skip permission check if nothing to do */
01482     if (!memberIds)
01483         return;
01484 
01485     /*
01486      * Check permissions: must have createrole or admin option on the role to
01487      * be changed.  To mess with a superuser role, you gotta be superuser.
01488      */
01489     if (superuser_arg(roleid))
01490     {
01491         if (!superuser())
01492             ereport(ERROR,
01493                     (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
01494                      errmsg("must be superuser to alter superusers")));
01495     }
01496     else
01497     {
01498         if (!have_createrole_privilege() &&
01499             !is_admin_of_role(GetUserId(), roleid))
01500             ereport(ERROR,
01501                     (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
01502                      errmsg("must have admin option on role \"%s\"",
01503                             rolename)));
01504     }
01505 
01506     pg_authmem_rel = heap_open(AuthMemRelationId, RowExclusiveLock);
01507     pg_authmem_dsc = RelationGetDescr(pg_authmem_rel);
01508 
01509     forboth(nameitem, memberNames, iditem, memberIds)
01510     {
01511         const char *membername = strVal(lfirst(nameitem));
01512         Oid         memberid = lfirst_oid(iditem);
01513         HeapTuple   authmem_tuple;
01514 
01515         /*
01516          * Find entry for this role/member
01517          */
01518         authmem_tuple = SearchSysCache2(AUTHMEMROLEMEM,
01519                                         ObjectIdGetDatum(roleid),
01520                                         ObjectIdGetDatum(memberid));
01521         if (!HeapTupleIsValid(authmem_tuple))
01522         {
01523             ereport(WARNING,
01524                     (errmsg("role \"%s\" is not a member of role \"%s\"",
01525                             membername, rolename)));
01526             continue;
01527         }
01528 
01529         if (!admin_opt)
01530         {
01531             /* Remove the entry altogether */
01532             simple_heap_delete(pg_authmem_rel, &authmem_tuple->t_self);
01533         }
01534         else
01535         {
01536             /* Just turn off the admin option */
01537             HeapTuple   tuple;
01538             Datum       new_record[Natts_pg_auth_members];
01539             bool        new_record_nulls[Natts_pg_auth_members];
01540             bool        new_record_repl[Natts_pg_auth_members];
01541 
01542             /* Build a tuple to update with */
01543             MemSet(new_record, 0, sizeof(new_record));
01544             MemSet(new_record_nulls, false, sizeof(new_record_nulls));
01545             MemSet(new_record_repl, false, sizeof(new_record_repl));
01546 
01547             new_record[Anum_pg_auth_members_admin_option - 1] = BoolGetDatum(false);
01548             new_record_repl[Anum_pg_auth_members_admin_option - 1] = true;
01549 
01550             tuple = heap_modify_tuple(authmem_tuple, pg_authmem_dsc,
01551                                       new_record,
01552                                       new_record_nulls, new_record_repl);
01553             simple_heap_update(pg_authmem_rel, &tuple->t_self, tuple);
01554             CatalogUpdateIndexes(pg_authmem_rel, tuple);
01555         }
01556 
01557         ReleaseSysCache(authmem_tuple);
01558 
01559         /* CCI after each change, in case there are duplicates in list */
01560         CommandCounterIncrement();
01561     }
01562 
01563     /*
01564      * Close pg_authmem, but keep lock till commit.
01565      */
01566     heap_close(pg_authmem_rel, NoLock);
01567 }