Commit 1826987a authored by Alvaro Herrera's avatar Alvaro Herrera

Use a bitmask to represent role attributes

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

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

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

Author: Adam Brightwell, minor tweaks by Álvaro
Reviewed by: Stephen Frost, Andres Freund, Álvaro Herrera
parent 7eca575d
...@@ -1391,89 +1391,134 @@ ...@@ -1391,89 +1391,134 @@
</row> </row>
<row> <row>
<entry><structfield>rolsuper</structfield></entry> <entry><structfield>rolattr</structfield></entry>
<entry><type>bool</type></entry> <entry><type>bigint</type></entry>
<entry>
Role attributes; see <xref linkend="catalog-rolattr-bitmap-table"> and
<xref linkend="sql-createrole"> for details
</entry>
</row>
<row>
<entry><structfield>rolconnlimit</structfield></entry>
<entry><type>int4</type></entry>
<entry>
For roles that can log in, this sets maximum number of concurrent
connections this role can make. -1 means no limit.
</entry>
</row>
<row>
<entry><structfield>rolpassword</structfield></entry>
<entry><type>text</type></entry>
<entry>
Password (possibly encrypted); null if none. If the password
is encrypted, this column will begin with the string <literal>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 <literal>joe</> has password <literal>xyzzy</>,
<productname>PostgreSQL</> will store the md5 hash of
<literal>xyzzyjoe</>. A password that does not follow that
format is assumed to be unencrypted.
</entry>
</row>
<row>
<entry><structfield>rolvaliduntil</structfield></entry>
<entry><type>timestamptz</type></entry>
<entry>Password expiry time (only used for password authentication);
null if no expiration</entry>
</row>
</tbody>
</tgroup>
</table>
<table id="catalog-rolattr-bitmap-table">
<title>Attributes in <structfield>rolattr</></title>
<tgroup cols="4">
<thead>
<row>
<entry>Attribute</entry>
<entry>CREATE ROLE Option</entry>
<entry>Description</entry>
<entry>Position</entry>
</row>
</thead>
<tbody>
<row>
<entry>Superuser</entry>
<entry>SUPERUSER</entry>
<entry>Role has superuser privileges</entry> <entry>Role has superuser privileges</entry>
<entry><literal>0</literal></entry>
</row> </row>
<row> <row>
<entry><structfield>rolinherit</structfield></entry> <entry>Inherit</entry>
<entry><type>bool</type></entry> <entry>INHERIT</entry>
<entry>Role automatically inherits privileges of roles it is a <entry>
member of</entry> Role automatically inherits privileges of roles it is a member of
</entry>
<entry><literal>1</literal></entry>
</row> </row>
<row> <row>
<entry><structfield>rolcreaterole</structfield></entry> <entry>Create Role</entry>
<entry><type>bool</type></entry> <entry>CREATEROLE</entry>
<entry>Role can create more roles</entry> <entry>Role can create more roles</entry>
<entry><literal>2</literal></entry>
</row> </row>
<row> <row>
<entry><structfield>rolcreatedb</structfield></entry> <entry>Create DB</entry>
<entry><type>bool</type></entry> <entry>CREATEDB</entry>
<entry>Role can create databases</entry> <entry>Role can create databases</entry>
<entry><literal>3</literal></entry>
</row> </row>
<row> <row>
<entry><structfield>rolcatupdate</structfield></entry> <entry>Catalog Update</entry>
<entry><type>bool</type></entry> <entry>CATUPDATE</entry>
<entry> <entry>
Role can update system catalogs directly. (Even a superuser cannot do Role can update system catalogs directly. (Even a superuser cannot do
this unless this column is true) this unless this column is true)
</entry> </entry>
<entry><literal>4</literal></entry>
</row> </row>
<row> <row>
<entry><structfield>rolcanlogin</structfield></entry> <entry>Can Login</entry>
<entry><type>bool</type></entry> <entry>LOGIN</entry>
<entry> <entry>
Role can log in. That is, this role can be given as the initial Role can log in. That is, this role can be given as the initial
session authorization identifier session authorization identifier
</entry> </entry>
<entry><literal>5</literal></entry>
</row> </row>
<row> <row>
<entry><structfield>rolreplication</structfield></entry> <entry>Replication</entry>
<entry><type>bool</type></entry> <entry>REPLICATION</entry>
<entry> <entry>
Role is a replication role. That is, this role can initiate streaming Role is a replication role. That is, this role can initiate streaming
replication (see <xref linkend="streaming-replication">) and set/unset replication (see <xref linkend="streaming-replication">) and set/unset
the system backup mode using <function>pg_start_backup</> and the system backup mode using <function>pg_start_backup</> and
<function>pg_stop_backup</> <function>pg_stop_backup</>
</entry> </entry>
<entry><literal>6</literal></entry>
</row> </row>
<row> <row>
<entry><structfield>rolconnlimit</structfield></entry> <entry>Bypass Row Level Security</entry>
<entry><type>int4</type></entry> <entry>BYPASSRLS</entry>
<entry> <entry>
For roles that can log in, this sets maximum number of concurrent Role can bypass row level security policies when <literal>row_security</>
connections this role can make. -1 means no limit. is set <literal>off</>
</entry>
</row>
<row>
<entry><structfield>rolpassword</structfield></entry>
<entry><type>text</type></entry>
<entry>
Password (possibly encrypted); null if none. If the password
is encrypted, this column will begin with the string <literal>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 <literal>joe</> has password <literal>xyzzy</>,
<productname>PostgreSQL</> will store the md5 hash of
<literal>xyzzyjoe</>. A password that does not follow that
format is assumed to be unencrypted.
</entry> </entry>
<entry><literal>7</literal></entry>
</row> </row>
<row>
<entry><structfield>rolvaliduntil</structfield></entry>
<entry><type>timestamptz</type></entry>
<entry>Password expiry time (only used for password authentication);
null if no expiration</entry>
</row>
</tbody> </tbody>
</tgroup> </tgroup>
</table> </table>
......
...@@ -15139,6 +15139,133 @@ SELECT has_function_privilege('joeuser', 'myfunc(int, text)', 'execute'); ...@@ -15139,6 +15139,133 @@ SELECT has_function_privilege('joeuser', 'myfunc(int, text)', 'execute');
are immediately available without doing <command>SET ROLE</>. are immediately available without doing <command>SET ROLE</>.
</para> </para>
<para>
<xref linkend="functions-info-role-attribute-table"> lists functions that
allow the user to query role attribute information programmatically.
</para>
<table id="functions-info-role-attribute-table">
<title>Role Attribute Inquiry Functions</title>
<tgroup cols="3">
<thead>
<row><entry>Name</entry> <entry>Return Type</entry> <entry>Description</entry></row>
</thead>
<tbody>
<row>
<entry><literal><function>pg_has_role_attribute(role, attribute)</function></literal></entry>
<entry><type>boolean</type></entry>
<entry>does role have the permissions allowed by named attribute</entry>
</row>
<row>
<entry><literal><function>pg_check_role_attribute(role, attribute)</function></literal></entry>
<entry><type>boolean</type></entry>
<entry>does role have the named attribute</entry>
</row>
<row>
<entry><literal><function>pg_check_role_attribute(role_attributes, attribute)</function></literal></entry>
<entry><type>boolean</type></entry>
<entry>is attribute set in bitmap of role attributes</entry>
</row>
<row>
<entry><literal><function>pg_all_role_attributes(role_attributes)</function></literal></entry>
<entry><type>text[]</type></entry>
<entry>convert bitmap of role attribute representation to text[]</entry>
</row>
</tbody>
</tgroup>
</table>
<indexterm>
<primary>pg_has_role_attribute</primary>
</indexterm>
<indexterm>
<primary>pg_check_role_attribute</primary>
</indexterm>
<indexterm>
<primary>pg_all_role_attributes</primary>
</indexterm>
<para>
<function>pg_has_role_attribute</function> checks the attribute permissions
given to a role. It will always return <literal>true</literal> for roles
with superuser privileges unless the attribute being checked is
<literal>CATUPDATE</literal> (superuser cannot bypass
<literal>CATUPDATE</literal> 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:
<literal>SUPERUSER</literal>,
<literal>INHERIT</literal>,
<literal>CREATEROLE</literal>,
<literal>CREATEDB</literal>,
<literal>CATUPDATE</literal>,
<literal>CANLOGIN</literal>,
<literal>REPLICATION</literal>, or
<literal>BYPASSRLS</literal>. See <xref linkend="sql-createrole"> for more
information. For example:
<programlisting>
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)
</programlisting>
</para>
<para>
<function>pg_check_role_attribute</function> 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
<function>pg_has_role_attribute</function>). A third variant of this function
allows for a bitmap representation (<literal>bigint</literal>) of attributes
to be given instead of a role.
Example:
<programlisting>
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)
</programlisting>
</para>
<para>
<function>pg_all_role_attributes</function> convert a set of role attributes
represented by an <literal>bigint</literal> bitmap to a text array.
Example:
<programlisting>
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)
</programlisting>
</para>
<para> <para>
<xref linkend="functions-info-schema-table"> shows functions that <xref linkend="functions-info-schema-table"> shows functions that
determine whether a certain object is <firstterm>visible</> in the determine whether a certain object is <firstterm>visible</> in the
......
...@@ -27,6 +27,7 @@ ...@@ -27,6 +27,7 @@
#include "miscadmin.h" #include "miscadmin.h"
#include "replication/walreceiver.h" #include "replication/walreceiver.h"
#include "storage/smgr.h" #include "storage/smgr.h"
#include "utils/acl.h"
#include "utils/builtins.h" #include "utils/builtins.h"
#include "utils/numeric.h" #include "utils/numeric.h"
#include "utils/guc.h" #include "utils/guc.h"
...@@ -54,7 +55,7 @@ pg_start_backup(PG_FUNCTION_ARGS) ...@@ -54,7 +55,7 @@ pg_start_backup(PG_FUNCTION_ARGS)
backupidstr = text_to_cstring(backupid); backupidstr = text_to_cstring(backupid);
if (!superuser() && !has_rolreplication(GetUserId())) if (!have_role_attribute(ROLE_ATTR_REPLICATION))
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("must be superuser or replication role to run a backup"))); errmsg("must be superuser or replication role to run a backup")));
...@@ -82,7 +83,7 @@ pg_stop_backup(PG_FUNCTION_ARGS) ...@@ -82,7 +83,7 @@ pg_stop_backup(PG_FUNCTION_ARGS)
{ {
XLogRecPtr stoppoint; XLogRecPtr stoppoint;
if (!superuser() && !has_rolreplication(GetUserId())) if (!have_role_attribute(ROLE_ATTR_REPLICATION))
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
(errmsg("must be superuser or replication role to run a backup")))); (errmsg("must be superuser or replication role to run a backup"))));
......
...@@ -176,7 +176,7 @@ sub Catalogs ...@@ -176,7 +176,7 @@ sub Catalogs
} }
} }
} }
$catalogs{$catname} = \%catalog; $catalogs{$catname} = \%catalog if defined $catname;
close INPUT_FILE; close INPUT_FILE;
} }
return \%catalogs; return \%catalogs;
......
...@@ -28,7 +28,7 @@ all: $(BKIFILES) schemapg.h ...@@ -28,7 +28,7 @@ all: $(BKIFILES) schemapg.h
# indexing.h had better be last, and toasting.h just before it. # indexing.h had better be last, and toasting.h just before it.
POSTGRES_BKI_SRCS = $(addprefix $(top_srcdir)/src/include/catalog/,\ 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_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_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 \ pg_language.h pg_largeobject_metadata.h pg_largeobject.h pg_aggregate.h \
......
...@@ -3423,26 +3423,6 @@ aclcheck_error_type(AclResult aclerr, Oid typeOid) ...@@ -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 * Relay for the various pg_*_mask routines depending on object kind
*/ */
...@@ -3630,7 +3610,7 @@ pg_class_aclmask(Oid table_oid, Oid roleid, ...@@ -3630,7 +3610,7 @@ pg_class_aclmask(Oid table_oid, Oid roleid,
if ((mask & (ACL_INSERT | ACL_UPDATE | ACL_DELETE | ACL_TRUNCATE | ACL_USAGE)) && if ((mask & (ACL_INSERT | ACL_UPDATE | ACL_DELETE | ACL_TRUNCATE | ACL_USAGE)) &&
IsSystemClass(table_oid, classForm) && IsSystemClass(table_oid, classForm) &&
classForm->relkind != RELKIND_VIEW && classForm->relkind != RELKIND_VIEW &&
!has_rolcatupdate(roleid) && !has_role_attribute(roleid, ROLE_ATTR_CATUPDATE) &&
!allowSystemTableMods) !allowSystemTableMods)
{ {
#ifdef ACLDEBUG #ifdef ACLDEBUG
...@@ -5051,52 +5031,87 @@ pg_extension_ownercheck(Oid ext_oid, Oid roleid) ...@@ -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.
*
* roleid - the oid of the role to check.
* attribute - the attribute to check.
* *
* Note: roles do not have owners per se; instead we use this test in * Note: Use this function for role attribute permission checking as it
* places where an ownership-like permissions test is needed for a role. * accounts for superuser status. It will always return true for roles with
* Be sure to apply it to the role trying to do the operation, not the * superuser privileges unless the attribute being checked is CATUPDATE
* role being operated on! Also note that this generally should not be * (superusers are not allowed to bypass CATUPDATE permissions).
* considered enough privilege if the target role is a superuser. *
* (We don't handle that consideration here because we want to give a * Note: roles do not have owners per se; instead we use this test in places
* separate error message for such cases, so the caller has to deal with it.) * 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 bool
has_createrole_privilege(Oid roleid) has_role_attribute(Oid roleid, RoleAttr attribute)
{ {
bool result = false; /*
HeapTuple utup; * Superusers bypass all permission checking except in the case of CATUPDATE
*/
/* Superusers bypass all permission checking. */ if (!(attribute & ROLE_ATTR_CATUPDATE) && superuser_arg(roleid))
if (superuser_arg(roleid))
return true; return true;
utup = SearchSysCache1(AUTHOID, ObjectIdGetDatum(roleid)); return check_role_attribute(roleid, attribute);
if (HeapTupleIsValid(utup)) }
{
result = ((Form_pg_authid) GETSTRUCT(utup))->rolcreaterole; /*
ReleaseSysCache(utup); * have_role_attribute
} * Convenience function for checking if the role id returned by GetUserId()
return result; * 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 bool
has_bypassrls_privilege(Oid roleid) check_role_attribute(Oid roleid, RoleAttr attribute)
{ {
bool result = false; RoleAttr attributes;
HeapTuple utup; HeapTuple tuple;
/* Superusers bypass all permission checking. */ /* ROLE_ATTR_NONE (zero) is not a valid attribute */
if (superuser_arg(roleid)) Assert(attribute != ROLE_ATTR_NONE);
return true;
utup = SearchSysCache1(AUTHOID, ObjectIdGetDatum(roleid)); /* Check that only one bit is set in 'attribute' */
if (HeapTupleIsValid(utup)) Assert(!(attribute & (attribute - 1)));
{
result = ((Form_pg_authid) GETSTRUCT(utup))->rolbypassrls; tuple = SearchSysCache1(AUTHOID, ObjectIdGetDatum(roleid));
ReleaseSysCache(utup);
} if (!HeapTupleIsValid(tuple))
return result; 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);
} }
/* /*
......
...@@ -90,6 +90,8 @@ my $BOOTSTRAP_SUPERUSERID = ...@@ -90,6 +90,8 @@ my $BOOTSTRAP_SUPERUSERID =
find_defined_symbol('pg_authid.h', 'BOOTSTRAP_SUPERUSERID'); find_defined_symbol('pg_authid.h', 'BOOTSTRAP_SUPERUSERID');
my $PG_CATALOG_NAMESPACE = my $PG_CATALOG_NAMESPACE =
find_defined_symbol('pg_namespace.h', '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 # Read all the input header files into internal data structures
my $catalogs = Catalog::Catalogs(@input_files); my $catalogs = Catalog::Catalogs(@input_files);
...@@ -144,6 +146,7 @@ foreach my $catname (@{ $catalogs->{names} }) ...@@ -144,6 +146,7 @@ foreach my $catname (@{ $catalogs->{names} })
# substitute constant values we acquired above # substitute constant values we acquired above
$row->{bki_values} =~ s/\bPGUID\b/$BOOTSTRAP_SUPERUSERID/g; $row->{bki_values} =~ s/\bPGUID\b/$BOOTSTRAP_SUPERUSERID/g;
$row->{bki_values} =~ s/\bPGNSP\b/$PG_CATALOG_NAMESPACE/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 # Save pg_type info for pg_attribute processing below
if ($catname eq 'pg_type') if ($catname eq 'pg_type')
......
...@@ -2884,7 +2884,12 @@ CREATE VIEW user_mapping_options AS ...@@ -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((pg_options_to_table(um.umoptions)).option_name AS sql_identifier) AS option_name,
CAST(CASE WHEN (umuser <> 0 AND authorization_identifier = current_user) CAST(CASE WHEN (umuser <> 0 AND authorization_identifier = current_user)
OR (umuser = 0 AND pg_has_role(srvowner, 'USAGE')) 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 ELSE NULL END AS character_data) AS option_value
FROM _pg_user_mappings um; FROM _pg_user_mappings um;
......
...@@ -1328,7 +1328,7 @@ check_object_ownership(Oid roleid, ObjectType objtype, ObjectAddress address, ...@@ -1328,7 +1328,7 @@ check_object_ownership(Oid roleid, ObjectType objtype, ObjectAddress address,
} }
else else
{ {
if (!has_createrole_privilege(roleid)) if (!has_role_attribute(roleid, ROLE_ATTR_CREATEROLE))
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("must have CREATEROLE privilege"))); errmsg("must have CREATEROLE privilege")));
......
...@@ -9,17 +9,17 @@ ...@@ -9,17 +9,17 @@
CREATE VIEW pg_roles AS CREATE VIEW pg_roles AS
SELECT SELECT
rolname, rolname,
rolsuper, pg_check_role_attribute(pg_authid.rolattr, 'SUPERUSER') AS rolsuper,
rolinherit, pg_check_role_attribute(pg_authid.rolattr, 'INHERIT') AS rolinherit,
rolcreaterole, pg_check_role_attribute(pg_authid.rolattr, 'CREATEROLE') AS rolcreaterole,
rolcreatedb, pg_check_role_attribute(pg_authid.rolattr, 'CREATEDB') AS rolcreatedb,
rolcatupdate, pg_check_role_attribute(pg_authid.rolattr, 'CATUPDATE') AS rolcatupdate,
rolcanlogin, pg_check_role_attribute(pg_authid.rolattr, 'CANLOGIN') AS rolcanlogin,
rolreplication, pg_check_role_attribute(pg_authid.rolattr, 'REPLICATION') AS rolreplication,
pg_check_role_attribute(pg_authid.rolattr, 'BYPASSRLS') AS rolbypassrls,
rolconnlimit, rolconnlimit,
'********'::text as rolpassword, '********'::text as rolpassword,
rolvaliduntil, rolvaliduntil,
rolbypassrls,
setconfig as rolconfig, setconfig as rolconfig,
pg_authid.oid pg_authid.oid
FROM pg_authid LEFT JOIN pg_db_role_setting s FROM pg_authid LEFT JOIN pg_db_role_setting s
...@@ -29,16 +29,16 @@ CREATE VIEW pg_shadow AS ...@@ -29,16 +29,16 @@ CREATE VIEW pg_shadow AS
SELECT SELECT
rolname AS usename, rolname AS usename,
pg_authid.oid AS usesysid, pg_authid.oid AS usesysid,
rolcreatedb AS usecreatedb, pg_check_role_attribute(pg_authid.rolattr, 'CREATEDB') AS usecreatedb,
rolsuper AS usesuper, pg_check_role_attribute(pg_authid.rolattr, 'SUPERUSER') AS usesuper,
rolcatupdate AS usecatupd, pg_check_role_attribute(pg_authid.rolattr, 'CATUPDATE') AS usecatupd,
rolreplication AS userepl, pg_check_role_attribute(pg_authid.rolattr, 'REPLICATION') AS userepl,
rolpassword AS passwd, rolpassword AS passwd,
rolvaliduntil::abstime AS valuntil, rolvaliduntil::abstime AS valuntil,
setconfig AS useconfig setconfig AS useconfig
FROM pg_authid LEFT JOIN pg_db_role_setting s FROM pg_authid LEFT JOIN pg_db_role_setting s
ON (pg_authid.oid = setrole AND setdatabase = 0) 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; REVOKE ALL on pg_shadow FROM public;
...@@ -48,7 +48,7 @@ CREATE VIEW pg_group AS ...@@ -48,7 +48,7 @@ CREATE VIEW pg_group AS
oid AS grosysid, oid AS grosysid,
ARRAY(SELECT member FROM pg_auth_members WHERE roleid = oid) AS grolist ARRAY(SELECT member FROM pg_auth_members WHERE roleid = oid) AS grolist
FROM pg_authid FROM pg_authid
WHERE NOT rolcanlogin; WHERE NOT pg_check_role_attribute(pg_authid.rolattr, 'CANLOGIN');
CREATE VIEW pg_user AS CREATE VIEW pg_user AS
SELECT SELECT
......
...@@ -85,7 +85,6 @@ static bool get_db_info(const char *name, LOCKMODE lockmode, ...@@ -85,7 +85,6 @@ static bool get_db_info(const char *name, LOCKMODE lockmode,
Oid *dbLastSysOidP, TransactionId *dbFrozenXidP, Oid *dbLastSysOidP, TransactionId *dbFrozenXidP,
MultiXactId *dbMinMultiP, MultiXactId *dbMinMultiP,
Oid *dbTablespace, char **dbCollate, char **dbCtype); Oid *dbTablespace, char **dbCollate, char **dbCtype);
static bool have_createdb_privilege(void);
static void remove_dbtablespaces(Oid db_id); static void remove_dbtablespaces(Oid db_id);
static bool check_db_file_conflict(Oid db_id); static bool check_db_file_conflict(Oid db_id);
static int errdetail_busy_db(int notherbackends, int npreparedxacts); static int errdetail_busy_db(int notherbackends, int npreparedxacts);
...@@ -291,7 +290,7 @@ createdb(const CreatedbStmt *stmt) ...@@ -291,7 +290,7 @@ createdb(const CreatedbStmt *stmt)
* "giveaway" attacks. Note that a superuser will always have both of * "giveaway" attacks. Note that a superuser will always have both of
* these privileges a fortiori. * these privileges a fortiori.
*/ */
if (!have_createdb_privilege()) if (!have_role_attribute(ROLE_ATTR_CREATEDB))
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("permission denied to create database"))); errmsg("permission denied to create database")));
...@@ -965,7 +964,7 @@ RenameDatabase(const char *oldname, const char *newname) ...@@ -965,7 +964,7 @@ RenameDatabase(const char *oldname, const char *newname)
oldname); oldname);
/* must have createdb rights */ /* must have createdb rights */
if (!have_createdb_privilege()) if (!have_role_attribute(ROLE_ATTR_CREATEDB))
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("permission denied to rename database"))); errmsg("permission denied to rename database")));
...@@ -1623,7 +1622,7 @@ AlterDatabaseOwner(const char *dbname, Oid newOwnerId) ...@@ -1623,7 +1622,7 @@ AlterDatabaseOwner(const char *dbname, Oid newOwnerId)
* databases. Because superusers will always have this right, we need * databases. Because superusers will always have this right, we need
* no special case for them. * no special case for them.
*/ */
if (!have_createdb_privilege()) if (!have_role_attribute(ROLE_ATTR_CREATEDB))
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("permission denied to change owner of database"))); errmsg("permission denied to change owner of database")));
...@@ -1802,26 +1801,6 @@ get_db_info(const char *name, LOCKMODE lockmode, ...@@ -1802,26 +1801,6 @@ get_db_info(const char *name, LOCKMODE lockmode,
return result; 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 * Remove tablespace directories
* *
......
This diff is collapsed.
...@@ -776,6 +776,7 @@ check_session_authorization(char **newval, void **extra, GucSource source) ...@@ -776,6 +776,7 @@ check_session_authorization(char **newval, void **extra, GucSource source)
Oid roleid; Oid roleid;
bool is_superuser; bool is_superuser;
role_auth_extra *myextra; role_auth_extra *myextra;
RoleAttr attributes;
/* Do nothing for the boot_val default of NULL */ /* Do nothing for the boot_val default of NULL */
if (*newval == NULL) if (*newval == NULL)
...@@ -800,7 +801,8 @@ check_session_authorization(char **newval, void **extra, GucSource source) ...@@ -800,7 +801,8 @@ check_session_authorization(char **newval, void **extra, GucSource source)
} }
roleid = HeapTupleGetOid(roleTup); 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); ReleaseSysCache(roleTup);
...@@ -844,6 +846,7 @@ check_role(char **newval, void **extra, GucSource source) ...@@ -844,6 +846,7 @@ check_role(char **newval, void **extra, GucSource source)
Oid roleid; Oid roleid;
bool is_superuser; bool is_superuser;
role_auth_extra *myextra; role_auth_extra *myextra;
RoleAttr attributes;
if (strcmp(*newval, "none") == 0) if (strcmp(*newval, "none") == 0)
{ {
...@@ -872,7 +875,8 @@ check_role(char **newval, void **extra, GucSource source) ...@@ -872,7 +875,8 @@ check_role(char **newval, void **extra, GucSource source)
} }
roleid = HeapTupleGetOid(roleTup); 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); ReleaseSysCache(roleTup);
......
...@@ -17,18 +17,14 @@ ...@@ -17,18 +17,14 @@
#include <unistd.h> #include <unistd.h>
#include "access/xlog_internal.h"
#include "catalog/pg_type.h"
#include "fmgr.h" #include "fmgr.h"
#include "funcapi.h" #include "funcapi.h"
#include "mb/pg_wchar.h"
#include "miscadmin.h" #include "miscadmin.h"
#include "access/xlog_internal.h"
#include "catalog/pg_type.h"
#include "nodes/makefuncs.h" #include "nodes/makefuncs.h"
#include "utils/acl.h"
#include "mb/pg_wchar.h"
#include "utils/array.h" #include "utils/array.h"
#include "utils/builtins.h" #include "utils/builtins.h"
#include "utils/inval.h" #include "utils/inval.h"
...@@ -36,11 +32,9 @@ ...@@ -36,11 +32,9 @@
#include "utils/pg_lsn.h" #include "utils/pg_lsn.h"
#include "utils/resowner.h" #include "utils/resowner.h"
#include "utils/lsyscache.h" #include "utils/lsyscache.h"
#include "replication/decode.h" #include "replication/decode.h"
#include "replication/logical.h" #include "replication/logical.h"
#include "replication/logicalfuncs.h" #include "replication/logicalfuncs.h"
#include "storage/fd.h" #include "storage/fd.h"
/* private date for writing out data */ /* private date for writing out data */
...@@ -205,7 +199,7 @@ XLogRead(char *buf, TimeLineID tli, XLogRecPtr startptr, Size count) ...@@ -205,7 +199,7 @@ XLogRead(char *buf, TimeLineID tli, XLogRecPtr startptr, Size count)
static void static void
check_permissions(void) check_permissions(void)
{ {
if (!superuser() && !has_rolreplication(GetUserId())) if (!have_role_attribute(ROLE_ATTR_REPLICATION))
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
(errmsg("must be superuser or replication role to use replication slots")))); (errmsg("must be superuser or replication role to use replication slots"))));
......
...@@ -20,13 +20,14 @@ ...@@ -20,13 +20,14 @@
#include "replication/slot.h" #include "replication/slot.h"
#include "replication/logical.h" #include "replication/logical.h"
#include "replication/logicalfuncs.h" #include "replication/logicalfuncs.h"
#include "utils/acl.h"
#include "utils/builtins.h" #include "utils/builtins.h"
#include "utils/pg_lsn.h" #include "utils/pg_lsn.h"
static void static void
check_permissions(void) check_permissions(void)
{ {
if (!superuser() && !has_rolreplication(GetUserId())) if (!have_role_attribute(ROLE_ATTR_REPLICATION))
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
(errmsg("must be superuser or replication role to use replication slots")))); (errmsg("must be superuser or replication role to use replication slots"))));
......
...@@ -521,7 +521,7 @@ check_enable_rls(Oid relid, Oid checkAsUser) ...@@ -521,7 +521,7 @@ check_enable_rls(Oid relid, Oid checkAsUser)
*/ */
if (!checkAsUser && row_security == ROW_SECURITY_OFF) 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 */ /* OK to bypass */
return RLS_NONE_ENV; return RLS_NONE_ENV;
else else
......
...@@ -115,6 +115,7 @@ static Oid convert_type_name(text *typename); ...@@ -115,6 +115,7 @@ static Oid convert_type_name(text *typename);
static AclMode convert_type_priv_string(text *priv_type_text); static AclMode convert_type_priv_string(text *priv_type_text);
static AclMode convert_role_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 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); static void RoleMembershipCacheCallback(Datum arg, int cacheid, uint32 hashvalue);
...@@ -4602,6 +4603,186 @@ pg_role_aclcheck(Oid role_oid, Oid roleid, AclMode mode) ...@@ -4602,6 +4603,186 @@ pg_role_aclcheck(Oid role_oid, Oid roleid, AclMode mode)
return ACLCHECK_NO_PRIV; 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) * initialization function (called by InitPostgres)
...@@ -4634,23 +4815,6 @@ RoleMembershipCacheCallback(Datum arg, int cacheid, uint32 hashvalue) ...@@ -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 * Get a list of roles that the specified roleid has the privileges of
* *
...@@ -4697,7 +4861,7 @@ roles_has_privs_of(Oid roleid) ...@@ -4697,7 +4861,7 @@ roles_has_privs_of(Oid roleid)
int i; int i;
/* Ignore non-inheriting roles */ /* Ignore non-inheriting roles */
if (!has_rolinherit(memberid)) if (!has_role_attribute(memberid, ROLE_ATTR_INHERIT))
continue; continue;
/* Find roles that memberid is directly a member of */ /* Find roles that memberid is directly a member of */
......
...@@ -2308,7 +2308,7 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel) ...@@ -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 * bypassrls right or is the table owner of the table(s) involved which
* have RLS enabled. * have RLS enabled.
*/ */
if (!has_bypassrls_privilege(GetUserId()) && if (!have_role_attribute(ROLE_ATTR_BYPASSRLS) &&
((pk_rel->rd_rel->relrowsecurity && ((pk_rel->rd_rel->relrowsecurity &&
!pg_class_ownercheck(pkrte->relid, GetUserId())) || !pg_class_ownercheck(pkrte->relid, GetUserId())) ||
(fk_rel->rd_rel->relrowsecurity && (fk_rel->rd_rel->relrowsecurity &&
......
...@@ -40,6 +40,7 @@ ...@@ -40,6 +40,7 @@
#include "storage/pg_shmem.h" #include "storage/pg_shmem.h"
#include "storage/proc.h" #include "storage/proc.h"
#include "storage/procarray.h" #include "storage/procarray.h"
#include "utils/acl.h"
#include "utils/builtins.h" #include "utils/builtins.h"
#include "utils/guc.h" #include "utils/guc.h"
#include "utils/memutils.h" #include "utils/memutils.h"
...@@ -328,24 +329,6 @@ SetUserIdAndContext(Oid userid, bool sec_def_context) ...@@ -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 * Initialize user identity during normal backend startup
*/ */
...@@ -375,7 +358,7 @@ InitializeSessionUserId(const char *rolename) ...@@ -375,7 +358,7 @@ InitializeSessionUserId(const char *rolename)
roleid = HeapTupleGetOid(roleTup); roleid = HeapTupleGetOid(roleTup);
AuthenticatedUserId = roleid; AuthenticatedUserId = roleid;
AuthenticatedUserIsSuperuser = rform->rolsuper; AuthenticatedUserIsSuperuser = (rform->rolattr & ROLE_ATTR_SUPERUSER);
/* This sets OuterUserId/CurrentUserId too */ /* This sets OuterUserId/CurrentUserId too */
SetSessionUserId(roleid, AuthenticatedUserIsSuperuser); SetSessionUserId(roleid, AuthenticatedUserIsSuperuser);
...@@ -394,7 +377,7 @@ InitializeSessionUserId(const char *rolename) ...@@ -394,7 +377,7 @@ InitializeSessionUserId(const char *rolename)
/* /*
* Is role allowed to login at all? * Is role allowed to login at all?
*/ */
if (!rform->rolcanlogin) if (!(rform->rolattr & ROLE_ATTR_CANLOGIN))
ereport(FATAL, ereport(FATAL,
(errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION), (errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION),
errmsg("role \"%s\" is not permitted to log in", errmsg("role \"%s\" is not permitted to log in",
......
...@@ -762,7 +762,7 @@ InitPostgres(const char *in_dbname, Oid dboid, const char *username, ...@@ -762,7 +762,7 @@ InitPostgres(const char *in_dbname, Oid dboid, const char *username,
{ {
Assert(!bootstrap); Assert(!bootstrap);
if (!superuser() && !has_rolreplication(GetUserId())) if (!have_role_attribute(ROLE_ATTR_REPLICATION))
ereport(FATAL, ereport(FATAL,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("must be superuser or replication role to start walsender"))); errmsg("must be superuser or replication role to start walsender")));
......
...@@ -58,6 +58,7 @@ superuser_arg(Oid roleid) ...@@ -58,6 +58,7 @@ superuser_arg(Oid roleid)
{ {
bool result; bool result;
HeapTuple rtup; HeapTuple rtup;
RoleAttr attributes;
/* Quick out for cache hit */ /* Quick out for cache hit */
if (OidIsValid(last_roleid) && last_roleid == roleid) if (OidIsValid(last_roleid) && last_roleid == roleid)
...@@ -71,7 +72,8 @@ superuser_arg(Oid roleid) ...@@ -71,7 +72,8 @@ superuser_arg(Oid roleid)
rtup = SearchSysCache1(AUTHOID, ObjectIdGetDatum(roleid)); rtup = SearchSysCache1(AUTHOID, ObjectIdGetDatum(roleid));
if (HeapTupleIsValid(rtup)) if (HeapTupleIsValid(rtup))
{ {
result = ((Form_pg_authid) GETSTRUCT(rtup))->rolsuper; attributes = ((Form_pg_authid) GETSTRUCT(rtup))->rolattr;
result = (attributes & ROLE_ATTR_SUPERUSER);
ReleaseSysCache(rtup); ReleaseSysCache(rtup);
} }
else else
......
...@@ -671,10 +671,16 @@ dumpRoles(PGconn *conn) ...@@ -671,10 +671,16 @@ dumpRoles(PGconn *conn)
/* note: rolconfig is dumped later */ /* note: rolconfig is dumped later */
if (server_version >= 90500) if (server_version >= 90500)
printfPQExpBuffer(buf, printfPQExpBuffer(buf,
"SELECT oid, rolname, rolsuper, rolinherit, " "SELECT oid, rolname, "
"rolcreaterole, rolcreatedb, " "pg_check_role_attribute(oid, 'SUPERUSER') AS rolsuper, "
"rolcanlogin, rolconnlimit, rolpassword, " "pg_check_role_attribute(oid, 'INHERIT') AS rolinherit, "
"rolvaliduntil, rolreplication, rolbypassrls, " "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, " "pg_catalog.shobj_description(oid, 'pg_authid') as rolcomment, "
"rolname = current_user AS is_current_user " "rolname = current_user AS is_current_user "
"FROM pg_authid " "FROM pg_authid "
......
/*-------------------------------------------------------------------------
*
* 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<<x */
#define ACL_NO_RIGHTS 0
/* Currently, SELECT ... FOR [KEY] UPDATE/SHARE requires UPDATE privileges */
#define ACL_SELECT_FOR_UPDATE ACL_UPDATE
#define ACL_ID_PUBLIC 0 /* placeholder for id in a PUBLIC acl item */
/*
* 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.
*
* Note about ROLE_ATTR_ALL: This symbol is used verbatim by genbki.pl, which
* means we need to hard-code its value instead of using a symbolic definition.
* Therefore, whenever role attributes are changed, this value MUST be updated
* manually.
*/
/* A bitmask for role attributes */
typedef uint64 RoleAttr;
#define ROLE_ATTR_NONE 0
#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<<x */
#define ROLE_ATTR_ALL 255 /* (1 << N_ROLE_ATTRIBUTES) - 1 */
#endif /* ACLDEFS_H */
...@@ -53,6 +53,6 @@ ...@@ -53,6 +53,6 @@
*/ */
/* yyyymmddN */ /* yyyymmddN */
#define CATALOG_VERSION_NO 201412191 #define CATALOG_VERSION_NO 201412232
#endif #endif
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
#ifndef PG_AUTHID_H #ifndef PG_AUTHID_H
#define PG_AUTHID_H #define PG_AUTHID_H
#include "catalog/acldefs.h"
#include "catalog/genbki.h" #include "catalog/genbki.h"
/* /*
...@@ -45,16 +46,8 @@ ...@@ -45,16 +46,8 @@
CATALOG(pg_authid,1260) BKI_SHARED_RELATION BKI_ROWTYPE_OID(2842) BKI_SCHEMA_MACRO CATALOG(pg_authid,1260) BKI_SHARED_RELATION BKI_ROWTYPE_OID(2842) BKI_SCHEMA_MACRO
{ {
NameData rolname; /* name of role */ NameData rolname; /* name of role */
bool rolsuper; /* read this field via superuser() only! */ int64 rolattr; /* role attribute bitmask */
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) */ int32 rolconnlimit; /* max connections allowed (-1=no limit) */
/* remaining fields may be null; use heap_getattr to read them! */ /* remaining fields may be null; use heap_getattr to read them! */
text rolpassword; /* password, if any */ text rolpassword; /* password, if any */
timestamptz rolvaliduntil; /* password expiration time, if any */ timestamptz rolvaliduntil; /* password expiration time, if any */
...@@ -74,28 +67,25 @@ typedef FormData_pg_authid *Form_pg_authid; ...@@ -74,28 +67,25 @@ typedef FormData_pg_authid *Form_pg_authid;
* compiler constants for pg_authid * compiler constants for pg_authid
* ---------------- * ----------------
*/ */
#define Natts_pg_authid 12 #define Natts_pg_authid 5
#define Anum_pg_authid_rolname 1 #define Anum_pg_authid_rolname 1
#define Anum_pg_authid_rolsuper 2 #define Anum_pg_authid_rolattr 2
#define Anum_pg_authid_rolinherit 3 #define Anum_pg_authid_rolconnlimit 3
#define Anum_pg_authid_rolcreaterole 4 #define Anum_pg_authid_rolpassword 4
#define Anum_pg_authid_rolcreatedb 5 #define Anum_pg_authid_rolvaliduntil 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 * initial contents of pg_authid
* *
* The uppercase quantities will be replaced at initdb time with * The uppercase quantities will be replaced at initdb time with
* user choices. * user choices.
*
* PGROLATTRALL is substituted by genbki.pl to use the value defined by
* ROLE_ATTR_ALL.
* ---------------- * ----------------
*/ */
DATA(insert OID = 10 ( "POSTGRES" t t t t t t t t -1 _null_ _null_)); DATA(insert OID = 10 ( "POSTGRES" PGROLATTRALL -1 _null_ _null_));
#define BOOTSTRAP_SUPERUSERID 10 #define BOOTSTRAP_SUPERUSERID 10
......
...@@ -5136,6 +5136,19 @@ DESCR("rank of hypothetical row without gaps"); ...@@ -5136,6 +5136,19 @@ DESCR("rank of hypothetical row without gaps");
DATA(insert OID = 3993 ( dense_rank_final PGNSP PGUID 12 1 0 2276 0 f f f f f f i 2 0 20 "2281 2276" "{2281,2276}" "{i,v}" _null_ _null_ hypothetical_dense_rank_final _null_ _null_ _null_ )); DATA(insert OID = 3993 ( dense_rank_final PGNSP PGUID 12 1 0 2276 0 f f f f f f i 2 0 20 "2281 2276" "{2281,2276}" "{i,v}" _null_ _null_ hypothetical_dense_rank_final _null_ _null_ _null_ ));
DESCR("aggregate final function"); DESCR("aggregate final function");
/* role attribute support functions */
DATA(insert OID = 3994 ( pg_has_role_attribute PGNSP PGUID 12 1 0 0 0 f f f f t f s 2 0 16 "26 25" _null_ _null_ _null_ _null_ pg_has_role_attribute_id _null_ _null_ _null_ ));
DESCR("check role attribute by role oid with superuser bypass check");
DATA(insert OID = 3995 ( pg_has_role_attribute PGNSP PGUID 12 1 0 0 0 f f f f t f s 2 0 16 "19 25" _null_ _null_ _null_ _null_ pg_has_role_attribute_name _null_ _null_ _null_ ));
DESCR("check role attribute by role name with superuser bypass check");
DATA(insert OID = 3996 ( pg_check_role_attribute PGNSP PGUID 12 1 0 0 0 f f f f t f s 2 0 16 "26 25" _null_ _null_ _null_ _null_ pg_check_role_attribute_id _null_ _null_ _null_ ));
DESCR("check role attribute by role id");
DATA(insert OID = 3997 ( pg_check_role_attribute PGNSP PGUID 12 1 0 0 0 f f f f t f s 2 0 16 "19 25" _null_ _null_ _null_ _null_ pg_check_role_attribute_name _null_ _null_ _null_ ));
DESCR("check role attribute by role name");
DATA(insert OID = 3998 ( pg_check_role_attribute PGNSP PGUID 12 1 0 0 0 f f f f t f s 2 0 16 "20 25" _null_ _null_ _null_ _null_ pg_check_role_attribute_attrs _null_ _null_ _null_ ));
DESCR("check role attribute");
DATA(insert OID = 3999 ( pg_all_role_attributes PGNSP PGUID 12 10 0 0 0 f f f f t f s 1 0 1009 "20" _null_ _null_ _null_ _null_ pg_all_role_attributes _null_ _null_ _null_));
DESCR("convert role attributes to string array");
/* /*
* Symbolic values for provolatile column: these indicate whether the result * Symbolic values for provolatile column: these indicate whether the result
......
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
#include "nodes/bitmapset.h" #include "nodes/bitmapset.h"
#include "nodes/primnodes.h" #include "nodes/primnodes.h"
#include "nodes/value.h" #include "nodes/value.h"
#include "catalog/acldefs.h"
#include "utils/lockwaitpolicy.h" #include "utils/lockwaitpolicy.h"
/* Possible sources of a Query */ /* Possible sources of a Query */
...@@ -51,33 +52,6 @@ typedef enum SortByNulls ...@@ -51,33 +52,6 @@ typedef enum SortByNulls
SORTBY_NULLS_LAST SORTBY_NULLS_LAST
} SortByNulls; } SortByNulls;
/*
* 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<<x */
#define ACL_NO_RIGHTS 0
/* Currently, SELECT ... FOR [KEY] UPDATE/SHARE requires UPDATE privileges */
#define ACL_SELECT_FOR_UPDATE ACL_UPDATE
/***************************************************************************** /*****************************************************************************
* Query Tree * Query Tree
......
...@@ -29,13 +29,6 @@ ...@@ -29,13 +29,6 @@
#include "utils/snapshot.h" #include "utils/snapshot.h"
/*
* typedef AclMode is declared in parsenodes.h, also the individual privilege
* bit meanings are defined there
*/
#define ACL_ID_PUBLIC 0 /* placeholder for id in a PUBLIC acl item */
/* /*
* AclItem * AclItem
* *
...@@ -326,7 +319,10 @@ extern bool pg_foreign_data_wrapper_ownercheck(Oid srv_oid, Oid roleid); ...@@ -326,7 +319,10 @@ extern bool pg_foreign_data_wrapper_ownercheck(Oid srv_oid, Oid roleid);
extern bool pg_foreign_server_ownercheck(Oid srv_oid, Oid roleid); extern bool pg_foreign_server_ownercheck(Oid srv_oid, Oid roleid);
extern bool pg_event_trigger_ownercheck(Oid et_oid, Oid roleid); extern bool pg_event_trigger_ownercheck(Oid et_oid, Oid roleid);
extern bool pg_extension_ownercheck(Oid ext_oid, Oid roleid); extern bool pg_extension_ownercheck(Oid ext_oid, Oid roleid);
extern bool has_createrole_privilege(Oid roleid);
extern bool has_bypassrls_privilege(Oid roleid); /* role attribute check routines */
extern bool has_role_attribute(Oid roleid, RoleAttr attribute);
extern bool have_role_attribute(RoleAttr attribute);
extern bool check_role_attribute(Oid roleid, RoleAttr attribute);
#endif /* ACL_H */ #endif /* ACL_H */
...@@ -106,6 +106,12 @@ extern Datum pg_has_role_id_name(PG_FUNCTION_ARGS); ...@@ -106,6 +106,12 @@ extern Datum pg_has_role_id_name(PG_FUNCTION_ARGS);
extern Datum pg_has_role_id_id(PG_FUNCTION_ARGS); extern Datum pg_has_role_id_id(PG_FUNCTION_ARGS);
extern Datum pg_has_role_name(PG_FUNCTION_ARGS); extern Datum pg_has_role_name(PG_FUNCTION_ARGS);
extern Datum pg_has_role_id(PG_FUNCTION_ARGS); extern Datum pg_has_role_id(PG_FUNCTION_ARGS);
extern Datum pg_has_role_attribute_id(PG_FUNCTION_ARGS);
extern Datum pg_has_role_attribute_name(PG_FUNCTION_ARGS);
extern Datum pg_check_role_attribute_id(PG_FUNCTION_ARGS);
extern Datum pg_check_role_attribute_name(PG_FUNCTION_ARGS);
extern Datum pg_check_role_attribute_attrs(PG_FUNCTION_ARGS);
extern Datum pg_all_role_attributes(PG_FUNCTION_ARGS);
/* bool.c */ /* bool.c */
extern Datum boolin(PG_FUNCTION_ARGS); extern Datum boolin(PG_FUNCTION_ARGS);
......
...@@ -1314,7 +1314,7 @@ pg_group| SELECT pg_authid.rolname AS groname, ...@@ -1314,7 +1314,7 @@ pg_group| SELECT pg_authid.rolname AS groname,
FROM pg_auth_members FROM pg_auth_members
WHERE (pg_auth_members.roleid = pg_authid.oid)) AS grolist WHERE (pg_auth_members.roleid = pg_authid.oid)) AS grolist
FROM pg_authid FROM pg_authid
WHERE (NOT pg_authid.rolcanlogin); WHERE (NOT pg_check_role_attribute(pg_authid.rolattr, 'CANLOGIN'::text));
pg_indexes| SELECT n.nspname AS schemaname, pg_indexes| SELECT n.nspname AS schemaname,
c.relname AS tablename, c.relname AS tablename,
i.relname AS indexname, i.relname AS indexname,
...@@ -1405,17 +1405,17 @@ pg_replication_slots| SELECT l.slot_name, ...@@ -1405,17 +1405,17 @@ pg_replication_slots| SELECT l.slot_name,
FROM (pg_get_replication_slots() l(slot_name, plugin, slot_type, datoid, active, xmin, catalog_xmin, restart_lsn) FROM (pg_get_replication_slots() l(slot_name, plugin, slot_type, datoid, active, xmin, catalog_xmin, restart_lsn)
LEFT JOIN pg_database d ON ((l.datoid = d.oid))); LEFT JOIN pg_database d ON ((l.datoid = d.oid)));
pg_roles| SELECT pg_authid.rolname, pg_roles| SELECT pg_authid.rolname,
pg_authid.rolsuper, pg_check_role_attribute(pg_authid.rolattr, 'SUPERUSER'::text) AS rolsuper,
pg_authid.rolinherit, pg_check_role_attribute(pg_authid.rolattr, 'INHERIT'::text) AS rolinherit,
pg_authid.rolcreaterole, pg_check_role_attribute(pg_authid.rolattr, 'CREATEROLE'::text) AS rolcreaterole,
pg_authid.rolcreatedb, pg_check_role_attribute(pg_authid.rolattr, 'CREATEDB'::text) AS rolcreatedb,
pg_authid.rolcatupdate, pg_check_role_attribute(pg_authid.rolattr, 'CATUPDATE'::text) AS rolcatupdate,
pg_authid.rolcanlogin, pg_check_role_attribute(pg_authid.rolattr, 'CANLOGIN'::text) AS rolcanlogin,
pg_authid.rolreplication, pg_check_role_attribute(pg_authid.rolattr, 'REPLICATION'::text) AS rolreplication,
pg_check_role_attribute(pg_authid.rolattr, 'BYPASSRLS'::text) AS rolbypassrls,
pg_authid.rolconnlimit, pg_authid.rolconnlimit,
'********'::text AS rolpassword, '********'::text AS rolpassword,
pg_authid.rolvaliduntil, pg_authid.rolvaliduntil,
pg_authid.rolbypassrls,
s.setconfig AS rolconfig, s.setconfig AS rolconfig,
pg_authid.oid pg_authid.oid
FROM (pg_authid FROM (pg_authid
...@@ -1608,16 +1608,16 @@ pg_settings| SELECT a.name, ...@@ -1608,16 +1608,16 @@ pg_settings| SELECT a.name,
FROM pg_show_all_settings() a(name, setting, unit, category, short_desc, extra_desc, context, vartype, source, min_val, max_val, enumvals, boot_val, reset_val, sourcefile, sourceline); FROM pg_show_all_settings() a(name, setting, unit, category, short_desc, extra_desc, context, vartype, source, min_val, max_val, enumvals, boot_val, reset_val, sourcefile, sourceline);
pg_shadow| SELECT pg_authid.rolname AS usename, pg_shadow| SELECT pg_authid.rolname AS usename,
pg_authid.oid AS usesysid, pg_authid.oid AS usesysid,
pg_authid.rolcreatedb AS usecreatedb, pg_check_role_attribute(pg_authid.rolattr, 'CREATEDB'::text) AS usecreatedb,
pg_authid.rolsuper AS usesuper, pg_check_role_attribute(pg_authid.rolattr, 'SUPERUSER'::text) AS usesuper,
pg_authid.rolcatupdate AS usecatupd, pg_check_role_attribute(pg_authid.rolattr, 'CATUPDATE'::text) AS usecatupd,
pg_authid.rolreplication AS userepl, pg_check_role_attribute(pg_authid.rolattr, 'REPLICATION'::text) AS userepl,
pg_authid.rolpassword AS passwd, pg_authid.rolpassword AS passwd,
(pg_authid.rolvaliduntil)::abstime AS valuntil, (pg_authid.rolvaliduntil)::abstime AS valuntil,
s.setconfig AS useconfig s.setconfig AS useconfig
FROM (pg_authid FROM (pg_authid
LEFT JOIN pg_db_role_setting s ON (((pg_authid.oid = s.setrole) AND (s.setdatabase = (0)::oid)))) LEFT JOIN pg_db_role_setting s ON (((pg_authid.oid = s.setrole) AND (s.setdatabase = (0)::oid))))
WHERE pg_authid.rolcanlogin; WHERE pg_check_role_attribute(pg_authid.rolattr, 'CANLOGIN'::text);
pg_stat_activity| SELECT s.datid, pg_stat_activity| SELECT s.datid,
d.datname, d.datname,
s.pid, s.pid,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment