]> granicus.if.org Git - postgresql/commitdiff
Use a bitmask to represent role attributes
authorAlvaro Herrera <alvherre@alvh.no-ip.org>
Tue, 23 Dec 2014 13:22:09 +0000 (10:22 -0300)
committerAlvaro Herrera <alvherre@alvh.no-ip.org>
Tue, 23 Dec 2014 13:22:09 +0000 (10:22 -0300)
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

30 files changed:
doc/src/sgml/catalogs.sgml
doc/src/sgml/func.sgml
src/backend/access/transam/xlogfuncs.c
src/backend/catalog/Catalog.pm
src/backend/catalog/Makefile
src/backend/catalog/aclchk.c
src/backend/catalog/genbki.pl
src/backend/catalog/information_schema.sql
src/backend/catalog/objectaddress.c
src/backend/catalog/system_views.sql
src/backend/commands/dbcommands.c
src/backend/commands/user.c
src/backend/commands/variable.c
src/backend/replication/logical/logicalfuncs.c
src/backend/replication/slotfuncs.c
src/backend/rewrite/rowsecurity.c
src/backend/utils/adt/acl.c
src/backend/utils/adt/ri_triggers.c
src/backend/utils/init/miscinit.c
src/backend/utils/init/postinit.c
src/backend/utils/misc/superuser.c
src/bin/pg_dump/pg_dumpall.c
src/include/catalog/acldefs.h [new file with mode: 0644]
src/include/catalog/catversion.h
src/include/catalog/pg_authid.h
src/include/catalog/pg_proc.h
src/include/nodes/parsenodes.h
src/include/utils/acl.h
src/include/utils/builtins.h
src/test/regress/expected/rules.out

index 9ceb96b54c74bc090373050e069a25897432e458..947091627fd9bc7246ea0e175215040cf80da9ee 100644 (file)
      </row>
 
      <row>
-      <entry><structfield>rolsuper</structfield></entry>
-      <entry><type>bool</type></entry>
+      <entry><structfield>rolattr</structfield></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><literal>0</literal></entry>
      </row>
 
      <row>
-      <entry><structfield>rolinherit</structfield></entry>
-      <entry><type>bool</type></entry>
-      <entry>Role automatically inherits privileges of roles it is a
-       member of</entry>
+      <entry>Inherit</entry>
+      <entry>INHERIT</entry>
+      <entry>
+       Role automatically inherits privileges of roles it is a member of
+      </entry>
+      <entry><literal>1</literal></entry>
      </row>
 
      <row>
-      <entry><structfield>rolcreaterole</structfield></entry>
-      <entry><type>bool</type></entry>
+      <entry>Create Role</entry>
+      <entry>CREATEROLE</entry>
       <entry>Role can create more roles</entry>
+      <entry><literal>2</literal></entry>
      </row>
 
      <row>
-      <entry><structfield>rolcreatedb</structfield></entry>
-      <entry><type>bool</type></entry>
+      <entry>Create DB</entry>
+      <entry>CREATEDB</entry>
       <entry>Role can create databases</entry>
+      <entry><literal>3</literal></entry>
      </row>
 
      <row>
-      <entry><structfield>rolcatupdate</structfield></entry>
-      <entry><type>bool</type></entry>
+      <entry>Catalog Update</entry>
+      <entry>CATUPDATE</entry>
       <entry>
        Role can update system catalogs directly.  (Even a superuser cannot do
        this unless this column is true)
       </entry>
+      <entry><literal>4</literal></entry>
      </row>
 
      <row>
-      <entry><structfield>rolcanlogin</structfield></entry>
-      <entry><type>bool</type></entry>
+      <entry>Can Login</entry>
+      <entry>LOGIN</entry>
       <entry>
        Role can log in. That is, this role can be given as the initial
        session authorization identifier
       </entry>
+      <entry><literal>5</literal></entry>
      </row>
 
      <row>
-      <entry><structfield>rolreplication</structfield></entry>
-      <entry><type>bool</type></entry>
+      <entry>Replication</entry>
+      <entry>REPLICATION</entry>
       <entry>
        Role is a replication role. That is, this role can initiate streaming
        replication (see <xref linkend="streaming-replication">) and set/unset
        the system backup mode using <function>pg_start_backup</> and
        <function>pg_stop_backup</>
       </entry>
+      <entry><literal>6</literal></entry>
      </row>
 
      <row>
-      <entry><structfield>rolconnlimit</structfield></entry>
-      <entry><type>int4</type></entry>
+      <entry>Bypass Row Level Security</entry>
+      <entry>BYPASSRLS</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.
+       Role can bypass row level security policies when <literal>row_security</>
+       is set <literal>off</>
       </entry>
+      <entry><literal>7</literal></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>
index 24c64b7187f2fa5f27612528608cb1eead714cff..2a37e65eb9a049eaa40febd1e06eafac21887742 100644 (file)
@@ -15139,6 +15139,133 @@ SELECT has_function_privilege('joeuser', 'myfunc(int, text)', 'execute');
     are immediately available without doing <command>SET ROLE</>.
    </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>
    <xref linkend="functions-info-schema-table"> shows functions that
    determine whether a certain object is <firstterm>visible</> in the
index 133143db9293ad5b592a19fec0b26e6a62f8c214..3181a79e3eda63606ce4efe723996ff4a1e4aded 100644 (file)
@@ -27,6 +27,7 @@
 #include "miscadmin.h"
 #include "replication/walreceiver.h"
 #include "storage/smgr.h"
+#include "utils/acl.h"
 #include "utils/builtins.h"
 #include "utils/numeric.h"
 #include "utils/guc.h"
@@ -54,7 +55,7 @@ pg_start_backup(PG_FUNCTION_ARGS)
 
        backupidstr = text_to_cstring(backupid);
 
-       if (!superuser() && !has_rolreplication(GetUserId()))
+       if (!have_role_attribute(ROLE_ATTR_REPLICATION))
                ereport(ERROR,
                                (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
                   errmsg("must be superuser or replication role to run a backup")));
@@ -82,7 +83,7 @@ pg_stop_backup(PG_FUNCTION_ARGS)
 {
        XLogRecPtr      stoppoint;
 
-       if (!superuser() && !has_rolreplication(GetUserId()))
+       if (!have_role_attribute(ROLE_ATTR_REPLICATION))
                ereport(ERROR,
                                (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
                 (errmsg("must be superuser or replication role to run a backup"))));
index 523b37995dbef8b689bcda1d47f62959c1a273df..fd52f48f344b5818fc1258f80e7fc1ab4ecca779 100644 (file)
@@ -176,7 +176,7 @@ sub Catalogs
                                }
                        }
                }
-               $catalogs{$catname} = \%catalog;
+               $catalogs{$catname} = \%catalog if defined $catname;
                close INPUT_FILE;
        }
        return \%catalogs;
index a403c643600b82201ff6e8f0f7e310db6054839a..a6de2ff054f82fa45b9af272f9d1823ae34e496e 100644 (file)
@@ -28,7 +28,7 @@ all: $(BKIFILES) schemapg.h
 # indexing.h had better be last, and toasting.h just before it.
 
 POSTGRES_BKI_SRCS = $(addprefix $(top_srcdir)/src/include/catalog/,\
-       pg_proc.h pg_type.h pg_attribute.h pg_class.h \
+       acldefs.h pg_proc.h pg_type.h pg_attribute.h pg_class.h \
        pg_attrdef.h pg_constraint.h pg_inherits.h pg_index.h pg_operator.h \
        pg_opfamily.h pg_opclass.h pg_am.h pg_amop.h pg_amproc.h \
        pg_language.h pg_largeobject_metadata.h pg_largeobject.h pg_aggregate.h \
index d30612c4d9abbd90dce86b323f3dfbfac50aa034..cd6073935287c6e77e153deba0ce5e66cb057f08 100644 (file)
@@ -3423,26 +3423,6 @@ aclcheck_error_type(AclResult aclerr, Oid typeOid)
 }
 
 
-/* Check if given user has rolcatupdate privilege according to pg_authid */
-static bool
-has_rolcatupdate(Oid roleid)
-{
-       bool            rolcatupdate;
-       HeapTuple       tuple;
-
-       tuple = SearchSysCache1(AUTHOID, ObjectIdGetDatum(roleid));
-       if (!HeapTupleIsValid(tuple))
-               ereport(ERROR,
-                               (errcode(ERRCODE_UNDEFINED_OBJECT),
-                                errmsg("role with OID %u does not exist", roleid)));
-
-       rolcatupdate = ((Form_pg_authid) GETSTRUCT(tuple))->rolcatupdate;
-
-       ReleaseSysCache(tuple);
-
-       return rolcatupdate;
-}
-
 /*
  * Relay for the various pg_*_mask routines depending on object kind
  */
@@ -3630,7 +3610,7 @@ pg_class_aclmask(Oid table_oid, Oid roleid,
        if ((mask & (ACL_INSERT | ACL_UPDATE | ACL_DELETE | ACL_TRUNCATE | ACL_USAGE)) &&
                IsSystemClass(table_oid, classForm) &&
                classForm->relkind != RELKIND_VIEW &&
-               !has_rolcatupdate(roleid) &&
+               !has_role_attribute(roleid, ROLE_ATTR_CATUPDATE) &&
                !allowSystemTableMods)
        {
 #ifdef ACLDEBUG
@@ -5051,52 +5031,87 @@ pg_extension_ownercheck(Oid ext_oid, Oid roleid)
 }
 
 /*
- * Check whether specified role has CREATEROLE privilege (or is a superuser)
+ * has_role_attribute
+ *   Check if the role with the specified id has been assigned a specific role
+ *   attribute.
+ *
+ * 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
- * 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.)
+ * Note: Use this function for role attribute permission checking as it
+ * accounts for superuser status.  It will always return true for roles with
+ * superuser privileges unless the attribute being checked is CATUPDATE
+ * (superusers are not allowed to bypass CATUPDATE permissions).
+ *
+ * Note: roles do not have owners per se; instead we use this test in places
+ * where an ownership-like permissions test is needed for a role.  Be sure to
+ * apply it to the role trying to do the operation, not the role being operated
+ * on!  Also note that this generally should not be considered enough privilege
+ * if the target role is a superuser.  (We don't handle that consideration here
+ * because we want to give a separate error message for such cases, so the
+ * caller has to deal with it.)
  */
 bool
-has_createrole_privilege(Oid roleid)
+has_role_attribute(Oid roleid, RoleAttr attribute)
 {
-       bool            result = false;
-       HeapTuple       utup;
-
-       /* Superusers bypass all permission checking. */
-       if (superuser_arg(roleid))
+       /*
+        * Superusers bypass all permission checking except in the case of CATUPDATE
+        */
+       if (!(attribute & ROLE_ATTR_CATUPDATE) && superuser_arg(roleid))
                return true;
 
-       utup = SearchSysCache1(AUTHOID, ObjectIdGetDatum(roleid));
-       if (HeapTupleIsValid(utup))
-       {
-               result = ((Form_pg_authid) GETSTRUCT(utup))->rolcreaterole;
-               ReleaseSysCache(utup);
-       }
-       return result;
+       return check_role_attribute(roleid, attribute);
+}
+
+/*
+ * have_role_attribute
+ *   Convenience function for checking if the role id returned by GetUserId()
+ *   has been assigned a specific role attribute.
+ *
+ * attribute - the attribute to check.
+ */
+bool
+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
-has_bypassrls_privilege(Oid roleid)
+check_role_attribute(Oid roleid, RoleAttr attribute)
 {
-       bool            result = false;
-       HeapTuple       utup;
+       RoleAttr        attributes;
+       HeapTuple       tuple;
 
-       /* Superusers bypass all permission checking. */
-       if (superuser_arg(roleid))
-               return true;
+       /* ROLE_ATTR_NONE (zero) is not a valid attribute */
+       Assert(attribute != ROLE_ATTR_NONE);
 
-       utup = SearchSysCache1(AUTHOID, ObjectIdGetDatum(roleid));
-       if (HeapTupleIsValid(utup))
-       {
-               result = ((Form_pg_authid) GETSTRUCT(utup))->rolbypassrls;
-               ReleaseSysCache(utup);
-       }
-       return result;
+       /* Check that only one bit is set in 'attribute' */
+       Assert(!(attribute & (attribute - 1)));
+
+       tuple = SearchSysCache1(AUTHOID, ObjectIdGetDatum(roleid));
+
+       if (!HeapTupleIsValid(tuple))
+               ereport(ERROR,
+                               (errcode(ERRCODE_UNDEFINED_OBJECT),
+                                errmsg("role with OID %u does not exist", roleid)));
+
+       attributes = ((Form_pg_authid) GETSTRUCT(tuple))->rolattr;
+       ReleaseSysCache(tuple);
+
+       return (attributes & attribute);
 }
 
 /*
index ca8987907c6d4dc88e9a888ca08f59ff3bf3db48..415ac17a7bfcc72012575d52e6abd3bee31529a2 100644 (file)
@@ -90,6 +90,8 @@ my $BOOTSTRAP_SUPERUSERID =
   find_defined_symbol('pg_authid.h', 'BOOTSTRAP_SUPERUSERID');
 my $PG_CATALOG_NAMESPACE =
   find_defined_symbol('pg_namespace.h', 'PG_CATALOG_NAMESPACE');
+my $ROLE_ATTR_ALL =
+  find_defined_symbol('acldefs.h', 'ROLE_ATTR_ALL');
 
 # Read all the input header files into internal data structures
 my $catalogs = Catalog::Catalogs(@input_files);
@@ -144,6 +146,7 @@ foreach my $catname (@{ $catalogs->{names} })
                        # substitute constant values we acquired above
                        $row->{bki_values} =~ s/\bPGUID\b/$BOOTSTRAP_SUPERUSERID/g;
                        $row->{bki_values} =~ s/\bPGNSP\b/$PG_CATALOG_NAMESPACE/g;
+                       $row->{bki_values} =~ s/\bPGROLATTRALL/$ROLE_ATTR_ALL/g;
 
                        # Save pg_type info for pg_attribute processing below
                        if ($catname eq 'pg_type')
index a036c62a3350a490eec5462da30e8fe840087775..87b6d8ce9cec3fd114da4aa470b2fe06452b8a08 100644 (file)
@@ -2884,7 +2884,12 @@ CREATE VIEW user_mapping_options AS
            CAST((pg_options_to_table(um.umoptions)).option_name AS sql_identifier) AS option_name,
            CAST(CASE WHEN (umuser <> 0 AND authorization_identifier = current_user)
                        OR (umuser = 0 AND pg_has_role(srvowner, 'USAGE'))
-                       OR (SELECT rolsuper FROM pg_authid WHERE rolname = current_user) THEN (pg_options_to_table(um.umoptions)).option_value
+                       OR (
+                            SELECT pg_check_role_attribute(pg_authid.rolattr, 'SUPERUSER') AS rolsuper
+                            FROM pg_authid
+                            WHERE rolname = current_user
+                          )
+                       THEN (pg_options_to_table(um.umoptions)).option_value
                      ELSE NULL END AS character_data) AS option_value
     FROM _pg_user_mappings um;
 
index 297deb5f3f07bac2ffaa292a012f7c1ea38c6c6b..3a4245d5dcc397154061747249ac6ec8fb2ef9a1 100644 (file)
@@ -1328,7 +1328,7 @@ check_object_ownership(Oid roleid, ObjectType objtype, ObjectAddress address,
                        }
                        else
                        {
-                               if (!has_createrole_privilege(roleid))
+                               if (!has_role_attribute(roleid, ROLE_ATTR_CREATEROLE))
                                        ereport(ERROR,
                                                        (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
                                                         errmsg("must have CREATEROLE privilege")));
index 22b8ceef622603cd4eae9a334cc8305b9e3dad6a..ae93832215b5e6785f2b12d5f03d17957be70d5b 100644 (file)
@@ -9,17 +9,17 @@
 CREATE VIEW pg_roles AS
     SELECT
         rolname,
-        rolsuper,
-        rolinherit,
-        rolcreaterole,
-        rolcreatedb,
-        rolcatupdate,
-        rolcanlogin,
-        rolreplication,
+        pg_check_role_attribute(pg_authid.rolattr, 'SUPERUSER') AS rolsuper,
+        pg_check_role_attribute(pg_authid.rolattr, 'INHERIT') AS rolinherit,
+        pg_check_role_attribute(pg_authid.rolattr, 'CREATEROLE') AS rolcreaterole,
+        pg_check_role_attribute(pg_authid.rolattr, 'CREATEDB') AS rolcreatedb,
+        pg_check_role_attribute(pg_authid.rolattr, 'CATUPDATE') AS rolcatupdate,
+        pg_check_role_attribute(pg_authid.rolattr, 'CANLOGIN') AS rolcanlogin,
+        pg_check_role_attribute(pg_authid.rolattr, 'REPLICATION') AS rolreplication,
+        pg_check_role_attribute(pg_authid.rolattr, 'BYPASSRLS') AS rolbypassrls,
         rolconnlimit,
         '********'::text as rolpassword,
         rolvaliduntil,
-        rolbypassrls,
         setconfig as rolconfig,
         pg_authid.oid
     FROM pg_authid LEFT JOIN pg_db_role_setting s
@@ -29,16 +29,16 @@ CREATE VIEW pg_shadow AS
     SELECT
         rolname AS usename,
         pg_authid.oid AS usesysid,
-        rolcreatedb AS usecreatedb,
-        rolsuper AS usesuper,
-        rolcatupdate AS usecatupd,
-        rolreplication AS userepl,
+        pg_check_role_attribute(pg_authid.rolattr, 'CREATEDB') AS usecreatedb,
+        pg_check_role_attribute(pg_authid.rolattr, 'SUPERUSER') AS usesuper,
+        pg_check_role_attribute(pg_authid.rolattr, 'CATUPDATE') AS usecatupd,
+        pg_check_role_attribute(pg_authid.rolattr, 'REPLICATION') AS userepl,
         rolpassword AS passwd,
         rolvaliduntil::abstime AS valuntil,
         setconfig AS useconfig
     FROM pg_authid LEFT JOIN pg_db_role_setting s
     ON (pg_authid.oid = setrole AND setdatabase = 0)
-    WHERE rolcanlogin;
+    WHERE pg_check_role_attribute(pg_authid.rolattr, 'CANLOGIN');
 
 REVOKE ALL on pg_shadow FROM public;
 
@@ -48,7 +48,7 @@ CREATE VIEW pg_group AS
         oid AS grosysid,
         ARRAY(SELECT member FROM pg_auth_members WHERE roleid = oid) AS grolist
     FROM pg_authid
-    WHERE NOT rolcanlogin;
+    WHERE NOT pg_check_role_attribute(pg_authid.rolattr, 'CANLOGIN');
 
 CREATE VIEW pg_user AS
     SELECT
index 1a5244cade20b9331c2785007c3f6a407f122964..c079168c83d4ef7f2aa34de761784d1aa48f375f 100644 (file)
@@ -85,7 +85,6 @@ static bool get_db_info(const char *name, LOCKMODE lockmode,
                        Oid *dbLastSysOidP, TransactionId *dbFrozenXidP,
                        MultiXactId *dbMinMultiP,
                        Oid *dbTablespace, char **dbCollate, char **dbCtype);
-static bool have_createdb_privilege(void);
 static void remove_dbtablespaces(Oid db_id);
 static bool check_db_file_conflict(Oid db_id);
 static int     errdetail_busy_db(int notherbackends, int npreparedxacts);
@@ -291,7 +290,7 @@ createdb(const CreatedbStmt *stmt)
         * "giveaway" attacks.  Note that a superuser will always have both of
         * these privileges a fortiori.
         */
-       if (!have_createdb_privilege())
+       if (!have_role_attribute(ROLE_ATTR_CREATEDB))
                ereport(ERROR,
                                (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
                                 errmsg("permission denied to create database")));
@@ -965,7 +964,7 @@ RenameDatabase(const char *oldname, const char *newname)
                                           oldname);
 
        /* must have createdb rights */
-       if (!have_createdb_privilege())
+       if (!have_role_attribute(ROLE_ATTR_CREATEDB))
                ereport(ERROR,
                                (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
                                 errmsg("permission denied to rename database")));
@@ -1623,7 +1622,7 @@ AlterDatabaseOwner(const char *dbname, Oid newOwnerId)
                 * databases.  Because superusers will always have this right, we need
                 * no special case for them.
                 */
-               if (!have_createdb_privilege())
+               if (!have_role_attribute(ROLE_ATTR_CREATEDB))
                        ereport(ERROR,
                                        (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
                                   errmsg("permission denied to change owner of database")));
@@ -1802,26 +1801,6 @@ get_db_info(const char *name, LOCKMODE lockmode,
        return result;
 }
 
-/* Check if current user has createdb privileges */
-static bool
-have_createdb_privilege(void)
-{
-       bool            result = false;
-       HeapTuple       utup;
-
-       /* Superusers can always do everything */
-       if (superuser())
-               return true;
-
-       utup = SearchSysCache1(AUTHOID, ObjectIdGetDatum(GetUserId()));
-       if (HeapTupleIsValid(utup))
-       {
-               result = ((Form_pg_authid) GETSTRUCT(utup))->rolcreatedb;
-               ReleaseSysCache(utup);
-       }
-       return result;
-}
-
 /*
  * Remove tablespace directories
  *
index 1a73fd85582b4923b8969694acf60504cefd1c8d..564f77a8695d90428cf0a1ff40e3715dfa296267 100644 (file)
@@ -56,14 +56,6 @@ static void DelRoleMems(const char *rolename, Oid roleid,
                        bool admin_opt);
 
 
-/* Check if current user has createrole privileges */
-static bool
-have_createrole_privilege(void)
-{
-       return has_createrole_privilege(GetUserId());
-}
-
-
 /*
  * CREATE ROLE
  */
@@ -81,13 +73,7 @@ CreateRole(CreateRoleStmt *stmt)
        char       *password = NULL;    /* user password */
        bool            encrypt_password = Password_encryption; /* encrypt password? */
        char            encrypted_password[MD5_PASSWD_LEN + 1];
-       bool            issuper = false;        /* Make the user a superuser? */
-       bool            inherit = true; /* Auto inherit privileges? */
-       bool            createrole = false;             /* Can this user create roles? */
-       bool            createdb = false;               /* Can the user create databases? */
-       bool            canlogin = false;               /* Can this user login? */
-       bool            isreplication = false;  /* Is this a replication role? */
-       bool            bypassrls = false;              /* Is this a row security enabled role? */
+       RoleAttr        attributes;
        int                     connlimit = -1; /* maximum connections allowed */
        List       *addroleto = NIL;    /* roles to make this a member of */
        List       *rolemembers = NIL;          /* roles to be members of this role */
@@ -109,13 +95,17 @@ CreateRole(CreateRoleStmt *stmt)
        DefElem    *dvalidUntil = NULL;
        DefElem    *dbypassRLS = NULL;
 
