1
0
mirror of https://github.com/postgres/postgres.git synced 2025-07-03 20:02:46 +03:00

Use a bitmask to represent role attributes

The previous representation using a boolean column for each attribute
would not scale as well as we want to add further attributes.

Extra auxilliary functions are added to go along with this change, to
make up for the lost convenience of access of the old representation.

Catalog version bumped due to change in catalogs and the new functions.

Author: Adam Brightwell, minor tweaks by Álvaro
Reviewed by: Stephen Frost, Andres Freund, Álvaro Herrera
This commit is contained in:
Alvaro Herrera
2014-12-23 10:22:09 -03:00
parent 7eca575d1c
commit 1826987a46
30 changed files with 807 additions and 381 deletions

View File

@ -85,7 +85,6 @@ static bool get_db_info(const char *name, LOCKMODE lockmode,
Oid *dbLastSysOidP, TransactionId *dbFrozenXidP,
MultiXactId *dbMinMultiP,
Oid *dbTablespace, char **dbCollate, char **dbCtype);
static bool have_createdb_privilege(void);
static void remove_dbtablespaces(Oid db_id);
static bool check_db_file_conflict(Oid db_id);
static int errdetail_busy_db(int notherbackends, int npreparedxacts);
@ -291,7 +290,7 @@ createdb(const CreatedbStmt *stmt)
* "giveaway" attacks. Note that a superuser will always have both of
* these privileges a fortiori.
*/
if (!have_createdb_privilege())
if (!have_role_attribute(ROLE_ATTR_CREATEDB))
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("permission denied to create database")));
@ -965,7 +964,7 @@ RenameDatabase(const char *oldname, const char *newname)
oldname);
/* must have createdb rights */
if (!have_createdb_privilege())
if (!have_role_attribute(ROLE_ATTR_CREATEDB))
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("permission denied to rename database")));
@ -1623,7 +1622,7 @@ AlterDatabaseOwner(const char *dbname, Oid newOwnerId)
* databases. Because superusers will always have this right, we need
* no special case for them.
*/
if (!have_createdb_privilege())
if (!have_role_attribute(ROLE_ATTR_CREATEDB))
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("permission denied to change owner of database")));
@ -1802,26 +1801,6 @@ get_db_info(const char *name, LOCKMODE lockmode,
return result;
}
/* Check if current user has createdb privileges */
static bool
have_createdb_privilege(void)
{
bool result = false;
HeapTuple utup;
/* Superusers can always do everything */
if (superuser())
return true;
utup = SearchSysCache1(AUTHOID, ObjectIdGetDatum(GetUserId()));
if (HeapTupleIsValid(utup))
{
result = ((Form_pg_authid) GETSTRUCT(utup))->rolcreatedb;
ReleaseSysCache(utup);
}
return result;
}
/*
* Remove tablespace directories
*

View File

@ -56,14 +56,6 @@ static void DelRoleMems(const char *rolename, Oid roleid,
bool admin_opt);
/* Check if current user has createrole privileges */
static bool
have_createrole_privilege(void)
{
return has_createrole_privilege(GetUserId());
}
/*
* CREATE ROLE
*/
@ -81,13 +73,7 @@ CreateRole(CreateRoleStmt *stmt)
char *password = NULL; /* user password */
bool encrypt_password = Password_encryption; /* encrypt password? */
char encrypted_password[MD5_PASSWD_LEN + 1];
bool issuper = false; /* Make the user a superuser? */
bool inherit = true; /* Auto inherit privileges? */
bool createrole = false; /* Can this user create roles? */
bool createdb = false; /* Can the user create databases? */
bool canlogin = false; /* Can this user login? */
bool isreplication = false; /* Is this a replication role? */
bool bypassrls = false; /* Is this a row security enabled role? */
RoleAttr attributes;
int connlimit = -1; /* maximum connections allowed */
List *addroleto = NIL; /* roles to make this a member of */
List *rolemembers = NIL; /* roles to be members of this role */
@ -109,13 +95,17 @@ CreateRole(CreateRoleStmt *stmt)
DefElem *dvalidUntil = NULL;
DefElem *dbypassRLS = NULL;
/* The defaults can vary depending on the original statement type */
/*
* Every role has INHERIT by default, and CANLOGIN depends on the statement
* type.
*/
attributes = ROLE_ATTR_INHERIT;
switch (stmt->stmt_type)
{
case ROLESTMT_ROLE:
break;
case ROLESTMT_USER:
canlogin = true;
attributes |= ROLE_ATTR_CANLOGIN;
/* may eventually want inherit to default to false here */
break;
case ROLESTMT_GROUP:
@ -249,18 +239,76 @@ CreateRole(CreateRoleStmt *stmt)
if (dpassword && dpassword->arg)
password = strVal(dpassword->arg);
/* Set up role attributes and check permissions to set each of them */
if (dissuper)
issuper = intVal(dissuper->arg) != 0;
{
if (intVal(dissuper->arg) != 0)
{
if (!superuser())
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("must be superuser to create superusers")));
attributes |= ROLE_ATTR_SUPERUSER;
}
else
attributes &= ~ROLE_ATTR_SUPERUSER;
}
if (dinherit)
inherit = intVal(dinherit->arg) != 0;
{
if (intVal(dinherit->arg) != 0)
attributes |= ROLE_ATTR_INHERIT;
else
attributes &= ~ROLE_ATTR_INHERIT;
}
if (dcreaterole)
createrole = intVal(dcreaterole->arg) != 0;
{
if (intVal(dcreaterole->arg) != 0)
attributes |= ROLE_ATTR_CREATEROLE;
else
attributes &= ~ROLE_ATTR_CREATEROLE;
}
if (dcreatedb)
createdb = intVal(dcreatedb->arg) != 0;
{
if (intVal(dcreatedb->arg) != 0)
attributes |= ROLE_ATTR_CREATEDB;
else
attributes &= ~ROLE_ATTR_CREATEDB;
}
if (dcanlogin)
canlogin = intVal(dcanlogin->arg) != 0;
{
if (intVal(dcanlogin->arg) != 0)
attributes |= ROLE_ATTR_CANLOGIN;
else
attributes &= ~ROLE_ATTR_CANLOGIN;
}
if (disreplication)
isreplication = intVal(disreplication->arg) != 0;
{
if (intVal(disreplication->arg) != 0)
{
if (!superuser())
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("must be superuser to create replication users")));
attributes |= ROLE_ATTR_REPLICATION;
}
else
attributes &= ~ROLE_ATTR_REPLICATION;
}
if (dbypassRLS)
{
if (intVal(dbypassRLS->arg) != 0)
{
if (!superuser())
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("must be superuser to change bypassrls attribute")));
attributes |= ROLE_ATTR_BYPASSRLS;
}
else
attributes &= ~ROLE_ATTR_BYPASSRLS;
}
if (dconnlimit)
{
connlimit = intVal(dconnlimit->arg);
@ -277,38 +325,12 @@ CreateRole(CreateRoleStmt *stmt)
adminmembers = (List *) dadminmembers->arg;
if (dvalidUntil)
validUntil = strVal(dvalidUntil->arg);
if (dbypassRLS)
bypassrls = intVal(dbypassRLS->arg) != 0;
/* Check some permissions first */
if (issuper)
{
if (!superuser())
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("must be superuser to create superusers")));
}
else if (isreplication)
{
if (!superuser())
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("must be superuser to create replication users")));
}
else if (bypassrls)
{
if (!superuser())
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("must be superuser to change bypassrls attribute.")));
}
else
{
if (!have_createrole_privilege())
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("permission denied to create role")));
}
/* Check permissions */
if (!have_role_attribute(ROLE_ATTR_CREATEROLE))
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("permission denied to create role")));
if (strcmp(stmt->role, "public") == 0 ||
strcmp(stmt->role, "none") == 0)
@ -364,14 +386,8 @@ CreateRole(CreateRoleStmt *stmt)
new_record[Anum_pg_authid_rolname - 1] =
DirectFunctionCall1(namein, CStringGetDatum(stmt->role));
new_record[Anum_pg_authid_rolsuper - 1] = BoolGetDatum(issuper);
new_record[Anum_pg_authid_rolinherit - 1] = BoolGetDatum(inherit);
new_record[Anum_pg_authid_rolcreaterole - 1] = BoolGetDatum(createrole);
new_record[Anum_pg_authid_rolcreatedb - 1] = BoolGetDatum(createdb);
/* superuser gets catupdate right by default */
new_record[Anum_pg_authid_rolcatupdate - 1] = BoolGetDatum(issuper);
new_record[Anum_pg_authid_rolcanlogin - 1] = BoolGetDatum(canlogin);
new_record[Anum_pg_authid_rolreplication - 1] = BoolGetDatum(isreplication);
new_record[Anum_pg_authid_rolattr - 1] = Int64GetDatum(attributes);
new_record[Anum_pg_authid_rolconnlimit - 1] = Int32GetDatum(connlimit);
if (password)
@ -394,8 +410,6 @@ CreateRole(CreateRoleStmt *stmt)
new_record[Anum_pg_authid_rolvaliduntil - 1] = validUntil_datum;
new_record_nulls[Anum_pg_authid_rolvaliduntil - 1] = validUntil_null;
new_record[Anum_pg_authid_rolbypassrls - 1] = BoolGetDatum(bypassrls);
tuple = heap_form_tuple(pg_authid_dsc, new_record, new_record_nulls);
/*
@ -508,6 +522,7 @@ AlterRole(AlterRoleStmt *stmt)
DefElem *dvalidUntil = NULL;
DefElem *dbypassRLS = NULL;
Oid roleid;
RoleAttr attributes;
/* Extract options from the statement node tree */
foreach(option, stmt->options)
@ -658,31 +673,34 @@ AlterRole(AlterRoleStmt *stmt)
roleid = HeapTupleGetOid(tuple);
/*
* To mess with a superuser you gotta be superuser; else you need
* createrole, or just want to change your own password
* To mess with a superuser or a replication user you gotta be superuser;
* else you need createrole, or just want to change your own password
*/
if (((Form_pg_authid) GETSTRUCT(tuple))->rolsuper || issuper >= 0)
attributes = ((Form_pg_authid) GETSTRUCT(tuple))->rolattr;
if ((attributes & ROLE_ATTR_SUPERUSER) || issuper >= 0)
{
if (!superuser())
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("must be superuser to alter superusers")));
}
else if (((Form_pg_authid) GETSTRUCT(tuple))->rolreplication || isreplication >= 0)
else if ((attributes & ROLE_ATTR_REPLICATION) || isreplication >= 0)
{
if (!superuser())
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("must be superuser to alter replication users")));
}
else if (((Form_pg_authid) GETSTRUCT(tuple))->rolbypassrls || bypassrls >= 0)
else if ((attributes & ROLE_ATTR_BYPASSRLS) || bypassrls >= 0)
{
if (!superuser())
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("must be superuser to change bypassrls attribute")));
}
else if (!have_createrole_privilege())
else if (!have_role_attribute(ROLE_ATTR_CREATEROLE))
{
if (!(inherit < 0 &&
createrole < 0 &&
@ -743,43 +761,71 @@ AlterRole(AlterRoleStmt *stmt)
*/
if (issuper >= 0)
{
new_record[Anum_pg_authid_rolsuper - 1] = BoolGetDatum(issuper > 0);
new_record_repl[Anum_pg_authid_rolsuper - 1] = true;
new_record[Anum_pg_authid_rolcatupdate - 1] = BoolGetDatum(issuper > 0);
new_record_repl[Anum_pg_authid_rolcatupdate - 1] = true;
if (issuper > 0)
attributes |= ROLE_ATTR_SUPERUSER | ROLE_ATTR_CATUPDATE;
else
attributes &= ~(ROLE_ATTR_SUPERUSER | ROLE_ATTR_CATUPDATE);
new_record_repl[Anum_pg_authid_rolattr - 1] = true;
}
if (inherit >= 0)
{
new_record[Anum_pg_authid_rolinherit - 1] = BoolGetDatum(inherit > 0);
new_record_repl[Anum_pg_authid_rolinherit - 1] = true;
if (inherit > 0)
attributes |= ROLE_ATTR_INHERIT;
else
attributes &= ~ROLE_ATTR_INHERIT;
new_record_repl[Anum_pg_authid_rolattr - 1] = true;
}
if (createrole >= 0)
{
new_record[Anum_pg_authid_rolcreaterole - 1] = BoolGetDatum(createrole > 0);
new_record_repl[Anum_pg_authid_rolcreaterole - 1] = true;
if (createrole > 0)
attributes |= ROLE_ATTR_CREATEROLE;
else
attributes &= ~ROLE_ATTR_CREATEROLE;
new_record_repl[Anum_pg_authid_rolattr - 1] = true;
}
if (createdb >= 0)
{
new_record[Anum_pg_authid_rolcreatedb - 1] = BoolGetDatum(createdb > 0);
new_record_repl[Anum_pg_authid_rolcreatedb - 1] = true;
if (createdb > 0)
attributes |= ROLE_ATTR_CREATEDB;
else
attributes &= ~ROLE_ATTR_CREATEDB;
new_record_repl[Anum_pg_authid_rolattr - 1] = true;
}
if (canlogin >= 0)
{
new_record[Anum_pg_authid_rolcanlogin - 1] = BoolGetDatum(canlogin > 0);
new_record_repl[Anum_pg_authid_rolcanlogin - 1] = true;
if (canlogin > 0)
attributes |= ROLE_ATTR_CANLOGIN;
else
attributes &= ~ROLE_ATTR_CANLOGIN;
new_record_repl[Anum_pg_authid_rolattr - 1] = true;
}
if (isreplication >= 0)
{
new_record[Anum_pg_authid_rolreplication - 1] = BoolGetDatum(isreplication > 0);
new_record_repl[Anum_pg_authid_rolreplication - 1] = true;
if (isreplication > 0)
attributes |= ROLE_ATTR_REPLICATION;
else
attributes &= ~ROLE_ATTR_REPLICATION;
new_record_repl[Anum_pg_authid_rolattr - 1] = true;
}
if (bypassrls >= 0)
{
if (bypassrls > 0)
attributes |= ROLE_ATTR_BYPASSRLS;
else
attributes &= ~ROLE_ATTR_BYPASSRLS;
new_record_repl[Anum_pg_authid_rolattr - 1] = true;
}
/* If any role attributes were set, then update. */
if (new_record_repl[Anum_pg_authid_rolattr - 1])
new_record[Anum_pg_authid_rolattr - 1] = Int64GetDatum(attributes);
if (dconnlimit)
{
new_record[Anum_pg_authid_rolconnlimit - 1] = Int32GetDatum(connlimit);
@ -815,11 +861,6 @@ AlterRole(AlterRoleStmt *stmt)
new_record_nulls[Anum_pg_authid_rolvaliduntil - 1] = validUntil_null;
new_record_repl[Anum_pg_authid_rolvaliduntil - 1] = true;
if (bypassrls >= 0)
{
new_record[Anum_pg_authid_rolbypassrls - 1] = BoolGetDatum(bypassrls > 0);
new_record_repl[Anum_pg_authid_rolbypassrls - 1] = true;
}
new_tuple = heap_modify_tuple(tuple, pg_authid_dsc, new_record,
new_record_nulls, new_record_repl);
@ -867,6 +908,7 @@ AlterRoleSet(AlterRoleSetStmt *stmt)
HeapTuple roletuple;
Oid databaseid = InvalidOid;
Oid roleid = InvalidOid;
RoleAttr attributes;
if (stmt->role)
{
@ -889,7 +931,8 @@ AlterRoleSet(AlterRoleSetStmt *stmt)
* To mess with a superuser you gotta be superuser; else you need
* createrole, or just want to change your own settings
*/
if (((Form_pg_authid) GETSTRUCT(roletuple))->rolsuper)
attributes = ((Form_pg_authid) GETSTRUCT(roletuple))->rolattr;
if (attributes & ROLE_ATTR_SUPERUSER)
{
if (!superuser())
ereport(ERROR,
@ -898,7 +941,7 @@ AlterRoleSet(AlterRoleSetStmt *stmt)
}
else
{
if (!have_createrole_privilege() &&
if (!have_role_attribute(ROLE_ATTR_CREATEROLE) &&
HeapTupleGetOid(roletuple) != GetUserId())
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
@ -951,7 +994,7 @@ DropRole(DropRoleStmt *stmt)
pg_auth_members_rel;
ListCell *item;
if (!have_createrole_privilege())
if (!have_role_attribute(ROLE_ATTR_CREATEROLE))
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("permission denied to drop role")));
@ -973,6 +1016,7 @@ DropRole(DropRoleStmt *stmt)
char *detail_log;
SysScanDesc sscan;
Oid roleid;
RoleAttr attributes;
tuple = SearchSysCache1(AUTHNAME, PointerGetDatum(role));
if (!HeapTupleIsValid(tuple))
@ -1013,8 +1057,8 @@ DropRole(DropRoleStmt *stmt)
* roles but not superuser roles. This is mainly to avoid the
* scenario where you accidentally drop the last superuser.
*/
if (((Form_pg_authid) GETSTRUCT(tuple))->rolsuper &&
!superuser())
attributes = ((Form_pg_authid) GETSTRUCT(tuple))->rolattr;
if ((attributes & ROLE_ATTR_SUPERUSER) && !superuser())
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("must be superuser to drop superusers")));
@ -1128,6 +1172,7 @@ RenameRole(const char *oldname, const char *newname)
bool repl_repl[Natts_pg_authid];
int i;
Oid roleid;
RoleAttr attributes;
rel = heap_open(AuthIdRelationId, RowExclusiveLock);
dsc = RelationGetDescr(rel);
@ -1173,7 +1218,8 @@ RenameRole(const char *oldname, const char *newname)
/*
* createrole is enough privilege unless you want to mess with a superuser
*/
if (((Form_pg_authid) GETSTRUCT(oldtuple))->rolsuper)
attributes = ((Form_pg_authid) GETSTRUCT(oldtuple))->rolattr;
if (attributes & ROLE_ATTR_SUPERUSER)
{
if (!superuser())
ereport(ERROR,
@ -1182,7 +1228,7 @@ RenameRole(const char *oldname, const char *newname)
}
else
{
if (!have_createrole_privilege())
if (!have_role_attribute(ROLE_ATTR_CREATEROLE))
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("permission denied to rename role")));
@ -1409,7 +1455,7 @@ AddRoleMems(const char *rolename, Oid roleid,
}
else
{
if (!have_createrole_privilege() &&
if (!have_role_attribute(ROLE_ATTR_CREATEROLE) &&
!is_admin_of_role(grantorId, roleid))
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
@ -1555,7 +1601,7 @@ DelRoleMems(const char *rolename, Oid roleid,
}
else
{
if (!have_createrole_privilege() &&
if (!have_role_attribute(ROLE_ATTR_CREATEROLE) &&
!is_admin_of_role(GetUserId(), roleid))
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),

View File

@ -776,6 +776,7 @@ check_session_authorization(char **newval, void **extra, GucSource source)
Oid roleid;
bool is_superuser;
role_auth_extra *myextra;
RoleAttr attributes;
/* Do nothing for the boot_val default of NULL */
if (*newval == NULL)
@ -800,7 +801,8 @@ check_session_authorization(char **newval, void **extra, GucSource source)
}
roleid = HeapTupleGetOid(roleTup);
is_superuser = ((Form_pg_authid) GETSTRUCT(roleTup))->rolsuper;
attributes = ((Form_pg_authid) GETSTRUCT(roleTup))->rolattr;
is_superuser = (attributes & ROLE_ATTR_SUPERUSER);
ReleaseSysCache(roleTup);
@ -844,6 +846,7 @@ check_role(char **newval, void **extra, GucSource source)
Oid roleid;
bool is_superuser;
role_auth_extra *myextra;
RoleAttr attributes;
if (strcmp(*newval, "none") == 0)
{
@ -872,7 +875,8 @@ check_role(char **newval, void **extra, GucSource source)
}
roleid = HeapTupleGetOid(roleTup);
is_superuser = ((Form_pg_authid) GETSTRUCT(roleTup))->rolsuper;
attributes = ((Form_pg_authid) GETSTRUCT(roleTup))->rolattr;
is_superuser = (attributes & ROLE_ATTR_SUPERUSER);
ReleaseSysCache(roleTup);