All, I am simply breaking this out into its own thread from the discussion on additional role attributes ( http://www.postgresql.org/message-id/20141015052259.gg28...@tamriel.snowman.net ).
A few related threads/discussions/posts: * http://www.postgresql.org/message-id/20141016115914.gq28...@tamriel.snowman.net * http://www.postgresql.org/message-id/CA+TgmobkYXNOWKEKzX2qGPSr_nvacFGueV=orxnd-xmzvov...@mail.gmail.com * http://www.postgresql.org/message-id/20141016115914.gq28...@tamriel.snowman.net 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 - adam.brightw...@crunchydatasolutions.com 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 (pgsql-hackers@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers