diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
new file mode 100644
index 9ceb96b..b0b4fca
*** a/doc/src/sgml/catalogs.sgml
--- b/doc/src/sgml/catalogs.sgml
***************
*** 1391,1441 ****
! 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
! 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
--- 1391,1493 ----
! rolattr
! bigint
!
! Role attributes; see for details
!
!
!
!
! rolconnlimit
! int4
!
! For roles that can log in, this sets maximum number of concurrent
! connections this role can make. -1 means no limit.
!
!
!
!
! rolpassword
! text
!
! Password (possibly encrypted); null if none. If the password
! is encrypted, this column will begin with the string md5>
! followed by a 32-character hexadecimal MD5 hash. The MD5 hash
! will be of the user's password concatenated to their user name.
! For example, if user joe> has password xyzzy>,
! PostgreSQL> will store the md5 hash of
! xyzzyjoe>. A password that does not follow that
! format is assumed to be unencrypted.
!
!
!
!
! rolvaliduntil
! timestamptz
! Password expiry time (only used for password authentication);
! null if no expiration
!
!
!
!
!
!
! rolattr> bitmap positions
!
!
!
!
! Position
! Attribute
! Description
!
!
!
!
!
! 0
! Superuser
Role has superuser privileges
! 1
! Inherit
! Role automatically inherits privileges of roles it is a member of
! 2
! Create Role
Role can create more roles
! 3
! Create DB
Role can create databases
! 4
! Catalog Update
! Role can update system catalogs directly. (Even a superuser cannot do this unless this column is true)
! 5
! Can Login
! Role can log in. That is, this role can be given as the initial session authorization identifier
! 6
! Replication
Role is a replication role. That is, this role can initiate streaming
replication (see ) and set/unset
***************
*** 1445,1479 ****
! rolconnlimit
! int4
!
! For roles that can log in, this sets maximum number of concurrent
! connections this role can make. -1 means no limit.
!
!
!
!
! rolpassword
! text
! Password (possibly encrypted); null if none. If the password
! is encrypted, this column will begin with the string md5>
! followed by a 32-character hexadecimal MD5 hash. The MD5 hash
! will be of the user's password concatenated to their user name.
! For example, if user joe> has password xyzzy>,
! PostgreSQL> will store the md5 hash of
! xyzzyjoe>. A password that does not follow that
! format is assumed to be unencrypted.
-
- rolvaliduntil
- timestamptz
- Password expiry time (only used for password authentication);
- null if no expiration
-
--- 1497,1509 ----
! 7
! By-pass Row Level Security
! Role can by-pass row level security policies when row_security> is set off>
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
new file mode 100644
index ef69b94..74bc702
*** a/doc/src/sgml/func.sgml
--- b/doc/src/sgml/func.sgml
*************** SELECT has_function_privilege('joeuser',
*** 15137,15142 ****
--- 15137,15259 ----
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)
+ boolean
+ convert bitmap of role attribute representation to string array
+
+
+
+
+
+
+ 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.
+ 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 check 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 string 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
new file mode 100644
index 133143d..8475985
*** a/src/backend/access/transam/xlogfuncs.c
--- b/src/backend/access/transam/xlogfuncs.c
***************
*** 22,32 ****
--- 22,34 ----
#include "access/xlog_internal.h"
#include "access/xlogutils.h"
#include "catalog/catalog.h"
+ #include "catalog/pg_authid.h"
#include "catalog/pg_type.h"
#include "funcapi.h"
#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"
*************** pg_start_backup(PG_FUNCTION_ARGS)
*** 54,60 ****
backupidstr = text_to_cstring(backupid);
! if (!superuser() && !has_rolreplication(GetUserId()))
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("must be superuser or replication role to run a backup")));
--- 56,62 ----
backupidstr = text_to_cstring(backupid);
! if (!have_role_attribute(ROLE_ATTR_REPLICATION))
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("must be superuser or replication role to run a backup")));
*************** pg_stop_backup(PG_FUNCTION_ARGS)
*** 82,88 ****
{
XLogRecPtr stoppoint;
! if (!superuser() && !has_rolreplication(GetUserId()))
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
(errmsg("must be superuser or replication role to run a backup"))));
--- 84,90 ----
{
XLogRecPtr stoppoint;
! 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/aclchk.c b/src/backend/catalog/aclchk.c
new file mode 100644
index d30612c..4663429
*** 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 &&
! !has_role_attribute(roleid, ROLE_ATTR_CATUPDATE) &&
!allowSystemTableMods)
{
#ifdef ACLDEBUG
*************** pg_extension_ownercheck(Oid ext_oid, Oid
*** 5051,5102 ****
}
/*
! * 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
* 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)
{
! 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;
}
/*
--- 5031,5110 ----
}
/*
! * has_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: 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_role_attribute(Oid roleid, RoleAttr attribute)
{
! /*
! * Superusers bypass all permission checking except in the case of CATUPDATE.
! */
! if (!(attribute & ROLE_ATTR_CATUPDATE) && superuser_arg(roleid))
return true;
! 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
! have_role_attribute(RoleAttr attribute)
{
! return has_role_attribute(GetUserId(), attribute);
! }
! /*
! * 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;
! 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
new file mode 100644
index ca89879..2929b66
*** a/src/backend/catalog/genbki.pl
--- b/src/backend/catalog/genbki.pl
*************** my $BOOTSTRAP_SUPERUSERID =
*** 90,95 ****
--- 90,97 ----
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('pg_authid.h', 'ROLE_ATTR_ALL');
# Read all the input header files into internal data structures
my $catalogs = Catalog::Catalogs(@input_files);
*************** foreach my $catname (@{ $catalogs->{name
*** 144,149 ****
--- 146,152 ----
# 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
new file mode 100644
index a036c62..87b6d8c
*** 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 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
new file mode 100644
index e261307..6df7e4d
*** a/src/backend/catalog/objectaddress.c
--- b/src/backend/catalog/objectaddress.c
*************** check_object_ownership(Oid roleid, Objec
*** 1310,1316 ****
}
else
{
! if (!has_createrole_privilege(roleid))
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("must have CREATEROLE privilege")));
--- 1310,1316 ----
}
else
{
! 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
new file mode 100644
index 22b8cee..ae93832
*** 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,
! 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,
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,
! 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 pg_check_role_attribute(pg_authid.rolattr, '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 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
new file mode 100644
index 1a5244c..c079168
*** a/src/backend/commands/dbcommands.c
--- b/src/backend/commands/dbcommands.c
*************** static bool get_db_info(const char *name
*** 85,91 ****
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);
--- 85,90 ----
*************** createdb(const CreatedbStmt *stmt)
*** 291,297 ****
* "giveaway" attacks. Note that a superuser will always have both of
* these privileges a fortiori.
*/
! if (!have_createdb_privilege())
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("permission denied to create database")));
--- 290,296 ----
* "giveaway" attacks. Note that a superuser will always have both of
* these privileges a fortiori.
*/
! if (!have_role_attribute(ROLE_ATTR_CREATEDB))
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("permission denied to create database")));
*************** RenameDatabase(const char *oldname, cons
*** 965,971 ****
oldname);
/* must have createdb rights */
! if (!have_createdb_privilege())
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("permission denied to rename database")));
--- 964,970 ----
oldname);
/* must have createdb rights */
! if (!have_role_attribute(ROLE_ATTR_CREATEDB))
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("permission denied to rename database")));
*************** AlterDatabaseOwner(const char *dbname, O
*** 1623,1629 ****
* databases. Because superusers will always have this right, we need
* no special case for them.
*/
! if (!have_createdb_privilege())
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("permission denied to change owner of database")));
--- 1622,1628 ----
* databases. Because superusers will always have this right, we need
* no special case for them.
*/
! if (!have_role_attribute(ROLE_ATTR_CREATEDB))
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("permission denied to change owner of database")));
*************** get_db_info(const char *name, LOCKMODE l
*** 1802,1827 ****
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
*
--- 1801,1806 ----
diff --git a/src/backend/commands/user.c b/src/backend/commands/user.c
new file mode 100644
index 1a73fd8..5bde610
*** a/src/backend/commands/user.c
--- b/src/backend/commands/user.c
*************** static void DelRoleMems(const char *role
*** 56,69 ****
bool admin_opt);
- /* Check if current user has createrole privileges */
- static bool
- have_createrole_privilege(void)
- {
- return has_createrole_privilege(GetUserId());
- }
-
-
/*
* CREATE ROLE
*/
--- 56,61 ----
*************** CreateRole(CreateRoleStmt *stmt)
*** 88,93 ****
--- 80,86 ----
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 ****
--- 242,249 ----
if (dpassword && dpassword->arg)
password = strVal(dpassword->arg);
+
+ /* Role Attributes */
if (dissuper)
issuper = intVal(dissuper->arg) != 0;
if (dinherit)
*************** CreateRole(CreateRoleStmt *stmt)
*** 261,266 ****
--- 256,264 ----
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)
--- 275,280 ----
*************** CreateRole(CreateRoleStmt *stmt)
*** 304,310 ****
}
else
{
! if (!have_createrole_privilege())
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("permission denied to create role")));
--- 300,306 ----
}
else
{
! if (!have_role_attribute(ROLE_ATTR_CREATEROLE))
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("permission denied to create role")));
*************** CreateRole(CreateRoleStmt *stmt)
*** 355,360 ****
--- 351,372 ----
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)
--- 376,383 ----
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);
/*
--- 400,405 ----
*************** AlterRole(AlterRoleStmt *stmt)
*** 508,513 ****
--- 512,518 ----
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,688 ****
* 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,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("must be superuser to change bypassrls attribute")));
}
! else if (!have_createrole_privilege())
{
if (!(inherit < 0 &&
createrole < 0 &&
--- 666,696 ----
* 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) || issuper >= 0)
{
if (!superuser())
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("must be superuser to alter superusers")));
}
! 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 ((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_role_attribute(ROLE_ATTR_CREATEROLE))
{
if (!(inherit < 0 &&
createrole < 0 &&
*************** 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);
--- 751,807 ----
*/
if (issuper >= 0)
{
! attributes = (issuper > 0) ? attributes | (ROLE_ATTR_SUPERUSER | ROLE_ATTR_CATUPDATE)
! : attributes & ~(ROLE_ATTR_SUPERUSER | ROLE_ATTR_CATUPDATE);
! new_record_repl[Anum_pg_authid_rolattr - 1] = true;
}
if (inherit >= 0)
{
! attributes = (inherit > 0) ? attributes | ROLE_ATTR_INHERIT
! : attributes & ~(ROLE_ATTR_INHERIT);
! new_record_repl[Anum_pg_authid_rolattr - 1] = true;
}
if (createrole >= 0)
{
! attributes = (createrole > 0) ? attributes | ROLE_ATTR_CREATEROLE
! : attributes & ~(ROLE_ATTR_CREATEROLE);
! new_record_repl[Anum_pg_authid_rolattr - 1] = true;
}
if (createdb >= 0)
{
! attributes = (createdb > 0) ? attributes | ROLE_ATTR_CREATEDB
! : attributes & ~(ROLE_ATTR_CREATEDB);
! new_record_repl[Anum_pg_authid_rolattr - 1] = true;
}
if (canlogin >= 0)
{
! attributes = (canlogin > 0) ? attributes | ROLE_ATTR_CANLOGIN
! : attributes & ~(ROLE_ATTR_CANLOGIN);
! new_record_repl[Anum_pg_authid_rolattr - 1] = true;
}
if (isreplication >= 0)
{
! attributes = (isreplication > 0) ? attributes | ROLE_ATTR_REPLICATION
! : attributes & ~(ROLE_ATTR_REPLICATION);
! new_record_repl[Anum_pg_authid_rolattr - 1] = true;
! }
!
! if (bypassrls >= 0)
! {
! attributes = (bypassrls > 0) ? attributes | ROLE_ATTR_BYPASSRLS
! : 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);
*************** 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);
--- 837,842 ----
*************** AlterRoleSet(AlterRoleSetStmt *stmt)
*** 867,872 ****
--- 884,890 ----
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,
--- 907,914 ----
* 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)
{
if (!superuser())
ereport(ERROR,
*************** AlterRoleSet(AlterRoleSetStmt *stmt)
*** 898,904 ****
}
else
{
! if (!have_createrole_privilege() &&
HeapTupleGetOid(roletuple) != GetUserId())
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
--- 917,923 ----
}
else
{
! if (!have_role_attribute(ROLE_ATTR_CREATEROLE) &&
HeapTupleGetOid(roletuple) != GetUserId())
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
*************** DropRole(DropRoleStmt *stmt)
*** 951,957 ****
pg_auth_members_rel;
ListCell *item;
! if (!have_createrole_privilege())
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("permission denied to drop role")));
--- 970,976 ----
pg_auth_members_rel;
ListCell *item;
! if (!have_role_attribute(ROLE_ATTR_CREATEROLE))
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("permission denied to drop role")));
*************** DropRole(DropRoleStmt *stmt)
*** 973,978 ****
--- 992,998 ----
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")));
--- 1033,1040 ----
* 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) && !superuser())
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("must be superuser to drop superusers")));
*************** RenameRole(const char *oldname, const ch
*** 1128,1133 ****
--- 1148,1154 ----
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,
--- 1194,1201 ----
/*
* createrole is enough privilege unless you want to mess with a superuser
*/
! attributes = ((Form_pg_authid) GETSTRUCT(oldtuple))->rolattr;
! if (attributes & ROLE_ATTR_SUPERUSER)
{
if (!superuser())
ereport(ERROR,
*************** RenameRole(const char *oldname, const ch
*** 1182,1188 ****
}
else
{
! if (!have_createrole_privilege())
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("permission denied to rename role")));
--- 1204,1210 ----
}
else
{
! if (!have_role_attribute(ROLE_ATTR_CREATEROLE))
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("permission denied to rename role")));
*************** AddRoleMems(const char *rolename, Oid ro
*** 1409,1415 ****
}
else
{
! if (!have_createrole_privilege() &&
!is_admin_of_role(grantorId, roleid))
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
--- 1431,1437 ----
}
else
{
! if (!have_role_attribute(ROLE_ATTR_CREATEROLE) &&
!is_admin_of_role(grantorId, roleid))
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
*************** DelRoleMems(const char *rolename, Oid ro
*** 1555,1561 ****
}
else
{
! if (!have_createrole_privilege() &&
!is_admin_of_role(GetUserId(), roleid))
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
--- 1577,1583 ----
}
else
{
! 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
new file mode 100644
index 6ce8dae..491dc38
*** 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);
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);
ReleaseSysCache(roleTup);
diff --git a/src/backend/replication/logical/logicalfuncs.c b/src/backend/replication/logical/logicalfuncs.c
new file mode 100644
index 1977f09..5f1126e
*** a/src/backend/replication/logical/logicalfuncs.c
--- b/src/backend/replication/logical/logicalfuncs.c
***************
*** 23,34 ****
--- 23,36 ----
#include "access/xlog_internal.h"
+ #include "catalog/pg_authid.h"
#include "catalog/pg_type.h"
#include "nodes/makefuncs.h"
#include "mb/pg_wchar.h"
+ #include "utils/acl.h"
#include "utils/array.h"
#include "utils/builtins.h"
#include "utils/inval.h"
*************** XLogRead(char *buf, TimeLineID tli, XLog
*** 205,211 ****
static void
check_permissions(void)
{
! if (!superuser() && !has_rolreplication(GetUserId()))
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
(errmsg("must be superuser or replication role to use replication slots"))));
--- 207,213 ----
static void
check_permissions(void)
{
! 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
new file mode 100644
index bd4701f..bc6a23a
*** a/src/backend/replication/slotfuncs.c
--- b/src/backend/replication/slotfuncs.c
***************
*** 17,32 ****
#include "miscadmin.h"
#include "access/htup_details.h"
#include "replication/slot.h"
#include "replication/logical.h"
#include "replication/logicalfuncs.h"
#include "utils/builtins.h"
#include "utils/pg_lsn.h"
static void
check_permissions(void)
{
! if (!superuser() && !has_rolreplication(GetUserId()))
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
(errmsg("must be superuser or replication role to use replication slots"))));
--- 17,34 ----
#include "miscadmin.h"
#include "access/htup_details.h"
+ #include "catalog/pg_authid.h"
#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 (!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
new file mode 100644
index 6c232dc..58633cc
*** a/src/backend/rewrite/rowsecurity.c
--- b/src/backend/rewrite/rowsecurity.c
***************
*** 36,41 ****
--- 36,42 ----
#include "access/heapam.h"
#include "access/htup_details.h"
#include "access/sysattr.h"
+ #include "catalog/pg_authid.h"
#include "catalog/pg_class.h"
#include "catalog/pg_inherits_fn.h"
#include "catalog/pg_policy.h"
*************** check_enable_rls(Oid relid, Oid checkAsU
*** 521,527 ****
*/
if (!checkAsUser && row_security == ROW_SECURITY_OFF)
{
! if (has_bypassrls_privilege(user_id))
/* OK to bypass */
return RLS_NONE_ENV;
else
--- 522,528 ----
*/
if (!checkAsUser && row_security == ROW_SECURITY_OFF)
{
! 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
new file mode 100644
index dc6eb2c..93017b2
*** 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);
*************** pg_role_aclcheck(Oid role_oid, Oid rolei
*** 4602,4607 ****
--- 4603,4790 ----
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;
+
+ /*
+ * If no attributes are assigned, then there is no need to go through the
+ * individual checks for which are assigned. Therefore, we can short circuit
+ * and return an empty array.
+ */
+ 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)
*************** RoleMembershipCacheCallback(Datum arg, i
*** 4634,4656 ****
}
- /* 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
*
--- 4817,4822 ----
*************** roles_has_privs_of(Oid roleid)
*** 4697,4703 ****
int i;
/* Ignore non-inheriting roles */
! if (!has_rolinherit(memberid))
continue;
/* Find roles that memberid is directly a member of */
--- 4863,4869 ----
int i;
/* Ignore non-inheriting roles */
! 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
new file mode 100644
index 2f02303..c0d0718
*** a/src/backend/utils/adt/ri_triggers.c
--- b/src/backend/utils/adt/ri_triggers.c
***************
*** 33,38 ****
--- 33,39 ----
#include "access/htup_details.h"
#include "access/sysattr.h"
#include "access/xact.h"
+ #include "catalog/pg_authid.h"
#include "catalog/pg_collation.h"
#include "catalog/pg_constraint.h"
#include "catalog/pg_operator.h"
***************
*** 43,48 ****
--- 44,50 ----
#include "parser/parse_coerce.h"
#include "parser/parse_relation.h"
#include "miscadmin.h"
+ #include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/fmgroids.h"
#include "utils/guc.h"
*************** RI_Initial_Check(Trigger *trigger, Relat
*** 2308,2314 ****
* bypassrls right or is the table owner of the table(s) involved which
* have RLS enabled.
*/
! if (!has_bypassrls_privilege(GetUserId()) &&
((pk_rel->rd_rel->relrowsecurity &&
!pg_class_ownercheck(pkrte->relid, GetUserId())) ||
(fk_rel->rd_rel->relrowsecurity &&
--- 2310,2316 ----
* bypassrls right or is the table owner of the table(s) involved which
* have RLS enabled.
*/
! 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
new file mode 100644
index 8fccb4c..db2a0fb
*** a/src/backend/utils/init/miscinit.c
--- b/src/backend/utils/init/miscinit.c
***************
*** 40,45 ****
--- 40,46 ----
#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"
*************** SetUserIdAndContext(Oid userid, bool sec
*** 329,352 ****
/*
- * 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
*/
void
--- 330,335 ----
*************** InitializeSessionUserId(const char *role
*** 375,381 ****
roleid = HeapTupleGetOid(roleTup);
AuthenticatedUserId = roleid;
! AuthenticatedUserIsSuperuser = rform->rolsuper;
/* This sets OuterUserId/CurrentUserId too */
SetSessionUserId(roleid, AuthenticatedUserIsSuperuser);
--- 358,364 ----
roleid = HeapTupleGetOid(roleTup);
AuthenticatedUserId = roleid;
! AuthenticatedUserIsSuperuser = (rform->rolattr & ROLE_ATTR_SUPERUSER);
/* 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",
--- 377,383 ----
/*
* Is role allowed to login at all?
*/
! 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
new file mode 100644
index c348034..268001f
*** a/src/backend/utils/init/postinit.c
--- b/src/backend/utils/init/postinit.c
*************** InitPostgres(const char *in_dbname, Oid
*** 762,768 ****
{
Assert(!bootstrap);
! if (!superuser() && !has_rolreplication(GetUserId()))
ereport(FATAL,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("must be superuser or replication role to start walsender")));
--- 762,768 ----
{
Assert(!bootstrap);
! 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
new file mode 100644
index ff0f947..9af77ed
*** a/src/backend/utils/misc/superuser.c
--- b/src/backend/utils/misc/superuser.c
***************
*** 22,27 ****
--- 22,28 ----
#include "access/htup_details.h"
#include "catalog/pg_authid.h"
+ #include "utils/acl.h"
#include "utils/inval.h"
#include "utils/syscache.h"
#include "miscadmin.h"
*************** superuser_arg(Oid roleid)
*** 58,63 ****
--- 59,65 ----
{
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
--- 73,80 ----
rtup = SearchSysCache1(AUTHOID, ObjectIdGetDatum(roleid));
if (HeapTupleIsValid(rtup))
{
! 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
new file mode 100644
index eb633bc..f638167
*** a/src/bin/pg_dump/pg_dumpall.c
--- b/src/bin/pg_dump/pg_dumpall.c
*************** dumpRoles(PGconn *conn)
*** 671,680 ****
/* note: rolconfig is dumped later */
if (server_version >= 90500)
printfPQExpBuffer(buf,
! "SELECT oid, rolname, rolsuper, rolinherit, "
! "rolcreaterole, rolcreatedb, "
! "rolcanlogin, rolconnlimit, rolpassword, "
! "rolvaliduntil, rolreplication, rolbypassrls, "
"pg_catalog.shobj_description(oid, 'pg_authid') as rolcomment, "
"rolname = current_user AS is_current_user "
"FROM pg_authid "
--- 671,686 ----
/* note: rolconfig is dumped later */
if (server_version >= 90500)
printfPQExpBuffer(buf,
! "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/pg_authid.h b/src/include/catalog/pg_authid.h
new file mode 100644
index 3b63d2b..f28d9f4
*** a/src/include/catalog/pg_authid.h
--- b/src/include/catalog/pg_authid.h
***************
*** 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 */
--- 45,52 ----
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,101 ****
* 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
*
* The uppercase quantities will be replaced at initdb time with
* user choices.
* ----------------
*/
! DATA(insert OID = 10 ( "POSTGRES" t t t t t t t t -1 _null_ _null_));
#define BOOTSTRAP_SUPERUSERID 10
--- 66,118 ----
* 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
!
! /* ----------------
! * Role attributes are encoded so that we can OR them together in a bitmask.
! * The present representation of RoleAttr (defined in acl.h) limits us to 64
! * distinct rights.
! * ----------------
! */
! #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 /* 1 plus the last 1<