-       /* The defaults can vary depending on the original statement type */
+       /*
+        * Every role has INHERIT by default, and CANLOGIN depends on the statement
+        * type.
+        */
+       attributes = ROLE_ATTR_INHERIT;
        switch (stmt->stmt_type)
        {
                case ROLESTMT_ROLE:
                        break;
                case ROLESTMT_USER:
-                       canlogin = true;
+                       attributes |= ROLE_ATTR_CANLOGIN;
                        /* may eventually want inherit to default to false here */
                        break;
                case ROLESTMT_GROUP:
@@ -249,18 +239,76 @@ CreateRole(CreateRoleStmt *stmt)
 
        if (dpassword && dpassword->arg)
                password = strVal(dpassword->arg);
+
+       /* Set up role attributes and check permissions to set each of them */
        if (dissuper)
-               issuper = intVal(dissuper->arg) != 0;
+       {
+               if (intVal(dissuper->arg) != 0)
+               {
+                       if (!superuser())
+                               ereport(ERROR,
+                                               (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+                                                errmsg("must be superuser to create superusers")));
+                       attributes |= ROLE_ATTR_SUPERUSER;
+               }
+               else
+                       attributes &= ~ROLE_ATTR_SUPERUSER;
+       }
        if (dinherit)
-               inherit = intVal(dinherit->arg) != 0;
+       {
+               if (intVal(dinherit->arg) != 0)
+                       attributes |= ROLE_ATTR_INHERIT;
+               else
+                       attributes &= ~ROLE_ATTR_INHERIT;
+       }
        if (dcreaterole)
-               createrole = intVal(dcreaterole->arg) != 0;
+       {
+               if (intVal(dcreaterole->arg) != 0)
+                       attributes |= ROLE_ATTR_CREATEROLE;
+               else
+                       attributes &= ~ROLE_ATTR_CREATEROLE;
+       }
        if (dcreatedb)
-               createdb = intVal(dcreatedb->arg) != 0;
+       {
+               if (intVal(dcreatedb->arg) != 0)
+                       attributes |= ROLE_ATTR_CREATEDB;
+               else
+                       attributes &= ~ROLE_ATTR_CREATEDB;
+       }
        if (dcanlogin)
-               canlogin = intVal(dcanlogin->arg) != 0;
+       {
+               if (intVal(dcanlogin->arg) != 0)
+                       attributes |= ROLE_ATTR_CANLOGIN;
+               else
+                       attributes &= ~ROLE_ATTR_CANLOGIN;
+       }
        if (disreplication)
-               isreplication = intVal(disreplication->arg) != 0;
+       {
+               if (intVal(disreplication->arg) != 0)
+               {
+                       if (!superuser())
+                               ereport(ERROR,
+                                               (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+                                                errmsg("must be superuser to create replication users")));
+                       attributes |= ROLE_ATTR_REPLICATION;
+               }
+               else
+                       attributes &= ~ROLE_ATTR_REPLICATION;
+       }
+       if (dbypassRLS)
+       {
+               if (intVal(dbypassRLS->arg) != 0)
+               {
+                       if (!superuser())
+                               ereport(ERROR,
+                                               (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+                                                errmsg("must be superuser to change bypassrls attribute")));
+                       attributes |= ROLE_ATTR_BYPASSRLS;
+               }
+               else
+                       attributes &= ~ROLE_ATTR_BYPASSRLS;
+       }
+
        if (dconnlimit)
        {
                connlimit = intVal(dconnlimit->arg);
@@ -277,38 +325,12 @@ CreateRole(CreateRoleStmt *stmt)
                adminmembers = (List *) dadminmembers->arg;
        if (dvalidUntil)
                validUntil = strVal(dvalidUntil->arg);
-       if (dbypassRLS)
-               bypassrls = intVal(dbypassRLS->arg) != 0;
 
-       /* Check some permissions first */
-       if (issuper)
-       {
-               if (!superuser())
-                       ereport(ERROR,
-                                       (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
-                                        errmsg("must be superuser to create superusers")));
-       }
-       else if (isreplication)
-       {
-               if (!superuser())
-                       ereport(ERROR,
-                                       (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
-                                  errmsg("must be superuser to create replication users")));
-       }
-       else if (bypassrls)
-       {
-               if (!superuser())
-                       ereport(ERROR,
-                                       (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
-                                        errmsg("must be superuser to change bypassrls attribute.")));
-       }
-       else
-       {
-               if (!have_createrole_privilege())
-                       ereport(ERROR,
-                                       (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
-                                        errmsg("permission denied to create role")));
-       }
+       /* Check permissions */
+       if (!have_role_attribute(ROLE_ATTR_CREATEROLE))
+               ereport(ERROR,
+                               (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+                                errmsg("permission denied to create role")));
 
        if (strcmp(stmt->role, "public") == 0 ||
                strcmp(stmt->role, "none") == 0)
@@ -364,14 +386,8 @@ CreateRole(CreateRoleStmt *stmt)
        new_record[Anum_pg_authid_rolname - 1] =
                DirectFunctionCall1(namein, CStringGetDatum(stmt->role));
 
-       new_record[Anum_pg_authid_rolsuper - 1] = BoolGetDatum(issuper);
-       new_record[Anum_pg_authid_rolinherit - 1] = BoolGetDatum(inherit);
-       new_record[Anum_pg_authid_rolcreaterole - 1] = BoolGetDatum(createrole);
-       new_record[Anum_pg_authid_rolcreatedb - 1] = BoolGetDatum(createdb);
-       /* superuser gets catupdate right by default */
-       new_record[Anum_pg_authid_rolcatupdate - 1] = BoolGetDatum(issuper);
-       new_record[Anum_pg_authid_rolcanlogin - 1] = BoolGetDatum(canlogin);
-       new_record[Anum_pg_authid_rolreplication - 1] = BoolGetDatum(isreplication);
+       new_record[Anum_pg_authid_rolattr - 1] = Int64GetDatum(attributes);
+
        new_record[Anum_pg_authid_rolconnlimit - 1] = Int32GetDatum(connlimit);
 
        if (password)
@@ -394,8 +410,6 @@ CreateRole(CreateRoleStmt *stmt)
        new_record[Anum_pg_authid_rolvaliduntil - 1] = validUntil_datum;
        new_record_nulls[Anum_pg_authid_rolvaliduntil - 1] = validUntil_null;
 
-       new_record[Anum_pg_authid_rolbypassrls - 1] = BoolGetDatum(bypassrls);
-
        tuple = heap_form_tuple(pg_authid_dsc, new_record, new_record_nulls);
 
        /*
@@ -508,6 +522,7 @@ AlterRole(AlterRoleStmt *stmt)
        DefElem    *dvalidUntil = NULL;
        DefElem    *dbypassRLS = NULL;
        Oid                     roleid;
+       RoleAttr        attributes;
 
        /* Extract options from the statement node tree */
        foreach(option, stmt->options)
@@ -658,31 +673,34 @@ AlterRole(AlterRoleStmt *stmt)
        roleid = HeapTupleGetOid(tuple);
 
        /*
-        * To mess with a superuser you gotta be superuser; else you need
-        * createrole, or just want to change your own password
+        * To mess with a superuser or a replication user you gotta be superuser;
+        * else you need createrole, or just want to change your own password
         */
-       if (((Form_pg_authid) GETSTRUCT(tuple))->rolsuper || issuper >= 0)
+
+       attributes = ((Form_pg_authid) GETSTRUCT(tuple))->rolattr;
+
+       if ((attributes & ROLE_ATTR_SUPERUSER) || issuper >= 0)
        {
                if (!superuser())
                        ereport(ERROR,
                                        (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
                                         errmsg("must be superuser to alter superusers")));
        }
-       else if (((Form_pg_authid) GETSTRUCT(tuple))->rolreplication || isreplication >= 0)
+       else if ((attributes & ROLE_ATTR_REPLICATION) || isreplication >= 0)
        {
                if (!superuser())
                        ereport(ERROR,
                                        (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
                                         errmsg("must be superuser to alter replication users")));
        }
-       else if (((Form_pg_authid) GETSTRUCT(tuple))->rolbypassrls || bypassrls >= 0)
+       else if ((attributes & ROLE_ATTR_BYPASSRLS) || bypassrls >= 0)
        {
                if (!superuser())
                        ereport(ERROR,
                                        (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
                                         errmsg("must be superuser to change bypassrls attribute")));
        }
-       else if (!have_createrole_privilege())
+       else if (!have_role_attribute(ROLE_ATTR_CREATEROLE))
        {
                if (!(inherit < 0 &&
                          createrole < 0 &&
@@ -743,43 +761,71 @@ AlterRole(AlterRoleStmt *stmt)
         */
        if (issuper >= 0)
        {
-               new_record[Anum_pg_authid_rolsuper - 1] = BoolGetDatum(issuper > 0);
-               new_record_repl[Anum_pg_authid_rolsuper - 1] = true;
-
-               new_record[Anum_pg_authid_rolcatupdate - 1] = BoolGetDatum(issuper > 0);
-               new_record_repl[Anum_pg_authid_rolcatupdate - 1] = true;
+               if (issuper > 0)
+                       attributes |= ROLE_ATTR_SUPERUSER | ROLE_ATTR_CATUPDATE;
+               else
+                       attributes &= ~(ROLE_ATTR_SUPERUSER | ROLE_ATTR_CATUPDATE);
+               new_record_repl[Anum_pg_authid_rolattr - 1] = true;
        }
 
        if (inherit >= 0)
        {
-               new_record[Anum_pg_authid_rolinherit - 1] = BoolGetDatum(inherit > 0);
-               new_record_repl[Anum_pg_authid_rolinherit - 1] = true;
+               if (inherit > 0)
+                       attributes |= ROLE_ATTR_INHERIT;
+               else
+                       attributes &= ~ROLE_ATTR_INHERIT;
+               new_record_repl[Anum_pg_authid_rolattr - 1] = true;
        }
 
        if (createrole >= 0)
        {
-               new_record[Anum_pg_authid_rolcreaterole - 1] = BoolGetDatum(createrole > 0);
-               new_record_repl[Anum_pg_authid_rolcreaterole - 1] = true;
+               if (createrole > 0)
+                       attributes |= ROLE_ATTR_CREATEROLE;
+               else
+                       attributes &= ~ROLE_ATTR_CREATEROLE;
+               new_record_repl[Anum_pg_authid_rolattr - 1] = true;
        }
 
        if (createdb >= 0)
        {
-               new_record[Anum_pg_authid_rolcreatedb - 1] = BoolGetDatum(createdb > 0);
-               new_record_repl[Anum_pg_authid_rolcreatedb - 1] = true;
+               if (createdb > 0)
+                       attributes |= ROLE_ATTR_CREATEDB;
+               else
+                       attributes &= ~ROLE_ATTR_CREATEDB;
+               new_record_repl[Anum_pg_authid_rolattr - 1] = true;
        }
 
        if (canlogin >= 0)
        {
-               new_record[Anum_pg_authid_rolcanlogin - 1] = BoolGetDatum(canlogin > 0);
-               new_record_repl[Anum_pg_authid_rolcanlogin - 1] = true;
+               if (canlogin > 0)
+                       attributes |= ROLE_ATTR_CANLOGIN;
+               else
+                       attributes &= ~ROLE_ATTR_CANLOGIN;
+               new_record_repl[Anum_pg_authid_rolattr - 1] = true;
        }
 
        if (isreplication >= 0)
        {
-               new_record[Anum_pg_authid_rolreplication - 1] = BoolGetDatum(isreplication > 0);
-               new_record_repl[Anum_pg_authid_rolreplication - 1] = true;
+               if (isreplication > 0)
+                       attributes |= ROLE_ATTR_REPLICATION;
+               else
+                       attributes &= ~ROLE_ATTR_REPLICATION;
+               new_record_repl[Anum_pg_authid_rolattr - 1] = true;
        }
 
+       if (bypassrls >= 0)
+       {
+               if (bypassrls > 0)
+                       attributes |= ROLE_ATTR_BYPASSRLS;
+               else
+                       attributes &= ~ROLE_ATTR_BYPASSRLS;
+               new_record_repl[Anum_pg_authid_rolattr - 1] = true;
+       }
+
+       /* If any role attributes were set, then update. */
+       if (new_record_repl[Anum_pg_authid_rolattr - 1])
+               new_record[Anum_pg_authid_rolattr - 1] = Int64GetDatum(attributes);
+
        if (dconnlimit)
        {
                new_record[Anum_pg_authid_rolconnlimit - 1] = Int32GetDatum(connlimit);
@@ -815,11 +861,6 @@ AlterRole(AlterRoleStmt *stmt)
        new_record_nulls[Anum_pg_authid_rolvaliduntil - 1] = validUntil_null;
        new_record_repl[Anum_pg_authid_rolvaliduntil - 1] = true;
 
-       if (bypassrls >= 0)
-       {
-               new_record[Anum_pg_authid_rolbypassrls - 1] = BoolGetDatum(bypassrls > 0);
-               new_record_repl[Anum_pg_authid_rolbypassrls - 1] = true;
-       }
 
        new_tuple = heap_modify_tuple(tuple, pg_authid_dsc, new_record,
                                                                  new_record_nulls, new_record_repl);
@@ -867,6 +908,7 @@ AlterRoleSet(AlterRoleSetStmt *stmt)
        HeapTuple       roletuple;
        Oid                     databaseid = InvalidOid;
        Oid                     roleid = InvalidOid;
+       RoleAttr        attributes;
 
        if (stmt->role)
        {
@@ -889,7 +931,8 @@ AlterRoleSet(AlterRoleSetStmt *stmt)
                 * To mess with a superuser you gotta be superuser; else you need
                 * createrole, or just want to change your own settings
                 */
-               if (((Form_pg_authid) GETSTRUCT(roletuple))->rolsuper)
+               attributes = ((Form_pg_authid) GETSTRUCT(roletuple))->rolattr;
+               if (attributes & ROLE_ATTR_SUPERUSER)
                {
                        if (!superuser())
                                ereport(ERROR,
@@ -898,7 +941,7 @@ AlterRoleSet(AlterRoleSetStmt *stmt)
                }
                else
                {
-                       if (!have_createrole_privilege() &&
+                       if (!have_role_attribute(ROLE_ATTR_CREATEROLE) &&
                                HeapTupleGetOid(roletuple) != GetUserId())
                                ereport(ERROR,
                                                (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
@@ -951,7 +994,7 @@ DropRole(DropRoleStmt *stmt)
                                pg_auth_members_rel;
        ListCell   *item;
 
-       if (!have_createrole_privilege())
+       if (!have_role_attribute(ROLE_ATTR_CREATEROLE))
                ereport(ERROR,
                                (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
                                 errmsg("permission denied to drop role")));
@@ -973,6 +1016,7 @@ DropRole(DropRoleStmt *stmt)
                char       *detail_log;
                SysScanDesc sscan;
                Oid                     roleid;
+               RoleAttr        attributes;
 
                tuple = SearchSysCache1(AUTHNAME, PointerGetDatum(role));
                if (!HeapTupleIsValid(tuple))
@@ -1013,8 +1057,8 @@ DropRole(DropRoleStmt *stmt)
                 * roles but not superuser roles.  This is mainly to avoid the
                 * scenario where you accidentally drop the last superuser.
                 */
-               if (((Form_pg_authid) GETSTRUCT(tuple))->rolsuper &&
-                       !superuser())
+               attributes = ((Form_pg_authid) GETSTRUCT(tuple))->rolattr;
+               if ((attributes & ROLE_ATTR_SUPERUSER) && !superuser())
                        ereport(ERROR,
                                        (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
                                         errmsg("must be superuser to drop superusers")));
@@ -1128,6 +1172,7 @@ RenameRole(const char *oldname, const char *newname)
        bool            repl_repl[Natts_pg_authid];
        int                     i;
        Oid                     roleid;
+       RoleAttr        attributes;
 
        rel = heap_open(AuthIdRelationId, RowExclusiveLock);
        dsc = RelationGetDescr(rel);
@@ -1173,7 +1218,8 @@ RenameRole(const char *oldname, const char *newname)
        /*
         * createrole is enough privilege unless you want to mess with a superuser
         */
-       if (((Form_pg_authid) GETSTRUCT(oldtuple))->rolsuper)
+       attributes = ((Form_pg_authid) GETSTRUCT(oldtuple))->rolattr;
+       if (attributes & ROLE_ATTR_SUPERUSER)
        {
                if (!superuser())
                        ereport(ERROR,
@@ -1182,7 +1228,7 @@ RenameRole(const char *oldname, const char *newname)
        }
        else
        {
-               if (!have_createrole_privilege())
+               if (!have_role_attribute(ROLE_ATTR_CREATEROLE))
                        ereport(ERROR,
                                        (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
                                         errmsg("permission denied to rename role")));
@@ -1409,7 +1455,7 @@ AddRoleMems(const char *rolename, Oid roleid,
        }
        else
        {
-               if (!have_createrole_privilege() &&
+               if (!have_role_attribute(ROLE_ATTR_CREATEROLE) &&
                        !is_admin_of_role(grantorId, roleid))
                        ereport(ERROR,
                                        (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
@@ -1555,7 +1601,7 @@ DelRoleMems(const char *rolename, Oid roleid,
        }
        else
        {
-               if (!have_createrole_privilege() &&
+               if (!have_role_attribute(ROLE_ATTR_CREATEROLE) &&
                        !is_admin_of_role(GetUserId(), roleid))
                        ereport(ERROR,
                                        (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
index 6ce8daeb95a6e7091ab5adf2852134a7fae8feff..491dc38caf3c581531e688eb4ac8fd28a3fa92b3 100644 (file)
@@ -776,6 +776,7 @@ check_session_authorization(char **newval, void **extra, GucSource source)
        Oid                     roleid;
        bool            is_superuser;
        role_auth_extra *myextra;
+       RoleAttr        attributes;
 
        /* Do nothing for the boot_val default of NULL */
        if (*newval == NULL)
@@ -800,7 +801,8 @@ check_session_authorization(char **newval, void **extra, GucSource source)
        }
 
        roleid = HeapTupleGetOid(roleTup);
-       is_superuser = ((Form_pg_authid) GETSTRUCT(roleTup))->rolsuper;
+       attributes = ((Form_pg_authid) GETSTRUCT(roleTup))->rolattr;
+       is_superuser = (attributes & ROLE_ATTR_SUPERUSER);
 
        ReleaseSysCache(roleTup);
 
@@ -844,6 +846,7 @@ check_role(char **newval, void **extra, GucSource source)
        Oid                     roleid;
        bool            is_superuser;
        role_auth_extra *myextra;
+       RoleAttr        attributes;
 
        if (strcmp(*newval, "none") == 0)
        {
@@ -872,7 +875,8 @@ check_role(char **newval, void **extra, GucSource source)
                }
 
                roleid = HeapTupleGetOid(roleTup);
-               is_superuser = ((Form_pg_authid) GETSTRUCT(roleTup))->rolsuper;
+               attributes = ((Form_pg_authid) GETSTRUCT(roleTup))->rolattr;
+               is_superuser = (attributes & ROLE_ATTR_SUPERUSER);
 
                ReleaseSysCache(roleTup);
 
index 1977f098c798b3cfa310f19ba24eec4ea6022762..1a38f56a7cc1507250733ccf14b1b671ce023832 100644 (file)
 
 #include <unistd.h>
 
+#include "access/xlog_internal.h"
+#include "catalog/pg_type.h"
 #include "fmgr.h"
 #include "funcapi.h"
+#include "mb/pg_wchar.h"
 #include "miscadmin.h"
-
-#include "access/xlog_internal.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"
 #include "utils/pg_lsn.h"
 #include "utils/resowner.h"
 #include "utils/lsyscache.h"
-
 #include "replication/decode.h"
 #include "replication/logical.h"
 #include "replication/logicalfuncs.h"
-
 #include "storage/fd.h"
 
 /* private date for writing out data */
@@ -205,7 +199,7 @@ XLogRead(char *buf, TimeLineID tli, XLogRecPtr startptr, Size count)
 static void
 check_permissions(void)
 {
-       if (!superuser() && !has_rolreplication(GetUserId()))
+       if (!have_role_attribute(ROLE_ATTR_REPLICATION))
                ereport(ERROR,
                                (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
                                 (errmsg("must be superuser or replication role to use replication slots"))));
index bd4701f97dfa04e2b8ade5c1c16363c31aa76544..c113a0bbaa78d8a63d436d4bdceb60daff7a53eb 100644 (file)
 #include "replication/slot.h"
 #include "replication/logical.h"
 #include "replication/logicalfuncs.h"
+#include "utils/acl.h"
 #include "utils/builtins.h"
 #include "utils/pg_lsn.h"
 
 static void
 check_permissions(void)
 {
-       if (!superuser() && !has_rolreplication(GetUserId()))
+       if (!have_role_attribute(ROLE_ATTR_REPLICATION))
                ereport(ERROR,
                                (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
                                 (errmsg("must be superuser or replication role to use replication slots"))));
index 6c232dcf9ae3cf3d58dd290c2dc6771661ef48bd..f41ad34744449fe41eebf79d3938cb4c196e156c 100644 (file)
@@ -521,7 +521,7 @@ check_enable_rls(Oid relid, Oid checkAsUser)
         */
        if (!checkAsUser && row_security == ROW_SECURITY_OFF)
        {
-               if (has_bypassrls_privilege(user_id))
+               if (has_role_attribute(user_id, ROLE_ATTR_BYPASSRLS))
                        /* OK to bypass */
                        return RLS_NONE_ENV;
                else
index dc6eb2c8aacdb6ac66ef2ce25f8e343bd4588d9f..4c03955799a0cb534cc6108c4dea81abce52d2fa 100644 (file)
@@ -115,6 +115,7 @@ static Oid  convert_type_name(text *typename);
 static AclMode convert_type_priv_string(text *priv_type_text);
 static AclMode convert_role_priv_string(text *priv_type_text);
 static AclResult pg_role_aclcheck(Oid role_oid, Oid roleid, AclMode mode);
+static RoleAttr convert_role_attr_string(text *attr_type_text);
 
 static void RoleMembershipCacheCallback(Datum arg, int cacheid, uint32 hashvalue);
 
@@ -4602,6 +4603,186 @@ pg_role_aclcheck(Oid role_oid, Oid roleid, AclMode mode)
        return ACLCHECK_NO_PRIV;
 }
 
+/*
+ * pg_has_role_attribute_id
+ *             Check that the role with the given oid has the given named role
+ *             attribute.
+ *
+ * Note: This function applies superuser checks.  Therefore, if the provided
+ * role is a superuser, then the result will always be true.
+ */
+Datum
+pg_has_role_attribute_id(PG_FUNCTION_ARGS)
+{
+       Oid                     roleoid = PG_GETARG_OID(0);
+       text       *attr_type_text = PG_GETARG_TEXT_P(1);
+       RoleAttr        attribute;
+
+       attribute = convert_role_attr_string(attr_type_text);
+
+       PG_RETURN_BOOL(has_role_attribute(roleoid, attribute));
+}
+
+/*
+ * pg_has_role_attribute_name
+ *             Check that the named role has the given named role attribute.
+ *
+ * Note: This function applies superuser checks.  Therefore, if the provided
+ * role is a superuser, then the result will always be true.
+ */
+Datum
+pg_has_role_attribute_name(PG_FUNCTION_ARGS)
+{
+       Name            rolename = PG_GETARG_NAME(0);
+       text       *attr_type_text = PG_GETARG_TEXT_P(1);
+       Oid                     roleoid;
+       RoleAttr        attribute;
+
+       roleoid = get_role_oid(NameStr(*rolename), false);
+       attribute = convert_role_attr_string(attr_type_text);
+
+       PG_RETURN_BOOL(has_role_attribute(roleoid, attribute));
+}
+
+/*
+ * pg_check_role_attribute_id
+ *             Check that the role with the given oid has the given named role
+ *             attribute.
+ *
+ * Note: This function is different from 'pg_has_role_attribute_id_attr' in that
+ * it does *not* apply any superuser checks.  Therefore, this function will
+ * always return the set value of the attribute, despite the superuser-ness of
+ * the provided role.
+ */
+Datum
+pg_check_role_attribute_id(PG_FUNCTION_ARGS)
+{
+       Oid                     roleoid = PG_GETARG_OID(0);
+       text       *attr_type_text = PG_GETARG_TEXT_P(1);
+       RoleAttr        attribute;
+
+       attribute = convert_role_attr_string(attr_type_text);
+
+       PG_RETURN_BOOL(check_role_attribute(roleoid, attribute));
+}
+
+/*
+ * pg_check_role_attribute_name
+ *             Check that the named role has the given named role attribute.
+ *
+ * Note: This function is different from 'pg_has_role_attribute_name_attr' in
+ * that it does *not* apply any superuser checks.  Therefore, this function will
+ * always return the set value of the attribute, despite the superuser-ness of
+ * the provided role.
+ */
+Datum
+pg_check_role_attribute_name(PG_FUNCTION_ARGS)
+{
+       Name            rolename = PG_GETARG_NAME(0);
+       text       *attr_type_text = PG_GETARG_TEXT_P(1);
+       Oid                     roleoid;
+       RoleAttr        attribute;
+
+       roleoid = get_role_oid(NameStr(*rolename), false);
+       attribute = convert_role_attr_string(attr_type_text);
+
+       PG_RETURN_BOOL(check_role_attribute(roleoid, attribute));
+}
+
+/*
+ * pg_check_role_attribute_attrs
+ *             Check that the named attribute is enabled in the given RoleAttr
+ *             representation of role attributes.
+ */
+Datum
+pg_check_role_attribute_attrs(PG_FUNCTION_ARGS)
+{
+       RoleAttr        attributes = PG_GETARG_INT64(0);
+       text       *attr_type_text = PG_GETARG_TEXT_P(1);
+       RoleAttr        attribute;
+
+       attribute = convert_role_attr_string(attr_type_text);
+
+       PG_RETURN_BOOL(attributes & attribute);
+}
+
+/*
+ * pg_all_role_attributes
+ *             Convert a RoleAttr representation of role attributes into an array of
+ *             corresponding text values.
+ *
+ * The first and only argument is a RoleAttr (int64) representation of the
+ * role attributes.
+ */
+Datum
+pg_all_role_attributes(PG_FUNCTION_ARGS)
+{
+       RoleAttr                attributes = PG_GETARG_INT64(0);
+       Datum              *temp_array;
+       ArrayType          *result;
+       int                             i = 0;
+
+       /*
+        * Short-circuit the case for no attributes assigned.
+        */
+       if (attributes == ROLE_ATTR_NONE)
+               PG_RETURN_ARRAYTYPE_P(construct_empty_array(TEXTOID));
+
+       temp_array = (Datum *) palloc(N_ROLE_ATTRIBUTES * sizeof(Datum));
+
+       /* Determine which attributes are assigned. */
+       if (attributes & ROLE_ATTR_SUPERUSER)
+               temp_array[i++] = CStringGetTextDatum(_("Superuser"));
+       if (attributes & ROLE_ATTR_INHERIT)
+               temp_array[i++] = CStringGetTextDatum(_("Inherit"));
+       if (attributes & ROLE_ATTR_CREATEROLE)
+               temp_array[i++] = CStringGetTextDatum(_("Create Role"));
+       if (attributes & ROLE_ATTR_CREATEDB)
+               temp_array[i++] = CStringGetTextDatum(_("Create DB"));
+       if (attributes & ROLE_ATTR_CATUPDATE)
+               temp_array[i++] = CStringGetTextDatum(_("Catalog Update"));
+       if (attributes & ROLE_ATTR_CANLOGIN)
+               temp_array[i++] = CStringGetTextDatum(_("Login"));
+       if (attributes & ROLE_ATTR_REPLICATION)
+               temp_array[i++] = CStringGetTextDatum(_("Replication"));
+       if (attributes & ROLE_ATTR_BYPASSRLS)
+               temp_array[i++] = CStringGetTextDatum(_("Bypass RLS"));
+
+       result = construct_array(temp_array, i, TEXTOID, -1, false, 'i');
+
+       PG_RETURN_ARRAYTYPE_P(result);
+}
+
+/*
+ * convert_role_attr_string
+ *             Convert text string to RoleAttr value.
+ */
+static RoleAttr
+convert_role_attr_string(text *attr_type_text)
+{
+       char       *attr_type = text_to_cstring(attr_type_text);
+
+       if (pg_strcasecmp(attr_type, "SUPERUSER") == 0)
+               return ROLE_ATTR_SUPERUSER;
+       else if (pg_strcasecmp(attr_type, "INHERIT") == 0)
+               return ROLE_ATTR_INHERIT;
+       else if (pg_strcasecmp(attr_type, "CREATEROLE") == 0)
+               return ROLE_ATTR_CREATEROLE;
+       else if (pg_strcasecmp(attr_type, "CREATEDB") == 0)
+               return ROLE_ATTR_CREATEDB;
+       else if (pg_strcasecmp(attr_type, "CATUPDATE") == 0)
+               return ROLE_ATTR_CATUPDATE;
+       else if (pg_strcasecmp(attr_type, "CANLOGIN") == 0)
+               return ROLE_ATTR_CANLOGIN;
+       else if (pg_strcasecmp(attr_type, "REPLICATION") == 0)
+               return ROLE_ATTR_REPLICATION;
+       else if (pg_strcasecmp(attr_type, "BYPASSRLS") == 0)
+               return ROLE_ATTR_BYPASSRLS;
+       else
+               ereport(ERROR,
+                               (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                                errmsg("unrecognized role attribute: \"%s\"", attr_type)));
+}
 
 /*
  * initialization function (called by InitPostgres)
@@ -4634,23 +4815,6 @@ RoleMembershipCacheCallback(Datum arg, int cacheid, uint32 hashvalue)
 }
 
 
-/* Check if specified role has rolinherit set */
-static bool
-has_rolinherit(Oid roleid)
-{
-       bool            result = false;
-       HeapTuple       utup;
-
-       utup = SearchSysCache1(AUTHOID, ObjectIdGetDatum(roleid));
-       if (HeapTupleIsValid(utup))
-       {
-               result = ((Form_pg_authid) GETSTRUCT(utup))->rolinherit;
-               ReleaseSysCache(utup);
-       }
-       return result;
-}
-
-
 /*
  * Get a list of roles that the specified roleid has the privileges of
  *
@@ -4697,7 +4861,7 @@ roles_has_privs_of(Oid roleid)
                int                     i;
 
                /* Ignore non-inheriting roles */
-               if (!has_rolinherit(memberid))
+               if (!has_role_attribute(memberid, ROLE_ATTR_INHERIT))
                        continue;
 
                /* Find roles that memberid is directly a member of */
index 5c75390ce4911b8c2b2f899f4c41ab994446a302..ccb1066732637d34a3739a5d555f01d42df30cd2 100644 (file)
@@ -2308,7 +2308,7 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
         * bypassrls right or is the table owner of the table(s) involved which
         * have RLS enabled.
         */
-       if (!has_bypassrls_privilege(GetUserId()) &&
+       if (!have_role_attribute(ROLE_ATTR_BYPASSRLS) &&
                ((pk_rel->rd_rel->relrowsecurity &&
                  !pg_class_ownercheck(pkrte->relid, GetUserId())) ||
                 (fk_rel->rd_rel->relrowsecurity &&
index 8fccb4c8262c60c6782b6223c5148dfed7312ff9..db2a0fb48efa4d41f2ff173bffad258fe5944003 100644 (file)
@@ -40,6 +40,7 @@
 #include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/procarray.h"
+#include "utils/acl.h"
 #include "utils/builtins.h"
 #include "utils/guc.h"
 #include "utils/memutils.h"
@@ -328,24 +329,6 @@ SetUserIdAndContext(Oid userid, bool sec_def_context)
 }
 
 
-/*
- * Check whether specified role has explicit REPLICATION privilege
- */
-bool
-has_rolreplication(Oid roleid)
-{
-       bool            result = false;
-       HeapTuple       utup;
-
-       utup = SearchSysCache1(AUTHOID, ObjectIdGetDatum(roleid));
-       if (HeapTupleIsValid(utup))
-       {
-               result = ((Form_pg_authid) GETSTRUCT(utup))->rolreplication;
-               ReleaseSysCache(utup);
-       }
-       return result;
-}
-
 /*
  * Initialize user identity during normal backend startup
  */
@@ -375,7 +358,7 @@ InitializeSessionUserId(const char *rolename)
        roleid = HeapTupleGetOid(roleTup);
 
        AuthenticatedUserId = roleid;
-       AuthenticatedUserIsSuperuser = rform->rolsuper;
+       AuthenticatedUserIsSuperuser = (rform->rolattr & ROLE_ATTR_SUPERUSER);
 
        /* This sets OuterUserId/CurrentUserId too */
        SetSessionUserId(roleid, AuthenticatedUserIsSuperuser);
@@ -394,7 +377,7 @@ InitializeSessionUserId(const char *rolename)
                /*
                 * Is role allowed to login at all?
                 */
-               if (!rform->rolcanlogin)
+               if (!(rform->rolattr & ROLE_ATTR_CANLOGIN))
                        ereport(FATAL,
                                        (errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION),
                                         errmsg("role \"%s\" is not permitted to log in",
index c34803437b640120adf9699f01ef493b39b67e47..268001f58afd8763b036320f0336e194b2844eed 100644 (file)
@@ -762,7 +762,7 @@ InitPostgres(const char *in_dbname, Oid dboid, const char *username,
        {
                Assert(!bootstrap);
 
-               if (!superuser() && !has_rolreplication(GetUserId()))
+               if (!have_role_attribute(ROLE_ATTR_REPLICATION))
                        ereport(FATAL,
                                        (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
                                         errmsg("must be superuser or replication role to start walsender")));
index ff0f94711d758649f86cbf5d79cc5fda9127c3eb..67d070ca2569cd00cc7a5d5d8cc16ceb42ccde0d 100644 (file)
@@ -58,6 +58,7 @@ superuser_arg(Oid roleid)
 {
        bool            result;
        HeapTuple       rtup;
+       RoleAttr        attributes;
 
        /* Quick out for cache hit */
        if (OidIsValid(last_roleid) && last_roleid == roleid)
@@ -71,7 +72,8 @@ superuser_arg(Oid roleid)
        rtup = SearchSysCache1(AUTHOID, ObjectIdGetDatum(roleid));
        if (HeapTupleIsValid(rtup))
        {
-               result = ((Form_pg_authid) GETSTRUCT(rtup))->rolsuper;
+               attributes = ((Form_pg_authid) GETSTRUCT(rtup))->rolattr;
+               result = (attributes & ROLE_ATTR_SUPERUSER);
                ReleaseSysCache(rtup);
        }
        else
index eb633bc1368dbd560ef878300ea08fe33096bab6..f638167d8faf6ae32b33838b3b09a04e848c365b 100644 (file)
@@ -671,10 +671,16 @@ dumpRoles(PGconn *conn)
        /* note: rolconfig is dumped later */
        if (server_version >= 90500)
                printfPQExpBuffer(buf,
-                                                 "SELECT oid, rolname, rolsuper, rolinherit, "
-                                                 "rolcreaterole, rolcreatedb, "
-                                                 "rolcanlogin, rolconnlimit, rolpassword, "
-                                                 "rolvaliduntil, rolreplication, rolbypassrls, "
+                                                 "SELECT oid, rolname, "
+                                                 "pg_check_role_attribute(oid, 'SUPERUSER') AS rolsuper, "
+                                                 "pg_check_role_attribute(oid, 'INHERIT') AS rolinherit, "
+                                                 "pg_check_role_attribute(oid, 'CREATEROLE') AS rolcreaterole, "
+                                                 "pg_check_role_attribute(oid, 'CREATEDB') AS rolcreatedb, "
+                                                 "pg_check_role_attribute(oid, 'CANLOGIN') AS rolcanlogin, "
+                                                 "pg_check_role_attribute(oid, 'REPLICATION') AS rolreplication, "
+                                                 "pg_check_role_attribute(oid, 'BYPASSRLS') AS rolbypassrls, "
+                                                 "rolconnlimit, rolpassword, "
+                                                 "rolvaliduntil, "
                         "pg_catalog.shobj_description(oid, 'pg_authid') as rolcomment, "
                                                  "rolname = current_user AS is_current_user "
                                                  "FROM pg_authid "
diff --git a/src/include/catalog/acldefs.h b/src/include/catalog/acldefs.h
new file mode 100644 (file)
index 0000000..2dcc174
--- /dev/null
@@ -0,0 +1,72 @@
+/*-------------------------------------------------------------------------
+ *
+ * acldefs.h
+ *       base definitions for ACLs and role attributes
+ *
+ * Portions Copyright (c) 2014, PostgreSQL Global Development Group
+ *
+ * src/include/catalog/acldefs.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef ACLDEFS_H
+#define ACLDEFS_H
+
+/*
+ * Grantable rights are encoded so that we can OR them together in a bitmask.
+ * The present representation of AclItem limits us to 16 distinct rights,
+ * even though AclMode is defined as uint32.  See utils/acl.h.
+ *
+ * Caution: changing these codes breaks stored ACLs, hence forces initdb.
+ */
+typedef uint32 AclMode;                        /* a bitmask of privilege bits */
+
+#define ACL_INSERT             (1<<0)  /* for relations */
+#define ACL_SELECT             (1<<1)
+#define ACL_UPDATE             (1<<2)
+#define ACL_DELETE             (1<<3)
+#define ACL_TRUNCATE   (1<<4)
+#define ACL_REFERENCES (1<<5)
+#define ACL_TRIGGER            (1<<6)
+#define ACL_EXECUTE            (1<<7)  /* for functions */
+#define ACL_USAGE              (1<<8)  /* for languages, namespaces, FDWs, and
+                                                                * servers */
+#define ACL_CREATE             (1<<9)  /* for namespaces and databases */
+#define ACL_CREATE_TEMP (1<<10) /* for databases */
+#define ACL_CONNECT            (1<<11) /* for databases */
+#define N_ACL_RIGHTS   12              /* 1 plus the last 1<<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 */
index 5e9961af69d43b0d0aee612c00f3966eaf395d74..e56f7ded9d992f5e597003c674fc50ea90b1317b 100644 (file)
@@ -53,6 +53,6 @@
  */
 
 /*                                                     yyyymmddN */
-#define CATALOG_VERSION_NO     201412191
+#define CATALOG_VERSION_NO     201412232
 
 #endif
index 3b63d2bb9e0be6ca6c56f2892d672e0aa673fe1b..a45f38d1ebcefe7baf2f5977ef039f360bfa2c1d 100644 (file)
@@ -21,6 +21,7 @@
 #ifndef PG_AUTHID_H
 #define PG_AUTHID_H
 
+#include "catalog/acldefs.h"
 #include "catalog/genbki.h"
 
 /*
 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? */
+       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 */
@@ -74,28 +67,25 @@ typedef FormData_pg_authid *Form_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_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
+#define Anum_pg_authid_rolattr                 2
+#define Anum_pg_authid_rolconnlimit            3
+#define Anum_pg_authid_rolpassword             4
+#define Anum_pg_authid_rolvaliduntil   5
+
 
 /* ----------------
  *             initial contents of pg_authid
  *
  * The uppercase quantities will be replaced at initdb time with
  * 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
 
index f766ed791fb46199f53dc93de898acb64e3ecbf9..7f64aaa964d26c5da0ed4feaba5a1ee86a2ffa7f 100644 (file)
@@ -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_ ));
 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
index 64508f0338ac183bac7bfe91a70be33c67f586d4..f027a3753518e6fc101e2d915d8cec67ff0b7f12 100644 (file)
@@ -23,6 +23,7 @@
 #include "nodes/bitmapset.h"
 #include "nodes/primnodes.h"
 #include "nodes/value.h"
+#include "catalog/acldefs.h"
 #include "utils/lockwaitpolicy.h"
 
 /* Possible sources of a Query */
@@ -51,33 +52,6 @@ typedef enum SortByNulls
        SORTBY_NULLS_LAST
 } 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
index a8e3164659c6dc0bf1f6178ad8d64a4dc956bfaf..4e8d81ca0ad8d550eef4ed1e6944c293ebf492fa 100644 (file)
 #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
  *
@@ -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_event_trigger_ownercheck(Oid et_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 */
index 2da3002e5d0bafdc5e3c2769a8300c475da482cd..c8e0e3a4344a35e5c5ce9270eeef3dd7a336f0a1 100644 (file)
@@ -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_name(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 */
 extern Datum boolin(PG_FUNCTION_ARGS);
index 80c3351291638cc8f37194f34e0eb773564616ca..d1052157349466273bcf8627eaef037905e47a5e 100644 (file)
@@ -1314,7 +1314,7 @@ pg_group| SELECT pg_authid.rolname AS groname,
            FROM pg_auth_members
           WHERE (pg_auth_members.roleid = pg_authid.oid)) AS grolist
    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,
     c.relname AS tablename,
     i.relname AS indexname,
@@ -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)
      LEFT JOIN pg_database d ON ((l.datoid = d.oid)));
 pg_roles| SELECT pg_authid.rolname,
-    pg_authid.rolsuper,
-    pg_authid.rolinherit,
-    pg_authid.rolcreaterole,
-    pg_authid.rolcreatedb,
-    pg_authid.rolcatupdate,
-    pg_authid.rolcanlogin,
-    pg_authid.rolreplication,
+    pg_check_role_attribute(pg_authid.rolattr, 'SUPERUSER'::text) AS rolsuper,
+    pg_check_role_attribute(pg_authid.rolattr, 'INHERIT'::text) AS rolinherit,
+    pg_check_role_attribute(pg_authid.rolattr, 'CREATEROLE'::text) AS rolcreaterole,
+    pg_check_role_attribute(pg_authid.rolattr, 'CREATEDB'::text) AS rolcreatedb,
+    pg_check_role_attribute(pg_authid.rolattr, 'CATUPDATE'::text) AS rolcatupdate,
+    pg_check_role_attribute(pg_authid.rolattr, 'CANLOGIN'::text) AS rolcanlogin,
+    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,
     '********'::text AS rolpassword,
     pg_authid.rolvaliduntil,
-    pg_authid.rolbypassrls,
     s.setconfig AS rolconfig,
     pg_authid.oid
    FROM (pg_authid
@@ -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);
 pg_shadow| SELECT pg_authid.rolname AS usename,
     pg_authid.oid AS usesysid,
-    pg_authid.rolcreatedb AS usecreatedb,
-    pg_authid.rolsuper AS usesuper,
-    pg_authid.rolcatupdate AS usecatupd,
-    pg_authid.rolreplication AS userepl,
+    pg_check_role_attribute(pg_authid.rolattr, 'CREATEDB'::text) AS usecreatedb,
+    pg_check_role_attribute(pg_authid.rolattr, 'SUPERUSER'::text) AS usesuper,
+    pg_check_role_attribute(pg_authid.rolattr, 'CATUPDATE'::text) AS usecatupd,
+    pg_check_role_attribute(pg_authid.rolattr, 'REPLICATION'::text) AS userepl,
     pg_authid.rolpassword AS passwd,
     (pg_authid.rolvaliduntil)::abstime AS valuntil,
     s.setconfig AS useconfig
    FROM (pg_authid
      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,
     d.datname,
     s.pid,