All,
I am simply breaking this out into its own thread from the discussion on
additional role attributes (
http://www.postgresql.org/message-id/[email protected]
).
A few related threads/discussions/posts:
*
http://www.postgresql.org/message-id/[email protected]
*
http://www.postgresql.org/message-id/CA+TgmobkYXNOWKEKzX2qGPSr_nvacFGueV=orxnd-xmzvov...@mail.gmail.com
*
http://www.postgresql.org/message-id/[email protected]
Based on these above I have attached an initial WIP patch for review and
discussion that takes a swing at changing the catalog representation.
This patch includes:
* int64 (C) to int8 (SQL) mapping for genbki.
* replace all role attributes columns in pg_authid with single int64 column
named rolattr.
* update CreateRole and AlterRole to use rolattr.
* update all has_*_privilege functions to check rolattr.
* builtin SQL function 'has_role_attribute' that takes a role oid and text
name of the attribute as input and returns a boolean.
Items not currently addressed:
* New syntax - previous discussion indicated a potential desire for this,
but I feel more discussion needs to occur around these before proposing as
part of a patch. Specifically, how would CREATE USER/ROLE be affected? I
suppose it is OK to keep it as WITH <attribute_or_capability>, though if
ALTER ROLE is modified to have ADD | DROP CAPABILITY for consistency would
WITH CAPABILITY <value,...>, make more sense for CREATE? I also felt these
were mutually exclusive from an implementation perspective and therefore
thought it would be best to keep them separate.
* Documentation - want to gain feedback on implementation prior to making
changes.
* Update regression tests, rules test for system_views - want to gain
feedback on approach to handling pg_roles, etc. before updating.
Thanks,
Adam
--
Adam Brightwell - [email protected]
Database Engineer - www.crunchydatasolutions.com
diff --git a/src/backend/catalog/Catalog.pm b/src/backend/catalog/Catalog.pm
new file mode 100644
index eb91c53..523b379
*** a/src/backend/catalog/Catalog.pm
--- b/src/backend/catalog/Catalog.pm
*************** sub Catalogs
*** 33,38 ****
--- 33,39 ----
my %RENAME_ATTTYPE = (
'int16' => 'int2',
'int32' => 'int4',
+ 'int64' => 'int8',
'Oid' => 'oid',
'NameData' => 'name',
'TransactionId' => 'xid');
diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c
new file mode 100644
index d30612c..93eb2e6
*** a/src/backend/catalog/aclchk.c
--- b/src/backend/catalog/aclchk.c
*************** aclcheck_error_type(AclResult aclerr, Oi
*** 3423,3448 ****
}
- /* Check if given user has rolcatupdate privilege according to pg_authid */
- static bool
- has_rolcatupdate(Oid roleid)
- {
- bool rolcatupdate;
- HeapTuple tuple;
-
- tuple = SearchSysCache1(AUTHOID, ObjectIdGetDatum(roleid));
- if (!HeapTupleIsValid(tuple))
- ereport(ERROR,
- (errcode(ERRCODE_UNDEFINED_OBJECT),
- errmsg("role with OID %u does not exist", roleid)));
-
- rolcatupdate = ((Form_pg_authid) GETSTRUCT(tuple))->rolcatupdate;
-
- ReleaseSysCache(tuple);
-
- return rolcatupdate;
- }
-
/*
* Relay for the various pg_*_mask routines depending on object kind
*/
--- 3423,3428 ----
*************** pg_class_aclmask(Oid table_oid, Oid role
*** 3630,3636 ****
if ((mask & (ACL_INSERT | ACL_UPDATE | ACL_DELETE | ACL_TRUNCATE | ACL_USAGE)) &&
IsSystemClass(table_oid, classForm) &&
classForm->relkind != RELKIND_VIEW &&
! !has_rolcatupdate(roleid) &&
!allowSystemTableMods)
{
#ifdef ACLDEBUG
--- 3610,3616 ----
if ((mask & (ACL_INSERT | ACL_UPDATE | ACL_DELETE | ACL_TRUNCATE | ACL_USAGE)) &&
IsSystemClass(table_oid, classForm) &&
classForm->relkind != RELKIND_VIEW &&
! !role_has_attribute(roleid, ROLE_ATTR_CATUPDATE) &&
!allowSystemTableMods)
{
#ifdef ACLDEBUG
*************** pg_extension_ownercheck(Oid ext_oid, Oid
*** 5051,5056 ****
--- 5031,5058 ----
}
/*
+ * Check whether the specified role has a specific role attribute.
+ */
+ bool
+ role_has_attribute(Oid roleid, RoleAttr attribute)
+ {
+ RoleAttr attributes;
+ HeapTuple tuple;
+
+ tuple = SearchSysCache1(AUTHOID, ObjectIdGetDatum(roleid));
+
+ if (!HeapTupleIsValid(tuple))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("role with OID %u does not exist", roleid)));
+
+ attributes = ((Form_pg_authid) GETSTRUCT(tuple))->rolattr;
+ ReleaseSysCache(tuple);
+
+ return ((attributes & attribute) > 0);
+ }
+
+ /*
* Check whether specified role has CREATEROLE privilege (or is a superuser)
*
* Note: roles do not have owners per se; instead we use this test in
*************** pg_extension_ownercheck(Oid ext_oid, Oid
*** 5064,5102 ****
bool
has_createrole_privilege(Oid roleid)
{
- bool result = false;
- HeapTuple utup;
-
/* Superusers bypass all permission checking. */
if (superuser_arg(roleid))
return true;
! utup = SearchSysCache1(AUTHOID, ObjectIdGetDatum(roleid));
! if (HeapTupleIsValid(utup))
! {
! result = ((Form_pg_authid) GETSTRUCT(utup))->rolcreaterole;
! ReleaseSysCache(utup);
! }
! return result;
}
bool
has_bypassrls_privilege(Oid roleid)
{
- bool result = false;
- HeapTuple utup;
-
/* Superusers bypass all permission checking. */
if (superuser_arg(roleid))
return true;
! utup = SearchSysCache1(AUTHOID, ObjectIdGetDatum(roleid));
! if (HeapTupleIsValid(utup))
! {
! result = ((Form_pg_authid) GETSTRUCT(utup))->rolbypassrls;
! ReleaseSysCache(utup);
! }
! return result;
}
/*
--- 5066,5089 ----
bool
has_createrole_privilege(Oid roleid)
{
/* Superusers bypass all permission checking. */
if (superuser_arg(roleid))
return true;
! return role_has_attribute(roleid, ROLE_ATTR_CREATEROLE);
}
+ /*
+ * Check whether specified role has BYPASSRLS privilege.
+ */
bool
has_bypassrls_privilege(Oid roleid)
{
/* Superusers bypass all permission checking. */
if (superuser_arg(roleid))
return true;
! return role_has_attribute(roleid, ROLE_ATTR_BYPASSRLS);
}
/*
diff --git a/src/backend/catalog/information_schema.sql b/src/backend/catalog/information_schema.sql
new file mode 100644
index a036c62..6904716
*** a/src/backend/catalog/information_schema.sql
--- b/src/backend/catalog/information_schema.sql
*************** CREATE VIEW user_mapping_options AS
*** 2884,2890 ****
CAST((pg_options_to_table(um.umoptions)).option_name AS sql_identifier) AS option_name,
CAST(CASE WHEN (umuser <> 0 AND authorization_identifier = current_user)
OR (umuser = 0 AND pg_has_role(srvowner, 'USAGE'))
! OR (SELECT rolsuper FROM pg_authid WHERE rolname = current_user) THEN (pg_options_to_table(um.umoptions)).option_value
ELSE NULL END AS character_data) AS option_value
FROM _pg_user_mappings um;
--- 2884,2895 ----
CAST((pg_options_to_table(um.umoptions)).option_name AS sql_identifier) AS option_name,
CAST(CASE WHEN (umuser <> 0 AND authorization_identifier = current_user)
OR (umuser = 0 AND pg_has_role(srvowner, 'USAGE'))
! OR (
! SELECT has_role_attribute(pg_authid.oid, 'SUPERUSER') AS rolsuper
! FROM pg_authid
! WHERE rolname = current_user
! )
! THEN (pg_options_to_table(um.umoptions)).option_value
ELSE NULL END AS character_data) AS option_value
FROM _pg_user_mappings um;
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
new file mode 100644
index a819952..8e02116
*** a/src/backend/catalog/system_views.sql
--- b/src/backend/catalog/system_views.sql
***************
*** 9,25 ****
CREATE VIEW pg_roles AS
SELECT
rolname,
! rolsuper,
! rolinherit,
! rolcreaterole,
! rolcreatedb,
! rolcatupdate,
! rolcanlogin,
! rolreplication,
rolconnlimit,
'********'::text as rolpassword,
rolvaliduntil,
- rolbypassrls,
setconfig as rolconfig,
pg_authid.oid
FROM pg_authid LEFT JOIN pg_db_role_setting s
--- 9,25 ----
CREATE VIEW pg_roles AS
SELECT
rolname,
! has_role_attribute(pg_authid.oid, 'SUPERUSER') AS rolsuper,
! has_role_attribute(pg_authid.oid, 'INHERIT') AS rolinherit,
! has_role_attribute(pg_authid.oid, 'CREATEROLE') AS rolcreaterole,
! has_role_attribute(pg_authid.oid, 'CREATEDB') AS rolcreatedb,
! has_role_attribute(pg_authid.oid, 'CATUPDATE') AS rolcatupdate,
! has_role_attribute(pg_authid.oid, 'CANLOGIN') AS rolcanlogin,
! has_role_attribute(pg_authid.oid, 'REPLICATION') AS rolreplication,
! has_role_attribute(pg_authid.oid, 'BYPASSRLS') AS rolbypassrls,
rolconnlimit,
'********'::text as rolpassword,
rolvaliduntil,
setconfig as rolconfig,
pg_authid.oid
FROM pg_authid LEFT JOIN pg_db_role_setting s
*************** CREATE VIEW pg_shadow AS
*** 29,44 ****
SELECT
rolname AS usename,
pg_authid.oid AS usesysid,
! rolcreatedb AS usecreatedb,
! rolsuper AS usesuper,
! rolcatupdate AS usecatupd,
! rolreplication AS userepl,
rolpassword AS passwd,
rolvaliduntil::abstime AS valuntil,
setconfig AS useconfig
FROM pg_authid LEFT JOIN pg_db_role_setting s
ON (pg_authid.oid = setrole AND setdatabase = 0)
! WHERE rolcanlogin;
REVOKE ALL on pg_shadow FROM public;
--- 29,44 ----
SELECT
rolname AS usename,
pg_authid.oid AS usesysid,
! has_role_attribute(pg_authid.oid, 'CREATEDB') AS usecreatedb,
! has_role_attribute(pg_authid.oid, 'SUPERUSER') AS usesuper,
! has_role_attribute(pg_authid.oid, 'CATUPDATE') AS usecatupd,
! has_role_attribute(pg_authid.oid, 'REPLICATION') AS userepl,
rolpassword AS passwd,
rolvaliduntil::abstime AS valuntil,
setconfig AS useconfig
FROM pg_authid LEFT JOIN pg_db_role_setting s
ON (pg_authid.oid = setrole AND setdatabase = 0)
! WHERE has_role_attribute(pg_authid.oid, 'CANLOGIN');
REVOKE ALL on pg_shadow FROM public;
*************** CREATE VIEW pg_group AS
*** 48,54 ****
oid AS grosysid,
ARRAY(SELECT member FROM pg_auth_members WHERE roleid = oid) AS grolist
FROM pg_authid
! WHERE NOT rolcanlogin;
CREATE VIEW pg_user AS
SELECT
--- 48,54 ----
oid AS grosysid,
ARRAY(SELECT member FROM pg_auth_members WHERE roleid = oid) AS grolist
FROM pg_authid
! WHERE NOT has_role_attribute(pg_authid.oid, 'CANLOGIN');
CREATE VIEW pg_user AS
SELECT
diff --git a/src/backend/commands/dbcommands.c b/src/backend/commands/dbcommands.c
new file mode 100644
index 94c82d3..78dae2d
*** a/src/backend/commands/dbcommands.c
--- b/src/backend/commands/dbcommands.c
*************** get_db_info(const char *name, LOCKMODE l
*** 1812,1831 ****
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;
}
/*
--- 1812,1822 ----
static bool
have_createdb_privilege(void)
{
/* Superusers can always do everything */
if (superuser())
return true;
! return role_has_attribute(GetUserId(), ROLE_ATTR_CREATEDB);
}
/*
diff --git a/src/backend/commands/user.c b/src/backend/commands/user.c
new file mode 100644
index 1a73fd8..72c5dcc
*** a/src/backend/commands/user.c
--- b/src/backend/commands/user.c
*************** have_createrole_privilege(void)
*** 63,68 ****
--- 63,73 ----
return has_createrole_privilege(GetUserId());
}
+ static RoleAttr
+ set_role_attribute(RoleAttr attributes, RoleAttr attribute)
+ {
+ return ((attributes & ~(0xFFFFFFFFFFFFFFFF)) | attribute);
+ }
/*
* CREATE ROLE
*************** CreateRole(CreateRoleStmt *stmt)
*** 81,93 ****
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? */
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 */
--- 86,99 ----
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 = ROLE_ATTR_NONE; /* role attributes, initialized to none. */
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 */
*************** CreateRole(CreateRoleStmt *stmt)
*** 249,254 ****
--- 255,262 ----
if (dpassword && dpassword->arg)
password = strVal(dpassword->arg);
+
+ /* Role Attributes */
if (dissuper)
issuper = intVal(dissuper->arg) != 0;
if (dinherit)
*************** CreateRole(CreateRoleStmt *stmt)
*** 261,266 ****
--- 269,277 ----
canlogin = intVal(dcanlogin->arg) != 0;
if (disreplication)
isreplication = intVal(disreplication->arg) != 0;
+ if (dbypassRLS)
+ bypassrls = intVal(dbypassRLS->arg) != 0;
+
if (dconnlimit)
{
connlimit = intVal(dconnlimit->arg);
*************** CreateRole(CreateRoleStmt *stmt)
*** 277,284 ****
adminmembers = (List *) dadminmembers->arg;
if (dvalidUntil)
validUntil = strVal(dvalidUntil->arg);
- if (dbypassRLS)
- bypassrls = intVal(dbypassRLS->arg) != 0;
/* Check some permissions first */
if (issuper)
--- 288,293 ----
*************** CreateRole(CreateRoleStmt *stmt)
*** 355,360 ****
--- 364,385 ----
validUntil_datum,
validUntil_null);
+ /* Set all role attributes */
+ if (issuper)
+ attributes |= ROLE_ATTR_SUPERUSER;
+ if (inherit)
+ attributes |= ROLE_ATTR_INHERIT;
+ if (createrole)
+ attributes |= ROLE_ATTR_CREATEROLE;
+ if (createdb)
+ attributes |= ROLE_ATTR_CREATEDB;
+ if (canlogin)
+ attributes |= ROLE_ATTR_CANLOGIN;
+ if (isreplication)
+ attributes |= ROLE_ATTR_REPLICATION;
+ if (bypassrls)
+ attributes |= ROLE_ATTR_BYPASSRLS;
+
/*
* Build a tuple to insert
*/
*************** CreateRole(CreateRoleStmt *stmt)
*** 364,377 ****
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_rolconnlimit - 1] = Int32GetDatum(connlimit);
if (password)
--- 389,396 ----
new_record[Anum_pg_authid_rolname - 1] =
DirectFunctionCall1(namein, CStringGetDatum(stmt->role));
! new_record[Anum_pg_authid_rolattr - 1] = Int64GetDatum(attributes);
!
new_record[Anum_pg_authid_rolconnlimit - 1] = Int32GetDatum(connlimit);
if (password)
*************** CreateRole(CreateRoleStmt *stmt)
*** 394,401 ****
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);
/*
--- 413,418 ----
*************** AlterRole(AlterRoleStmt *stmt)
*** 508,513 ****
--- 525,531 ----
DefElem *dvalidUntil = NULL;
DefElem *dbypassRLS = NULL;
Oid roleid;
+ RoleAttr attributes;
/* Extract options from the statement node tree */
foreach(option, stmt->options)
*************** AlterRole(AlterRoleStmt *stmt)
*** 661,681 ****
* To mess with a superuser 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)
{
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)
{
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)
{
if (!superuser())
ereport(ERROR,
--- 679,702 ----
* To mess with a superuser you gotta be superuser; else you need
* createrole, or just want to change your own password
*/
!
! attributes = ((Form_pg_authid) GETSTRUCT(tuple))->rolattr;
!
! if (((attributes & ROLE_ATTR_SUPERUSER) > 0) || issuper >= 0)
{
if (!superuser())
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("must be superuser to alter superusers")));
}
! else if (((attributes & ROLE_ATTR_REPLICATION) > 0) || isreplication >= 0)
{
if (!superuser())
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("must be superuser to alter replication users")));
}
! else if (((attributes & ROLE_ATTR_BYPASSRLS) > 0) || bypassrls >= 0)
{
if (!superuser())
ereport(ERROR,
*************** AlterRole(AlterRoleStmt *stmt)
*** 743,785 ****
*/
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 (inherit >= 0)
{
! new_record[Anum_pg_authid_rolinherit - 1] = BoolGetDatum(inherit > 0);
! new_record_repl[Anum_pg_authid_rolinherit - 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 (createdb >= 0)
{
! new_record[Anum_pg_authid_rolcreatedb - 1] = BoolGetDatum(createdb > 0);
! new_record_repl[Anum_pg_authid_rolcreatedb - 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 (isreplication >= 0)
{
! new_record[Anum_pg_authid_rolreplication - 1] = BoolGetDatum(isreplication > 0);
! new_record_repl[Anum_pg_authid_rolreplication - 1] = true;
}
if (dconnlimit)
{
new_record[Anum_pg_authid_rolconnlimit - 1] = Int32GetDatum(connlimit);
--- 764,821 ----
*/
if (issuper >= 0)
{
! attributes = set_role_attribute(attributes,
! (issuper > 0) ? (ROLE_ATTR_SUPERUSER | ROLE_ATTR_CATUPDATE) :
! ~(ROLE_ATTR_SUPERUSER | ROLE_ATTR_CATUPDATE));
! new_record_repl[Anum_pg_authid_rolattr - 1] = true;
}
if (inherit >= 0)
{
! attributes = set_role_attribute(attributes,
! (inherit > 0) ? ROLE_ATTR_INHERIT : ~(ROLE_ATTR_INHERIT));
! new_record_repl[Anum_pg_authid_rolattr - 1] = true;
}
if (createrole >= 0)
{
! attributes = set_role_attribute(attributes,
! (createrole > 0) ? ROLE_ATTR_CREATEROLE : ~(ROLE_ATTR_CREATEROLE));
! new_record_repl[Anum_pg_authid_rolattr - 1] = true;
}
if (createdb >= 0)
{
! attributes = set_role_attribute(attributes,
! (createdb > 0) ? ROLE_ATTR_CREATEDB : ~(ROLE_ATTR_CREATEDB));
! new_record_repl[Anum_pg_authid_rolattr - 1] = true;
}
if (canlogin >= 0)
{
! attributes = set_role_attribute(attributes,
! (canlogin > 0) ? ROLE_ATTR_CANLOGIN : ~(ROLE_ATTR_CANLOGIN));
! new_record_repl[Anum_pg_authid_rolattr - 1] = true;
}
if (isreplication >= 0)
{
! attributes = set_role_attribute(attributes,
! (isreplication > 0) ? ROLE_ATTR_REPLICATION : ~(ROLE_ATTR_REPLICATION));
! new_record_repl[Anum_pg_authid_rolattr - 1] = true;
}
+ if (bypassrls >= 0)
+ {
+ attributes = set_role_attribute(attributes,
+ (bypassrls > 0) ? ROLE_ATTR_BYPASSRLS : ~(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);
*************** AlterRole(AlterRoleStmt *stmt)
*** 815,825 ****
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);
--- 851,856 ----
*************** AlterRoleSet(AlterRoleSetStmt *stmt)
*** 867,872 ****
--- 898,904 ----
HeapTuple roletuple;
Oid databaseid = InvalidOid;
Oid roleid = InvalidOid;
+ RoleAttr attributes;
if (stmt->role)
{
*************** AlterRoleSet(AlterRoleSetStmt *stmt)
*** 889,895 ****
* 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)
{
if (!superuser())
ereport(ERROR,
--- 921,928 ----
* To mess with a superuser you gotta be superuser; else you need
* createrole, or just want to change your own settings
*/
! attributes = ((Form_pg_authid) GETSTRUCT(roletuple))->rolattr;
! if ((attributes & ROLE_ATTR_SUPERUSER) > 0)
{
if (!superuser())
ereport(ERROR,
*************** DropRole(DropRoleStmt *stmt)
*** 973,978 ****
--- 1006,1012 ----
char *detail_log;
SysScanDesc sscan;
Oid roleid;
+ RoleAttr attributes;
tuple = SearchSysCache1(AUTHNAME, PointerGetDatum(role));
if (!HeapTupleIsValid(tuple))
*************** DropRole(DropRoleStmt *stmt)
*** 1013,1020 ****
* 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())
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("must be superuser to drop superusers")));
--- 1047,1054 ----
* roles but not superuser roles. This is mainly to avoid the
* scenario where you accidentally drop the last superuser.
*/
! attributes = ((Form_pg_authid) GETSTRUCT(tuple))->rolattr;
! if (((attributes & ROLE_ATTR_SUPERUSER) > 0) && !superuser())
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("must be superuser to drop superusers")));
*************** RenameRole(const char *oldname, const ch
*** 1128,1133 ****
--- 1162,1168 ----
bool repl_repl[Natts_pg_authid];
int i;
Oid roleid;
+ RoleAttr attributes;
rel = heap_open(AuthIdRelationId, RowExclusiveLock);
dsc = RelationGetDescr(rel);
*************** RenameRole(const char *oldname, const ch
*** 1173,1179 ****
/*
* createrole is enough privilege unless you want to mess with a superuser
*/
! if (((Form_pg_authid) GETSTRUCT(oldtuple))->rolsuper)
{
if (!superuser())
ereport(ERROR,
--- 1208,1215 ----
/*
* createrole is enough privilege unless you want to mess with a superuser
*/
! attributes = ((Form_pg_authid) GETSTRUCT(oldtuple))->rolattr;
! if ((attributes & ROLE_ATTR_SUPERUSER) > 0)
{
if (!superuser())
ereport(ERROR,
diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c
new file mode 100644
index 6ce8dae..a8a9f2e
*** a/src/backend/commands/variable.c
--- b/src/backend/commands/variable.c
*************** check_session_authorization(char **newva
*** 776,781 ****
--- 776,782 ----
Oid roleid;
bool is_superuser;
role_auth_extra *myextra;
+ RoleAttr attributes;
/* Do nothing for the boot_val default of NULL */
if (*newval == NULL)
*************** check_session_authorization(char **newva
*** 800,806 ****
}
roleid = HeapTupleGetOid(roleTup);
! is_superuser = ((Form_pg_authid) GETSTRUCT(roleTup))->rolsuper;
ReleaseSysCache(roleTup);
--- 801,808 ----
}
roleid = HeapTupleGetOid(roleTup);
! attributes = ((Form_pg_authid) GETSTRUCT(roleTup))->rolattr;
! is_superuser = ((attributes & ROLE_ATTR_SUPERUSER) > 0);
ReleaseSysCache(roleTup);
*************** check_role(char **newval, void **extra,
*** 844,849 ****
--- 846,852 ----
Oid roleid;
bool is_superuser;
role_auth_extra *myextra;
+ RoleAttr attributes;
if (strcmp(*newval, "none") == 0)
{
*************** check_role(char **newval, void **extra,
*** 872,878 ****
}
roleid = HeapTupleGetOid(roleTup);
! is_superuser = ((Form_pg_authid) GETSTRUCT(roleTup))->rolsuper;
ReleaseSysCache(roleTup);
--- 875,882 ----
}
roleid = HeapTupleGetOid(roleTup);
! attributes = ((Form_pg_authid) GETSTRUCT(roleTup))->rolattr;
! is_superuser = ((attributes & ROLE_ATTR_SUPERUSER) > 0);
ReleaseSysCache(roleTup);
diff --git a/src/backend/utils/adt/acl.c b/src/backend/utils/adt/acl.c
new file mode 100644
index dc6eb2c..f3108a5
*** a/src/backend/utils/adt/acl.c
--- b/src/backend/utils/adt/acl.c
*************** static Oid convert_type_name(text *typen
*** 115,120 ****
--- 115,121 ----
static AclMode convert_type_priv_string(text *priv_type_text);
static AclMode convert_role_priv_string(text *priv_type_text);
static AclResult pg_role_aclcheck(Oid role_oid, Oid roleid, AclMode mode);
+ static RoleAttr convert_role_attr_string(text *attr_type_text);
static void RoleMembershipCacheCallback(Datum arg, int cacheid, uint32 hashvalue);
*************** aclitemin(PG_FUNCTION_ARGS)
*** 577,582 ****
--- 578,584 ----
PG_RETURN_ACLITEM_P(aip);
}
+
/*
* aclitemout
* Allocates storage for, and fills in, a new null-delimited string
*************** pg_role_aclcheck(Oid role_oid, Oid rolei
*** 4602,4607 ****
--- 4604,4655 ----
return ACLCHECK_NO_PRIV;
}
+ /*
+ * has_role_attribute_id
+ * Check the named role attribute on a role by given role oid.
+ */
+ Datum
+ has_role_attribute_id(PG_FUNCTION_ARGS)
+ {
+ Oid roleoid = PG_GETARG_OID(0);
+ text *attr_type_text = PG_GETARG_TEXT_P(1);
+ RoleAttr attribute;
+
+ attribute = convert_role_attr_string(attr_type_text);
+
+ PG_RETURN_BOOL(role_has_attribute(roleoid, attribute));
+ }
+
+ /*
+ * convert_role_attr_string
+ * Convert text string to RoleAttr value.
+ */
+ static RoleAttr
+ convert_role_attr_string(text *attr_type_text)
+ {
+ char *attr_type = text_to_cstring(attr_type_text);
+
+ if (pg_strcasecmp(attr_type, "SUPERUSER") == 0)
+ return ROLE_ATTR_SUPERUSER;
+ else if (pg_strcasecmp(attr_type, "INHERIT") == 0)
+ return ROLE_ATTR_INHERIT;
+ else if (pg_strcasecmp(attr_type, "CREATEROLE") == 0)
+ return ROLE_ATTR_CREATEROLE;
+ else if (pg_strcasecmp(attr_type, "CREATEDB") == 0)
+ return ROLE_ATTR_CREATEDB;
+ else if (pg_strcasecmp(attr_type, "CATUPDATE") == 0)
+ return ROLE_ATTR_CATUPDATE;
+ else if (pg_strcasecmp(attr_type, "CANLOGIN") == 0)
+ return ROLE_ATTR_CANLOGIN;
+ else if (pg_strcasecmp(attr_type, "REPLICATION") == 0)
+ return ROLE_ATTR_REPLICATION;
+ else if (pg_strcasecmp(attr_type, "BYPASSRLS") == 0)
+ return ROLE_ATTR_BYPASSRLS;
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("unrecognized role attribute: \"%s\"", attr_type)));
+ }
/*
* initialization function (called by InitPostgres)
*************** RoleMembershipCacheCallback(Datum arg, i
*** 4638,4653 ****
static bool
has_rolinherit(Oid roleid)
{
! bool result = false;
HeapTuple utup;
utup = SearchSysCache1(AUTHOID, ObjectIdGetDatum(roleid));
if (HeapTupleIsValid(utup))
{
! result = ((Form_pg_authid) GETSTRUCT(utup))->rolinherit;
ReleaseSysCache(utup);
}
! return result;
}
--- 4686,4701 ----
static bool
has_rolinherit(Oid roleid)
{
! RoleAttr attributes;
HeapTuple utup;
utup = SearchSysCache1(AUTHOID, ObjectIdGetDatum(roleid));
if (HeapTupleIsValid(utup))
{
! attributes = ((Form_pg_authid) GETSTRUCT(utup))->rolattr;
ReleaseSysCache(utup);
}
! return ((attributes & ROLE_ATTR_INHERIT) > 0);
}
diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c
new file mode 100644
index 8fccb4c..51bf035
*** a/src/backend/utils/init/miscinit.c
--- b/src/backend/utils/init/miscinit.c
*************** SetUserIdAndContext(Oid userid, bool sec
*** 334,349 ****
bool
has_rolreplication(Oid roleid)
{
! bool result = false;
HeapTuple utup;
utup = SearchSysCache1(AUTHOID, ObjectIdGetDatum(roleid));
if (HeapTupleIsValid(utup))
{
! result = ((Form_pg_authid) GETSTRUCT(utup))->rolreplication;
ReleaseSysCache(utup);
}
! return result;
}
/*
--- 334,349 ----
bool
has_rolreplication(Oid roleid)
{
! RoleAttr attributes;
HeapTuple utup;
utup = SearchSysCache1(AUTHOID, ObjectIdGetDatum(roleid));
if (HeapTupleIsValid(utup))
{
! attributes = ((Form_pg_authid) GETSTRUCT(utup))->rolattr;
ReleaseSysCache(utup);
}
! return ((attributes & ROLE_ATTR_REPLICATION) > 0);
}
/*
*************** InitializeSessionUserId(const char *role
*** 375,381 ****
roleid = HeapTupleGetOid(roleTup);
AuthenticatedUserId = roleid;
! AuthenticatedUserIsSuperuser = rform->rolsuper;
/* This sets OuterUserId/CurrentUserId too */
SetSessionUserId(roleid, AuthenticatedUserIsSuperuser);
--- 375,381 ----
roleid = HeapTupleGetOid(roleTup);
AuthenticatedUserId = roleid;
! AuthenticatedUserIsSuperuser = ((rform->rolattr & ROLE_ATTR_SUPERUSER) > 0);
/* This sets OuterUserId/CurrentUserId too */
SetSessionUserId(roleid, AuthenticatedUserIsSuperuser);
*************** InitializeSessionUserId(const char *role
*** 394,400 ****
/*
* Is role allowed to login at all?
*/
! if (!rform->rolcanlogin)
ereport(FATAL,
(errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION),
errmsg("role \"%s\" is not permitted to log in",
--- 394,400 ----
/*
* Is role allowed to login at all?
*/
! if (!((rform->rolattr & ROLE_ATTR_CANLOGIN) > 0))
ereport(FATAL,
(errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION),
errmsg("role \"%s\" is not permitted to log in",
diff --git a/src/backend/utils/misc/superuser.c b/src/backend/utils/misc/superuser.c
new file mode 100644
index ff0f947..c1ea1c4
*** a/src/backend/utils/misc/superuser.c
--- b/src/backend/utils/misc/superuser.c
*************** superuser_arg(Oid roleid)
*** 58,63 ****
--- 58,64 ----
{
bool result;
HeapTuple rtup;
+ RoleAttr attributes;
/* Quick out for cache hit */
if (OidIsValid(last_roleid) && last_roleid == roleid)
*************** superuser_arg(Oid roleid)
*** 71,77 ****
rtup = SearchSysCache1(AUTHOID, ObjectIdGetDatum(roleid));
if (HeapTupleIsValid(rtup))
{
! result = ((Form_pg_authid) GETSTRUCT(rtup))->rolsuper;
ReleaseSysCache(rtup);
}
else
--- 72,79 ----
rtup = SearchSysCache1(AUTHOID, ObjectIdGetDatum(roleid));
if (HeapTupleIsValid(rtup))
{
! attributes = ((Form_pg_authid) GETSTRUCT(rtup))->rolattr;
! result = ((attributes & ROLE_ATTR_SUPERUSER) > 0);
ReleaseSysCache(rtup);
}
else
diff --git a/src/include/catalog/pg_authid.h b/src/include/catalog/pg_authid.h
new file mode 100644
index 3b63d2b..54c3098
*** a/src/include/catalog/pg_authid.h
--- b/src/include/catalog/pg_authid.h
***************
*** 22,27 ****
--- 22,28 ----
#define PG_AUTHID_H
#include "catalog/genbki.h"
+ #include "nodes/parsenodes.h"
/*
* The CATALOG definition has to refer to the type of rolvaliduntil as
***************
*** 45,60 ****
CATALOG(pg_authid,1260) BKI_SHARED_RELATION BKI_ROWTYPE_OID(2842) BKI_SCHEMA_MACRO
{
NameData rolname; /* name of role */
! bool rolsuper; /* read this field via superuser() only! */
! bool rolinherit; /* inherit privileges from other roles? */
! bool rolcreaterole; /* allowed to create more roles? */
! bool rolcreatedb; /* allowed to create databases? */
! bool rolcatupdate; /* allowed to alter catalogs manually? */
! bool rolcanlogin; /* allowed to log in as session user? */
! bool rolreplication; /* role used for streaming replication */
! bool rolbypassrls; /* allowed to bypass row level security? */
int32 rolconnlimit; /* max connections allowed (-1=no limit) */
-
/* remaining fields may be null; use heap_getattr to read them! */
text rolpassword; /* password, if any */
timestamptz rolvaliduntil; /* password expiration time, if any */
--- 46,53 ----
CATALOG(pg_authid,1260) BKI_SHARED_RELATION BKI_ROWTYPE_OID(2842) BKI_SCHEMA_MACRO
{
NameData rolname; /* name of role */
! int64 rolattr; /* role attribute bitmask */
int32 rolconnlimit; /* max connections allowed (-1=no limit) */
/* remaining fields may be null; use heap_getattr to read them! */
text rolpassword; /* password, if any */
timestamptz rolvaliduntil; /* password expiration time, if any */
*************** typedef FormData_pg_authid *Form_pg_auth
*** 74,92 ****
* compiler constants for pg_authid
* ----------------
*/
! #define Natts_pg_authid 12
#define Anum_pg_authid_rolname 1
! #define Anum_pg_authid_rolsuper 2
! #define Anum_pg_authid_rolinherit 3
! #define Anum_pg_authid_rolcreaterole 4
! #define Anum_pg_authid_rolcreatedb 5
! #define Anum_pg_authid_rolcatupdate 6
! #define Anum_pg_authid_rolcanlogin 7
! #define Anum_pg_authid_rolreplication 8
! #define Anum_pg_authid_rolbypassrls 9
! #define Anum_pg_authid_rolconnlimit 10
! #define Anum_pg_authid_rolpassword 11
! #define Anum_pg_authid_rolvaliduntil 12
/* ----------------
* initial contents of pg_authid
--- 67,78 ----
* compiler constants for pg_authid
* ----------------
*/
! #define Natts_pg_authid 5
#define Anum_pg_authid_rolname 1
! #define Anum_pg_authid_rolattr 2
! #define Anum_pg_authid_rolconnlimit 3
! #define Anum_pg_authid_rolpassword 4
! #define Anum_pg_authid_rolvaliduntil 5
/* ----------------
* initial contents of pg_authid
*************** typedef FormData_pg_authid *Form_pg_auth
*** 95,101 ****
* user choices.
* ----------------
*/
! DATA(insert OID = 10 ( "POSTGRES" t t t t t t t t -1 _null_ _null_));
#define BOOTSTRAP_SUPERUSERID 10
--- 81,87 ----
* user choices.
* ----------------
*/
! DATA(insert OID = 10 ( "POSTGRES" 255 -1 _null_ _null_));
#define BOOTSTRAP_SUPERUSERID 10
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
new file mode 100644
index 5d4e889..c818e51
*** a/src/include/catalog/pg_proc.h
--- b/src/include/catalog/pg_proc.h
*************** DESCR("current user privilege on any col
*** 2676,2681 ****
--- 2676,2684 ----
DATA(insert OID = 3029 ( has_any_column_privilege PGNSP PGUID 12 10 0 0 0 f f f f t f s 2 0 16 "26 25" _null_ _null_ _null_ _null_ has_any_column_privilege_id _null_ _null_ _null_ ));
DESCR("current user privilege on any column by rel oid");
+ DATA(insert OID = 6000 ( has_role_attribute PGNSP PGUID 12 10 0 0 0 f f f f t f s 2 0 16 "26 25" _null_ _null_ _null_ _null_ has_role_attribute_id _null_ _null_ _null_));
+ DESCR("user role attribute by user oid");
+
DATA(insert OID = 1928 ( pg_stat_get_numscans PGNSP PGUID 12 1 0 0 0 f f f f t f s 1 0 20 "26" _null_ _null_ _null_ _null_ pg_stat_get_numscans _null_ _null_ _null_ ));
DESCR("statistics: number of scans done for table/index");
DATA(insert OID = 1929 ( pg_stat_get_tuples_returned PGNSP PGUID 12 1 0 0 0 f f f f t f s 1 0 20 "26" _null_ _null_ _null_ _null_ pg_stat_get_tuples_returned _null_ _null_ _null_ ));
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
new file mode 100644
index 3e4f815..d560d69
*** a/src/include/nodes/parsenodes.h
--- b/src/include/nodes/parsenodes.h
*************** typedef uint32 AclMode; /* a bitmask o
*** 78,83 ****
--- 78,101 ----
/* Currently, SELECT ... FOR [KEY] UPDATE/SHARE requires UPDATE privileges */
#define ACL_SELECT_FOR_UPDATE ACL_UPDATE
+ /*
+ * Role attributes are encoded so that we can OR them together in a bitmask.
+ * The present representation of RoleAttr limits us to 64 distinct rights.
+ *
+ * Caution: changing these codes breaks stored RoleAttrs, hence forces initdb.
+ */
+ typedef uint64 RoleAttr; /* a bitmask for role attribute bits */
+
+ #define ROLE_ATTR_SUPERUSER (1<<0)
+ #define ROLE_ATTR_INHERIT (1<<1)
+ #define ROLE_ATTR_CREATEROLE (1<<2)
+ #define ROLE_ATTR_CREATEDB (1<<3)
+ #define ROLE_ATTR_CATUPDATE (1<<4)
+ #define ROLE_ATTR_CANLOGIN (1<<5)
+ #define ROLE_ATTR_REPLICATION (1<<6)
+ #define ROLE_ATTR_BYPASSRLS (1<<7)
+ #define N_ROLE_ATTRIBUTES 8
+ #define ROLE_ATTR_NONE 0
/*****************************************************************************
* Query Tree
diff --git a/src/include/utils/acl.h b/src/include/utils/acl.h
new file mode 100644
index a8e3164..aecad4f
*** a/src/include/utils/acl.h
--- b/src/include/utils/acl.h
*************** extern bool pg_event_trigger_ownercheck(
*** 328,332 ****
--- 328,333 ----
extern bool pg_extension_ownercheck(Oid ext_oid, Oid roleid);
extern bool has_createrole_privilege(Oid roleid);
extern bool has_bypassrls_privilege(Oid roleid);
+ extern bool role_has_attribute(Oid roleid, RoleAttr capability);
#endif /* ACL_H */
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
new file mode 100644
index 417fd17..31d7559
*** a/src/include/utils/builtins.h
--- b/src/include/utils/builtins.h
*************** extern Datum pg_has_role_id_name(PG_FUNC
*** 106,111 ****
--- 106,112 ----
extern Datum pg_has_role_id_id(PG_FUNCTION_ARGS);
extern Datum pg_has_role_name(PG_FUNCTION_ARGS);
extern Datum pg_has_role_id(PG_FUNCTION_ARGS);
+ extern Datum has_role_attribute_id(PG_FUNCTION_ARGS);
/* bool.c */
extern Datum boolin(PG_FUNCTION_ARGS);
--
Sent via pgsql-hackers mailing list ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers