diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index 9ceb96b54c7..947091627fd 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -1391,56 +1391,11 @@
- rolsuper
- bool
- Role has superuser privileges
-
-
-
- rolinherit
- bool
- Role automatically inherits privileges of roles it is a
- member of
-
-
-
- rolcreaterole
- bool
- Role can create more roles
-
-
-
- rolcreatedb
- bool
- Role can create databases
-
-
-
- rolcatupdate
- bool
+ rolattr
+ bigint
- Role can update system catalogs directly. (Even a superuser cannot do
- this unless this column is true)
-
-
-
-
- rolcanlogin
- bool
-
- Role can log in. That is, this role can be given as the initial
- session authorization identifier
-
-
-
-
- rolreplication
- bool
-
- Role is a replication role. That is, this role can initiate streaming
- replication (see ) and set/unset
- the system backup mode using pg_start_backup> and
- pg_stop_backup>
+ Role attributes; see and
+ for details
@@ -1478,6 +1433,96 @@
+
+ Attributes in rolattr>
+
+
+
+
+ Attribute
+ CREATE ROLE Option
+ Description
+ Position
+
+
+
+
+
+ Superuser
+ SUPERUSER
+ Role has superuser privileges
+ 0
+
+
+
+ Inherit
+ INHERIT
+
+ Role automatically inherits privileges of roles it is a member of
+
+ 1
+
+
+
+ Create Role
+ CREATEROLE
+ Role can create more roles
+ 2
+
+
+
+ Create DB
+ CREATEDB
+ Role can create databases
+ 3
+
+
+
+ Catalog Update
+ CATUPDATE
+
+ Role can update system catalogs directly. (Even a superuser cannot do
+ this unless this column is true)
+
+ 4
+
+
+
+ Can Login
+ LOGIN
+
+ Role can log in. That is, this role can be given as the initial
+ session authorization identifier
+
+ 5
+
+
+
+ Replication
+ REPLICATION
+
+ Role is a replication role. That is, this role can initiate streaming
+ replication (see ) and set/unset
+ the system backup mode using pg_start_backup> and
+ pg_stop_backup>
+
+ 6
+
+
+
+ Bypass Row Level Security
+ BYPASSRLS
+
+ Role can bypass row level security policies when row_security>
+ is set off>
+
+ 7
+
+
+
+
+
+
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 24c64b7187f..2a37e65eb9a 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -15139,6 +15139,133 @@ SELECT has_function_privilege('joeuser', 'myfunc(int, text)', 'execute');
are immediately available without doing SET ROLE>.
+
+ lists functions that
+ allow the user to query role attribute information programmatically.
+
+
+
+ Role Attribute Inquiry Functions
+
+
+ Name Return Type Description
+
+
+
+ pg_has_role_attribute(role, attribute)
+ boolean
+ does role have the permissions allowed by named attribute
+
+
+ pg_check_role_attribute(role, attribute)
+ boolean
+ does role have the named attribute
+
+
+ pg_check_role_attribute(role_attributes, attribute)
+ boolean
+ is attribute set in bitmap of role attributes
+
+
+ pg_all_role_attributes(role_attributes)
+ text[]
+ convert bitmap of role attribute representation to text[]
+
+
+
+
+
+
+ pg_has_role_attribute
+
+
+ pg_check_role_attribute
+
+
+ pg_all_role_attributes
+
+
+
+ pg_has_role_attribute checks the attribute permissions
+ given to a role. It will always return true for roles
+ with superuser privileges unless the attribute being checked is
+ CATUPDATE (superuser cannot bypass
+ CATUPDATE permissions). The role can be specified by name
+ and by OID. The attribute is specified by a text string which must evaluate
+ to one of the following role attributes:
+ SUPERUSER,
+ INHERIT,
+ CREATEROLE,
+ CREATEDB,
+ CATUPDATE,
+ CANLOGIN,
+ REPLICATION, or
+ BYPASSRLS. See for more
+ information. For example:
+
+SELECT pg_has_role_attribute('joe', 'SUPERUSER');
+ pg_has_role_attribute
+-----------------------
+ f
+(1 row)
+
+SELECT rolname, pg_has_role_attribute(oid, 'INHERIT') AS rolinherit FROM pg_roles;
+ rolname | rolinherit
+----------+------------
+ postgres | t
+ joe | t
+(2 rows)
+
+
+
+
+ pg_check_role_attribute checks the attribute value given
+ to a role. The role can be specified by name and by OID. The attribute is
+ specified by a text string which must evaluate to a valid role attribute (see
+ pg_has_role_attribute). A third variant of this function
+ allows for a bitmap representation (bigint) of attributes
+ to be given instead of a role.
+ Example:
+
+SELECT pg_check_role_attribute('joe', 'SUPERUSER');
+ pg_check_role_attribute
+-------------------------
+ f
+(1 row)
+
+SELECT rolname, pg_check_role_attribute(oid, 'INHERIT') as rolinherit FROM pg_roles;
+ rolname | rolinherit
+----------+------------
+ postgres | t
+ joe | t
+(2 rows)
+ t
+(1 row)
+
+
+SELECT rolname, pg_check_role_attribute(rolattr, 'SUPERUSER') AS rolsuper FROM pg_authid;
+ rolname | rolsuper
+----------+----------
+ postgres | t
+ joe | f
+(2 rows)
+
+
+
+
+ pg_all_role_attributes convert a set of role attributes
+ represented by an bigint bitmap to a text array.
+ Example:
+
+SELECT rolname, pg_all_role_attributes(rolattr) AS attributes FROM pg_authid;
+ rolname | attributes
+----------+-----------------------------------------------------------------------------------------------
+ postgres | {Superuser,Inherit,"Create Role","Create DB","Catalog Update",Login,Replication,"Bypass RLS"}
+ joe | {Inherit,Login}
+(2 rows)
+
+
+
shows functions that
determine whether a certain object is visible> in the
diff --git a/src/backend/access/transam/xlogfuncs.c b/src/backend/access/transam/xlogfuncs.c
index 133143db929..3181a79e3ed 100644
--- a/src/backend/access/transam/xlogfuncs.c
+++ b/src/backend/access/transam/xlogfuncs.c
@@ -27,6 +27,7 @@
#include "miscadmin.h"
#include "replication/walreceiver.h"
#include "storage/smgr.h"
+#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/numeric.h"
#include "utils/guc.h"
@@ -54,7 +55,7 @@ pg_start_backup(PG_FUNCTION_ARGS)
backupidstr = text_to_cstring(backupid);
- if (!superuser() && !has_rolreplication(GetUserId()))
+ if (!have_role_attribute(ROLE_ATTR_REPLICATION))
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("must be superuser or replication role to run a backup")));
@@ -82,7 +83,7 @@ pg_stop_backup(PG_FUNCTION_ARGS)
{
XLogRecPtr stoppoint;
- if (!superuser() && !has_rolreplication(GetUserId()))
+ if (!have_role_attribute(ROLE_ATTR_REPLICATION))
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
(errmsg("must be superuser or replication role to run a backup"))));
diff --git a/src/backend/catalog/Catalog.pm b/src/backend/catalog/Catalog.pm
index 523b37995db..fd52f48f344 100644
--- a/src/backend/catalog/Catalog.pm
+++ b/src/backend/catalog/Catalog.pm
@@ -176,7 +176,7 @@ sub Catalogs
}
}
}
- $catalogs{$catname} = \%catalog;
+ $catalogs{$catname} = \%catalog if defined $catname;
close INPUT_FILE;
}
return \%catalogs;
diff --git a/src/backend/catalog/Makefile b/src/backend/catalog/Makefile
index a403c643600..a6de2ff054f 100644
--- a/src/backend/catalog/Makefile
+++ b/src/backend/catalog/Makefile
@@ -28,7 +28,7 @@ all: $(BKIFILES) schemapg.h
# indexing.h had better be last, and toasting.h just before it.
POSTGRES_BKI_SRCS = $(addprefix $(top_srcdir)/src/include/catalog/,\
- pg_proc.h pg_type.h pg_attribute.h pg_class.h \
+ acldefs.h pg_proc.h pg_type.h pg_attribute.h pg_class.h \
pg_attrdef.h pg_constraint.h pg_inherits.h pg_index.h pg_operator.h \
pg_opfamily.h pg_opclass.h pg_am.h pg_amop.h pg_amproc.h \
pg_language.h pg_largeobject_metadata.h pg_largeobject.h pg_aggregate.h \
diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c
index d30612c4d9a..cd607393528 100644
--- a/src/backend/catalog/aclchk.c
+++ b/src/backend/catalog/aclchk.c
@@ -3423,26 +3423,6 @@ aclcheck_error_type(AclResult aclerr, Oid typeOid)
}
-/* 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
*/
@@ -3630,7 +3610,7 @@ pg_class_aclmask(Oid table_oid, Oid roleid,
if ((mask & (ACL_INSERT | ACL_UPDATE | ACL_DELETE | ACL_TRUNCATE | ACL_USAGE)) &&
IsSystemClass(table_oid, classForm) &&
classForm->relkind != RELKIND_VIEW &&
- !has_rolcatupdate(roleid) &&
+ !has_role_attribute(roleid, ROLE_ATTR_CATUPDATE) &&
!allowSystemTableMods)
{
#ifdef ACLDEBUG
@@ -5051,52 +5031,87 @@ pg_extension_ownercheck(Oid ext_oid, Oid roleid)
}
/*
- * Check whether specified role has CREATEROLE privilege (or is a superuser)
+ * has_role_attribute
+ * Check if the role with the specified id has been assigned a specific role
+ * attribute.
*
- * Note: roles do not have owners per se; instead we use this test in
- * places where an ownership-like permissions test is needed for a role.
- * Be sure to apply it to the role trying to do the operation, not the
- * role being operated on! Also note that this generally should not be
- * considered enough privilege if the target role is a superuser.
- * (We don't handle that consideration here because we want to give a
- * separate error message for such cases, so the caller has to deal with it.)
+ * roleid - the oid of the role to check.
+ * attribute - the attribute to check.
+ *
+ * Note: Use this function for role attribute permission checking as it
+ * accounts for superuser status. It will always return true for roles with
+ * superuser privileges unless the attribute being checked is CATUPDATE
+ * (superusers are not allowed to bypass CATUPDATE permissions).
+ *
+ * Note: roles do not have owners per se; instead we use this test in places
+ * where an ownership-like permissions test is needed for a role. Be sure to
+ * apply it to the role trying to do the operation, not the role being operated
+ * on! Also note that this generally should not be considered enough privilege
+ * if the target role is a superuser. (We don't handle that consideration here
+ * because we want to give a separate error message for such cases, so the
+ * caller has to deal with it.)
*/
bool
-has_createrole_privilege(Oid roleid)
+has_role_attribute(Oid roleid, RoleAttr attribute)
{
- bool result = false;
- HeapTuple utup;
-
- /* Superusers bypass all permission checking. */
- if (superuser_arg(roleid))
+ /*
+ * Superusers bypass all permission checking except in the case of CATUPDATE
+ */
+ if (!(attribute & ROLE_ATTR_CATUPDATE) && superuser_arg(roleid))
return true;
- utup = SearchSysCache1(AUTHOID, ObjectIdGetDatum(roleid));
- if (HeapTupleIsValid(utup))
- {
- result = ((Form_pg_authid) GETSTRUCT(utup))->rolcreaterole;
- ReleaseSysCache(utup);
- }
- return result;
+ return check_role_attribute(roleid, attribute);
}
+/*
+ * have_role_attribute
+ * Convenience function for checking if the role id returned by GetUserId()
+ * has been assigned a specific role attribute.
+ *
+ * attribute - the attribute to check.
+ */
bool
-has_bypassrls_privilege(Oid roleid)
+have_role_attribute(RoleAttr attribute)
{
- bool result = false;
- HeapTuple utup;
+ return has_role_attribute(GetUserId(), attribute);
+}
- /* Superusers bypass all permission checking. */
- if (superuser_arg(roleid))
- return true;
+/*
+ * check_role_attribute
+ * Check if the role with the specified id has been assigned a specific role
+ * attribute.
+ *
+ * roleid - the oid of the role to check.
+ * attribute - the attribute to check.
+ *
+ * Note: This function should only be used for checking the value of an
+ * individual attribute in the rolattr bitmap and should *not* be used for
+ * permission checking. For the purposes of permission checking use
+ * 'has_role_attribute' instead.
+ */
+bool
+check_role_attribute(Oid roleid, RoleAttr attribute)
+{
+ RoleAttr attributes;
+ HeapTuple tuple;
- utup = SearchSysCache1(AUTHOID, ObjectIdGetDatum(roleid));
- if (HeapTupleIsValid(utup))
- {
- result = ((Form_pg_authid) GETSTRUCT(utup))->rolbypassrls;
- ReleaseSysCache(utup);
- }
- return result;
+ /* ROLE_ATTR_NONE (zero) is not a valid attribute */
+ Assert(attribute != ROLE_ATTR_NONE);
+
+ /* Check that only one bit is set in 'attribute' */
+ Assert(!(attribute & (attribute - 1)));
+
+ 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);
}
/*
diff --git a/src/backend/catalog/genbki.pl b/src/backend/catalog/genbki.pl
index ca8987907c6..415ac17a7bf 100644
--- a/src/backend/catalog/genbki.pl
+++ b/src/backend/catalog/genbki.pl
@@ -90,6 +90,8 @@ my $BOOTSTRAP_SUPERUSERID =
find_defined_symbol('pg_authid.h', 'BOOTSTRAP_SUPERUSERID');
my $PG_CATALOG_NAMESPACE =
find_defined_symbol('pg_namespace.h', 'PG_CATALOG_NAMESPACE');
+my $ROLE_ATTR_ALL =
+ find_defined_symbol('acldefs.h', 'ROLE_ATTR_ALL');
# Read all the input header files into internal data structures
my $catalogs = Catalog::Catalogs(@input_files);
@@ -144,6 +146,7 @@ foreach my $catname (@{ $catalogs->{names} })
# substitute constant values we acquired above
$row->{bki_values} =~ s/\bPGUID\b/$BOOTSTRAP_SUPERUSERID/g;
$row->{bki_values} =~ s/\bPGNSP\b/$PG_CATALOG_NAMESPACE/g;
+ $row->{bki_values} =~ s/\bPGROLATTRALL/$ROLE_ATTR_ALL/g;
# Save pg_type info for pg_attribute processing below
if ($catname eq 'pg_type')
diff --git a/src/backend/catalog/information_schema.sql b/src/backend/catalog/information_schema.sql
index a036c62a335..87b6d8ce9ce 100644
--- a/src/backend/catalog/information_schema.sql
+++ b/src/backend/catalog/information_schema.sql
@@ -2884,7 +2884,12 @@ CREATE VIEW user_mapping_options AS
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
+ OR (
+ SELECT pg_check_role_attribute(pg_authid.rolattr, '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/objectaddress.c b/src/backend/catalog/objectaddress.c
index 297deb5f3f0..3a4245d5dcc 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -1328,7 +1328,7 @@ check_object_ownership(Oid roleid, ObjectType objtype, ObjectAddress address,
}
else
{
- if (!has_createrole_privilege(roleid))
+ if (!has_role_attribute(roleid, ROLE_ATTR_CREATEROLE))
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("must have CREATEROLE privilege")));
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index 22b8ceef622..ae93832215b 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -9,17 +9,17 @@
CREATE VIEW pg_roles AS
SELECT
rolname,
- rolsuper,
- rolinherit,
- rolcreaterole,
- rolcreatedb,
- rolcatupdate,
- rolcanlogin,
- rolreplication,
+ pg_check_role_attribute(pg_authid.rolattr, 'SUPERUSER') AS rolsuper,
+ pg_check_role_attribute(pg_authid.rolattr, 'INHERIT') AS rolinherit,
+ pg_check_role_attribute(pg_authid.rolattr, 'CREATEROLE') AS rolcreaterole,
+ pg_check_role_attribute(pg_authid.rolattr, 'CREATEDB') AS rolcreatedb,
+ pg_check_role_attribute(pg_authid.rolattr, 'CATUPDATE') AS rolcatupdate,
+ pg_check_role_attribute(pg_authid.rolattr, 'CANLOGIN') AS rolcanlogin,
+ pg_check_role_attribute(pg_authid.rolattr, 'REPLICATION') AS rolreplication,
+ pg_check_role_attribute(pg_authid.rolattr, 'BYPASSRLS') AS rolbypassrls,
rolconnlimit,
'********'::text as rolpassword,
rolvaliduntil,
- rolbypassrls,
setconfig as rolconfig,
pg_authid.oid
FROM pg_authid LEFT JOIN pg_db_role_setting s
@@ -29,16 +29,16 @@ CREATE VIEW pg_shadow AS
SELECT
rolname AS usename,
pg_authid.oid AS usesysid,
- rolcreatedb AS usecreatedb,
- rolsuper AS usesuper,
- rolcatupdate AS usecatupd,
- rolreplication AS userepl,
+ pg_check_role_attribute(pg_authid.rolattr, 'CREATEDB') AS usecreatedb,
+ pg_check_role_attribute(pg_authid.rolattr, 'SUPERUSER') AS usesuper,
+ pg_check_role_attribute(pg_authid.rolattr, 'CATUPDATE') AS usecatupd,
+ pg_check_role_attribute(pg_authid.rolattr, '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 rolcanlogin;
+ WHERE pg_check_role_attribute(pg_authid.rolattr, 'CANLOGIN');
REVOKE ALL on pg_shadow FROM public;
@@ -48,7 +48,7 @@ CREATE VIEW pg_group AS
oid AS grosysid,
ARRAY(SELECT member FROM pg_auth_members WHERE roleid = oid) AS grolist
FROM pg_authid
- WHERE NOT rolcanlogin;
+ WHERE NOT pg_check_role_attribute(pg_authid.rolattr, 'CANLOGIN');
CREATE VIEW pg_user AS
SELECT
diff --git a/src/backend/commands/dbcommands.c b/src/backend/commands/dbcommands.c
index 1a5244cade2..c079168c83d 100644
--- a/src/backend/commands/dbcommands.c
+++ b/src/backend/commands/dbcommands.c
@@ -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
*
diff --git a/src/backend/commands/user.c b/src/backend/commands/user.c
index 1a73fd85582..564f77a8695 100644
--- a/src/backend/commands/user.c
+++ b/src/backend/commands/user.c
@@ -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),
diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c
index 6ce8daeb95a..491dc38caf3 100644
--- a/src/backend/commands/variable.c
+++ b/src/backend/commands/variable.c
@@ -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);
diff --git a/src/backend/replication/logical/logicalfuncs.c b/src/backend/replication/logical/logicalfuncs.c
index 1977f098c79..1a38f56a7cc 100644
--- a/src/backend/replication/logical/logicalfuncs.c
+++ b/src/backend/replication/logical/logicalfuncs.c
@@ -17,18 +17,14 @@
#include
+#include "access/xlog_internal.h"
+#include "catalog/pg_type.h"
#include "fmgr.h"
#include "funcapi.h"
-#include "miscadmin.h"
-
-#include "access/xlog_internal.h"
-
-#include "catalog/pg_type.h"
-
-#include "nodes/makefuncs.h"
-
#include "mb/pg_wchar.h"
-
+#include "miscadmin.h"
+#include "nodes/makefuncs.h"
+#include "utils/acl.h"
#include "utils/array.h"
#include "utils/builtins.h"
#include "utils/inval.h"
@@ -36,11 +32,9 @@
#include "utils/pg_lsn.h"
#include "utils/resowner.h"
#include "utils/lsyscache.h"
-
#include "replication/decode.h"
#include "replication/logical.h"
#include "replication/logicalfuncs.h"
-
#include "storage/fd.h"
/* private date for writing out data */
@@ -205,7 +199,7 @@ XLogRead(char *buf, TimeLineID tli, XLogRecPtr startptr, Size count)
static void
check_permissions(void)
{
- if (!superuser() && !has_rolreplication(GetUserId()))
+ if (!have_role_attribute(ROLE_ATTR_REPLICATION))
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
(errmsg("must be superuser or replication role to use replication slots"))));
diff --git a/src/backend/replication/slotfuncs.c b/src/backend/replication/slotfuncs.c
index bd4701f97df..c113a0bbaa7 100644
--- a/src/backend/replication/slotfuncs.c
+++ b/src/backend/replication/slotfuncs.c
@@ -20,13 +20,14 @@
#include "replication/slot.h"
#include "replication/logical.h"
#include "replication/logicalfuncs.h"
+#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/pg_lsn.h"
static void
check_permissions(void)
{
- if (!superuser() && !has_rolreplication(GetUserId()))
+ if (!have_role_attribute(ROLE_ATTR_REPLICATION))
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
(errmsg("must be superuser or replication role to use replication slots"))));
diff --git a/src/backend/rewrite/rowsecurity.c b/src/backend/rewrite/rowsecurity.c
index 6c232dcf9ae..f41ad347444 100644
--- a/src/backend/rewrite/rowsecurity.c
+++ b/src/backend/rewrite/rowsecurity.c
@@ -521,7 +521,7 @@ check_enable_rls(Oid relid, Oid checkAsUser)
*/
if (!checkAsUser && row_security == ROW_SECURITY_OFF)
{
- if (has_bypassrls_privilege(user_id))
+ if (has_role_attribute(user_id, ROLE_ATTR_BYPASSRLS))
/* OK to bypass */
return RLS_NONE_ENV;
else
diff --git a/src/backend/utils/adt/acl.c b/src/backend/utils/adt/acl.c
index dc6eb2c8aac..4c03955799a 100644
--- a/src/backend/utils/adt/acl.c
+++ b/src/backend/utils/adt/acl.c
@@ -115,6 +115,7 @@ static Oid convert_type_name(text *typename);
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);
@@ -4602,6 +4603,186 @@ pg_role_aclcheck(Oid role_oid, Oid roleid, AclMode mode)
return ACLCHECK_NO_PRIV;
}
+/*
+ * pg_has_role_attribute_id
+ * Check that the role with the given oid has the given named role
+ * attribute.
+ *
+ * Note: This function applies superuser checks. Therefore, if the provided
+ * role is a superuser, then the result will always be true.
+ */
+Datum
+pg_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(has_role_attribute(roleoid, attribute));
+}
+
+/*
+ * pg_has_role_attribute_name
+ * Check that the named role has the given named role attribute.
+ *
+ * Note: This function applies superuser checks. Therefore, if the provided
+ * role is a superuser, then the result will always be true.
+ */
+Datum
+pg_has_role_attribute_name(PG_FUNCTION_ARGS)
+{
+ Name rolename = PG_GETARG_NAME(0);
+ text *attr_type_text = PG_GETARG_TEXT_P(1);
+ Oid roleoid;
+ RoleAttr attribute;
+
+ roleoid = get_role_oid(NameStr(*rolename), false);
+ attribute = convert_role_attr_string(attr_type_text);
+
+ PG_RETURN_BOOL(has_role_attribute(roleoid, attribute));
+}
+
+/*
+ * pg_check_role_attribute_id
+ * Check that the role with the given oid has the given named role
+ * attribute.
+ *
+ * Note: This function is different from 'pg_has_role_attribute_id_attr' in that
+ * it does *not* apply any superuser checks. Therefore, this function will
+ * always return the set value of the attribute, despite the superuser-ness of
+ * the provided role.
+ */
+Datum
+pg_check_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(check_role_attribute(roleoid, attribute));
+}
+
+/*
+ * pg_check_role_attribute_name
+ * Check that the named role has the given named role attribute.
+ *
+ * Note: This function is different from 'pg_has_role_attribute_name_attr' in
+ * that it does *not* apply any superuser checks. Therefore, this function will
+ * always return the set value of the attribute, despite the superuser-ness of
+ * the provided role.
+ */
+Datum
+pg_check_role_attribute_name(PG_FUNCTION_ARGS)
+{
+ Name rolename = PG_GETARG_NAME(0);
+ text *attr_type_text = PG_GETARG_TEXT_P(1);
+ Oid roleoid;
+ RoleAttr attribute;
+
+ roleoid = get_role_oid(NameStr(*rolename), false);
+ attribute = convert_role_attr_string(attr_type_text);
+
+ PG_RETURN_BOOL(check_role_attribute(roleoid, attribute));
+}
+
+/*
+ * pg_check_role_attribute_attrs
+ * Check that the named attribute is enabled in the given RoleAttr
+ * representation of role attributes.
+ */
+Datum
+pg_check_role_attribute_attrs(PG_FUNCTION_ARGS)
+{
+ RoleAttr attributes = PG_GETARG_INT64(0);
+ text *attr_type_text = PG_GETARG_TEXT_P(1);
+ RoleAttr attribute;
+
+ attribute = convert_role_attr_string(attr_type_text);
+
+ PG_RETURN_BOOL(attributes & attribute);
+}
+
+/*
+ * pg_all_role_attributes
+ * Convert a RoleAttr representation of role attributes into an array of
+ * corresponding text values.
+ *
+ * The first and only argument is a RoleAttr (int64) representation of the
+ * role attributes.
+ */
+Datum
+pg_all_role_attributes(PG_FUNCTION_ARGS)
+{
+ RoleAttr attributes = PG_GETARG_INT64(0);
+ Datum *temp_array;
+ ArrayType *result;
+ int i = 0;
+
+ /*
+ * Short-circuit the case for no attributes assigned.
+ */
+ if (attributes == ROLE_ATTR_NONE)
+ PG_RETURN_ARRAYTYPE_P(construct_empty_array(TEXTOID));
+
+ temp_array = (Datum *) palloc(N_ROLE_ATTRIBUTES * sizeof(Datum));
+
+ /* Determine which attributes are assigned. */
+ if (attributes & ROLE_ATTR_SUPERUSER)
+ temp_array[i++] = CStringGetTextDatum(_("Superuser"));
+ if (attributes & ROLE_ATTR_INHERIT)
+ temp_array[i++] = CStringGetTextDatum(_("Inherit"));
+ if (attributes & ROLE_ATTR_CREATEROLE)
+ temp_array[i++] = CStringGetTextDatum(_("Create Role"));
+ if (attributes & ROLE_ATTR_CREATEDB)
+ temp_array[i++] = CStringGetTextDatum(_("Create DB"));
+ if (attributes & ROLE_ATTR_CATUPDATE)
+ temp_array[i++] = CStringGetTextDatum(_("Catalog Update"));
+ if (attributes & ROLE_ATTR_CANLOGIN)
+ temp_array[i++] = CStringGetTextDatum(_("Login"));
+ if (attributes & ROLE_ATTR_REPLICATION)
+ temp_array[i++] = CStringGetTextDatum(_("Replication"));
+ if (attributes & ROLE_ATTR_BYPASSRLS)
+ temp_array[i++] = CStringGetTextDatum(_("Bypass RLS"));
+
+ result = construct_array(temp_array, i, TEXTOID, -1, false, 'i');
+
+ PG_RETURN_ARRAYTYPE_P(result);
+}
+
+/*
+ * 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)
@@ -4634,23 +4815,6 @@ RoleMembershipCacheCallback(Datum arg, int cacheid, uint32 hashvalue)
}
-/* Check if specified role has rolinherit set */
-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;
-}
-
-
/*
* Get a list of roles that the specified roleid has the privileges of
*
@@ -4697,7 +4861,7 @@ roles_has_privs_of(Oid roleid)
int i;
/* Ignore non-inheriting roles */
- if (!has_rolinherit(memberid))
+ if (!has_role_attribute(memberid, ROLE_ATTR_INHERIT))
continue;
/* Find roles that memberid is directly a member of */
diff --git a/src/backend/utils/adt/ri_triggers.c b/src/backend/utils/adt/ri_triggers.c
index 5c75390ce49..ccb10667326 100644
--- a/src/backend/utils/adt/ri_triggers.c
+++ b/src/backend/utils/adt/ri_triggers.c
@@ -2308,7 +2308,7 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
* bypassrls right or is the table owner of the table(s) involved which
* have RLS enabled.
*/
- if (!has_bypassrls_privilege(GetUserId()) &&
+ if (!have_role_attribute(ROLE_ATTR_BYPASSRLS) &&
((pk_rel->rd_rel->relrowsecurity &&
!pg_class_ownercheck(pkrte->relid, GetUserId())) ||
(fk_rel->rd_rel->relrowsecurity &&
diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c
index 8fccb4c8262..db2a0fb48ef 100644
--- a/src/backend/utils/init/miscinit.c
+++ b/src/backend/utils/init/miscinit.c
@@ -40,6 +40,7 @@
#include "storage/pg_shmem.h"
#include "storage/proc.h"
#include "storage/procarray.h"
+#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/guc.h"
#include "utils/memutils.h"
@@ -328,24 +329,6 @@ SetUserIdAndContext(Oid userid, bool sec_def_context)
}
-/*
- * Check whether specified role has explicit REPLICATION privilege
- */
-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;
-}
-
/*
* Initialize user identity during normal backend startup
*/
@@ -375,7 +358,7 @@ InitializeSessionUserId(const char *rolename)
roleid = HeapTupleGetOid(roleTup);
AuthenticatedUserId = roleid;
- AuthenticatedUserIsSuperuser = rform->rolsuper;
+ AuthenticatedUserIsSuperuser = (rform->rolattr & ROLE_ATTR_SUPERUSER);
/* This sets OuterUserId/CurrentUserId too */
SetSessionUserId(roleid, AuthenticatedUserIsSuperuser);
@@ -394,7 +377,7 @@ InitializeSessionUserId(const char *rolename)
/*
* Is role allowed to login at all?
*/
- if (!rform->rolcanlogin)
+ if (!(rform->rolattr & ROLE_ATTR_CANLOGIN))
ereport(FATAL,
(errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION),
errmsg("role \"%s\" is not permitted to log in",
diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c
index c34803437b6..268001f58af 100644
--- a/src/backend/utils/init/postinit.c
+++ b/src/backend/utils/init/postinit.c
@@ -762,7 +762,7 @@ InitPostgres(const char *in_dbname, Oid dboid, const char *username,
{
Assert(!bootstrap);
- if (!superuser() && !has_rolreplication(GetUserId()))
+ if (!have_role_attribute(ROLE_ATTR_REPLICATION))
ereport(FATAL,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("must be superuser or replication role to start walsender")));
diff --git a/src/backend/utils/misc/superuser.c b/src/backend/utils/misc/superuser.c
index ff0f94711d7..67d070ca256 100644
--- a/src/backend/utils/misc/superuser.c
+++ b/src/backend/utils/misc/superuser.c
@@ -58,6 +58,7 @@ superuser_arg(Oid roleid)
{
bool result;
HeapTuple rtup;
+ RoleAttr attributes;
/* Quick out for cache hit */
if (OidIsValid(last_roleid) && last_roleid == roleid)
@@ -71,7 +72,8 @@ superuser_arg(Oid roleid)
rtup = SearchSysCache1(AUTHOID, ObjectIdGetDatum(roleid));
if (HeapTupleIsValid(rtup))
{
- result = ((Form_pg_authid) GETSTRUCT(rtup))->rolsuper;
+ attributes = ((Form_pg_authid) GETSTRUCT(rtup))->rolattr;
+ result = (attributes & ROLE_ATTR_SUPERUSER);
ReleaseSysCache(rtup);
}
else
diff --git a/src/bin/pg_dump/pg_dumpall.c b/src/bin/pg_dump/pg_dumpall.c
index eb633bc1368..f638167d8fa 100644
--- a/src/bin/pg_dump/pg_dumpall.c
+++ b/src/bin/pg_dump/pg_dumpall.c
@@ -671,10 +671,16 @@ dumpRoles(PGconn *conn)
/* note: rolconfig is dumped later */
if (server_version >= 90500)
printfPQExpBuffer(buf,
- "SELECT oid, rolname, rolsuper, rolinherit, "
- "rolcreaterole, rolcreatedb, "
- "rolcanlogin, rolconnlimit, rolpassword, "
- "rolvaliduntil, rolreplication, rolbypassrls, "
+ "SELECT oid, rolname, "
+ "pg_check_role_attribute(oid, 'SUPERUSER') AS rolsuper, "
+ "pg_check_role_attribute(oid, 'INHERIT') AS rolinherit, "
+ "pg_check_role_attribute(oid, 'CREATEROLE') AS rolcreaterole, "
+ "pg_check_role_attribute(oid, 'CREATEDB') AS rolcreatedb, "
+ "pg_check_role_attribute(oid, 'CANLOGIN') AS rolcanlogin, "
+ "pg_check_role_attribute(oid, 'REPLICATION') AS rolreplication, "
+ "pg_check_role_attribute(oid, 'BYPASSRLS') AS rolbypassrls, "
+ "rolconnlimit, rolpassword, "
+ "rolvaliduntil, "
"pg_catalog.shobj_description(oid, 'pg_authid') as rolcomment, "
"rolname = current_user AS is_current_user "
"FROM pg_authid "
diff --git a/src/include/catalog/acldefs.h b/src/include/catalog/acldefs.h
new file mode 100644
index 00000000000..2dcc17495d3
--- /dev/null
+++ b/src/include/catalog/acldefs.h
@@ -0,0 +1,72 @@
+/*-------------------------------------------------------------------------
+ *
+ * acldefs.h
+ * base definitions for ACLs and role attributes
+ *
+ * Portions Copyright (c) 2014, PostgreSQL Global Development Group
+ *
+ * src/include/catalog/acldefs.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef ACLDEFS_H
+#define ACLDEFS_H
+
+/*
+ * Grantable rights are encoded so that we can OR them together in a bitmask.
+ * The present representation of AclItem limits us to 16 distinct rights,
+ * even though AclMode is defined as uint32. See utils/acl.h.
+ *
+ * Caution: changing these codes breaks stored ACLs, hence forces initdb.
+ */
+typedef uint32 AclMode; /* a bitmask of privilege bits */
+
+#define ACL_INSERT (1<<0) /* for relations */
+#define ACL_SELECT (1<<1)
+#define ACL_UPDATE (1<<2)
+#define ACL_DELETE (1<<3)
+#define ACL_TRUNCATE (1<<4)
+#define ACL_REFERENCES (1<<5)
+#define ACL_TRIGGER (1<<6)
+#define ACL_EXECUTE (1<<7) /* for functions */
+#define ACL_USAGE (1<<8) /* for languages, namespaces, FDWs, and
+ * servers */
+#define ACL_CREATE (1<<9) /* for namespaces and databases */
+#define ACL_CREATE_TEMP (1<<10) /* for databases */
+#define ACL_CONNECT (1<<11) /* for databases */
+#define N_ACL_RIGHTS 12 /* 1 plus the last 1<