]> granicus.if.org Git - postgresql/commitdiff
Add SET ROLE. This is a partial commit of Stephen Frost's recent patch;
authorTom Lane <tgl@sss.pgh.pa.us>
Mon, 25 Jul 2005 22:12:34 +0000 (22:12 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Mon, 25 Jul 2005 22:12:34 +0000 (22:12 +0000)
I'm still working on the has_role function and information_schema changes.

17 files changed:
doc/src/sgml/func.sgml
doc/src/sgml/ref/allfiles.sgml
doc/src/sgml/ref/pg_dump.sgml
doc/src/sgml/ref/pg_dumpall.sgml
doc/src/sgml/ref/pg_restore.sgml
doc/src/sgml/ref/set_role.sgml [new file with mode: 0644]
doc/src/sgml/ref/set_session_auth.sgml
doc/src/sgml/reference.sgml
src/backend/access/transam/xact.c
src/backend/commands/user.c
src/backend/commands/variable.c
src/backend/parser/gram.y
src/backend/utils/init/miscinit.c
src/backend/utils/misc/check_guc
src/backend/utils/misc/guc.c
src/include/commands/variable.h
src/include/miscadmin.h

index 59813e16f164c72899631a28c5ad03d7ca7da8db..370873551502e0f41e60e7eb08d09dd22cba0644 100644 (file)
@@ -1,5 +1,5 @@
 <!--
-$PostgreSQL: pgsql/doc/src/sgml/func.sgml,v 1.269 2005/07/22 21:16:14 momjian Exp $
+$PostgreSQL: pgsql/doc/src/sgml/func.sgml,v 1.270 2005/07/25 22:12:30 tgl Exp $
 PostgreSQL documentation
 -->
 
@@ -8266,7 +8266,9 @@ select current_date + s.a as dates from generate_series(0,14,7) as s(a);
     with <xref linkend="sql-set-session-authorization" endterm="sql-set-session-authorization-title">.
     The <function>current_user</function> is the user identifier
     that is applicable for permission checking. Normally, it is equal
-    to the session user, but it changes during the execution of
+    to the session user, but it can be changed with
+    <xref linkend="sql-set-role" endterm="sql-set-role-title">.
+    It also changes during the execution of
     functions with the attribute <literal>SECURITY DEFINER</literal>.
     In Unix parlance, the session user is the <quote>real user</quote> and
     the current user is the <quote>effective user</quote>.
index 33e9e68b9d55b2dc757253fd2cfaac543cac17a9..d993b64ad0486958a44787d5006af3ff00d609f1 100644 (file)
@@ -1,5 +1,5 @@
 <!--
-$PostgreSQL: pgsql/doc/src/sgml/ref/allfiles.sgml,v 1.63 2005/06/17 22:32:42 tgl Exp $
+$PostgreSQL: pgsql/doc/src/sgml/ref/allfiles.sgml,v 1.64 2005/07/25 22:12:31 tgl Exp $
 PostgreSQL documentation
 Complete list of usable sgml source files in this directory.
 -->
@@ -102,6 +102,7 @@ Complete list of usable sgml source files in this directory.
 <!entity selectInto         system "select_into.sgml">
 <!entity set                system "set.sgml">
 <!entity setConstraints     system "set_constraints.sgml">
+<!entity setRole            system "set_role.sgml">
 <!entity setSessionAuth     system "set_session_auth.sgml">
 <!entity setTransaction     system "set_transaction.sgml">
 <!entity show               system "show.sgml">
index a6d8bb240784f77492dc0022abfb7f32099e6c71..288ae20a411ed05e3604d3aaa851bc4df323a84f 100644 (file)
@@ -1,5 +1,5 @@
 <!--
-$PostgreSQL: pgsql/doc/src/sgml/ref/pg_dump.sgml,v 1.79 2005/07/10 15:08:52 momjian Exp $
+$PostgreSQL: pgsql/doc/src/sgml/ref/pg_dump.sgml,v 1.80 2005/07/25 22:12:31 tgl Exp $
 PostgreSQL documentation
 -->
 
@@ -474,8 +474,8 @@ PostgreSQL documentation
       <term><option>--use-set-session-authorization</></term>
       <listitem>
        <para>
-        Output SQL standard SET SESSION AUTHORIZATION commands instead
-        of OWNER TO commands.  This makes the dump more standards compatible,
+        Output SQL standard SET SESSION AUTHORIZATION commands instead of
+        ALTER OWNER commands.  This makes the dump more standards compatible,
         but depending on the history of the objects in the dump, may not
         restore properly.
        </para>
index c61ae09404224a57f1e9f6c4005080e505a3c48e..4cee1a4ed7249a00090f871a8827ba88b290ce8d 100644 (file)
@@ -1,5 +1,5 @@
 <!--
-$PostgreSQL: pgsql/doc/src/sgml/ref/pg_dumpall.sgml,v 1.51 2005/06/21 20:45:43 tgl Exp $
+$PostgreSQL: pgsql/doc/src/sgml/ref/pg_dumpall.sgml,v 1.52 2005/07/25 22:12:31 tgl Exp $
 PostgreSQL documentation
 -->
 
@@ -277,8 +277,8 @@ PostgreSQL documentation
       <term><option>--use-set-session-authorization</></term>
       <listitem>
        <para>
-        Output SQL standard SET SESSION AUTHORIZATION commands instead
-        of OWNER TO commands.  This makes the dump more standards compatible,
+        Output SQL standard SET SESSION AUTHORIZATION commands instead of
+        ALTER OWNER commands.  This makes the dump more standards compatible,
         but depending on the history of the objects in the dump, may not
         restore properly.
        </para>
index 9b2b5fc3f26ef1c5a7ca47126a8a043a8d172605..d4a1a3e0f0594fc08a5b9ae612e01f98ef92694a 100644 (file)
@@ -1,4 +1,4 @@
-<!-- $PostgreSQL: pgsql/doc/src/sgml/ref/pg_restore.sgml,v 1.53 2005/06/21 20:45:43 tgl Exp $ -->
+<!-- $PostgreSQL: pgsql/doc/src/sgml/ref/pg_restore.sgml,v 1.54 2005/07/25 22:12:31 tgl Exp $ -->
 
 <refentry id="APP-PGRESTORE">
  <refmeta>
       <term><option>--use-set-session-authorization</option></term>
       <listitem>
        <para>
-        Output SQL standard SET SESSION AUTHORIZATION commands instead
-        of OWNER TO commands.  This makes the dump more standards compatible,
+        Output SQL standard SET SESSION AUTHORIZATION commands instead of
+        ALTER OWNER commands.  This makes the dump more standards compatible,
         but depending on the history of the objects in the dump, may not
         restore properly.
        </para>
diff --git a/doc/src/sgml/ref/set_role.sgml b/doc/src/sgml/ref/set_role.sgml
new file mode 100644 (file)
index 0000000..6fbe40f
--- /dev/null
@@ -0,0 +1,116 @@
+<!-- $PostgreSQL: pgsql/doc/src/sgml/ref/set_role.sgml,v 1.1 2005/07/25 22:12:31 tgl Exp $ -->
+<refentry id="SQL-SET-ROLE">
+ <refmeta>
+  <refentrytitle id="sql-set-role-title">SET ROLE</refentrytitle>
+  <refmiscinfo>SQL - Language Statements</refmiscinfo>
+ </refmeta>
+
+ <refnamediv>
+  <refname>SET ROLE</refname>
+  <refpurpose>set the current user identifier of the current session</refpurpose>
+ </refnamediv>
+
+ <indexterm zone="sql-set-role">
+  <primary>SET ROLE</primary>
+ </indexterm>
+
+ <refsynopsisdiv>
+<synopsis>
+SET [ SESSION | LOCAL ] ROLE <replaceable class="parameter">rolename</replaceable>
+SET [ SESSION | LOCAL ] ROLE NONE
+RESET ROLE
+</synopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+  <title>Description</title>
+
+  <para>
+   This command sets the current user
+   identifier of the current SQL-session context to be <replaceable
+   class="parameter">rolename</replaceable>.  The role name may be
+   written as either an identifier or a string literal.  Using this
+   command, it is possible to either add privileges or restrict one's
+   privileges.
+  </para>
+
+  <para>
+   The specified <replaceable class="parameter">rolename</replaceable>
+   must be a role that the current session user is a member of.
+   (If the session user is a superuser, any role can be selected.)
+  </para>
+
+  <para>
+   The <literal>SESSION</> and <literal>LOCAL</> modifiers act the same
+   as for the regular <xref linkend="SQL-SET" endterm="SQL-SET-title">
+   command.
+  </para>
+
+  <para>
+   The <literal>NONE</> and <literal>RESET</> forms reset the current
+   user identifier to be the current session user identifier.
+   These forms may be executed by any user.
+  </para>
+ </refsect1>
+
+ <refsect1>
+  <title>Examples</title>
+
+<programlisting>
+SELECT SESSION_USER, CURRENT_USER;
+
+ session_user | current_user 
+--------------+--------------
+ peter        | peter
+
+SET ROLE 'paul';
+
+SELECT SESSION_USER, CURRENT_USER;
+
+ session_user | current_user 
+--------------+--------------
+ peter        | paul
+</programlisting>
+ </refsect1>
+
+ <refsect1>
+  <title>Compatibility</title>
+
+  <para>
+   <productname>PostgreSQL</productname>
+   allows identifier syntax (<literal>"rolename"</literal>), while
+   the SQL standard requires the role name to be written as a string
+   literal.  SQL does not allow this command during a transaction;
+   <productname>PostgreSQL</productname> does not make this
+   restriction because there is no reason to.
+   The <literal>SESSION</> and <literal>LOCAL</> modifiers are a
+   <productname>PostgreSQL</productname> extension, as is the
+   <literal>RESET</> syntax.
+  </para>
+ </refsect1>
+
+ <refsect1>
+  <title>See Also</title>
+
+  <simplelist type="inline">
+   <member><xref linkend="sql-set-session-authorization" endterm="sql-set-session-authorization-title"></member>
+  </simplelist>
+ </refsect1>
+</refentry>
+
+<!-- Keep this comment at the end of the file
+Local variables:
+mode:sgml
+sgml-omittag:nil
+sgml-shorttag:t
+sgml-minimize-attributes:nil
+sgml-always-quote-attributes:t
+sgml-indent-step:1
+sgml-indent-data:t
+sgml-parent-document:nil
+sgml-default-dtd-file:"../reference.ced"
+sgml-exposed-tags:nil
+sgml-local-catalogs:("/usr/lib/sgml/catalog")
+sgml-local-ecat-files:nil
+End:
+-->
index 7014b8d2ab357cc063d442743df83d5ea2aab94f..334847fb00e99cdcec53ba86745407b15434c9a3 100644 (file)
@@ -1,4 +1,4 @@
-<!-- $PostgreSQL: pgsql/doc/src/sgml/ref/set_session_auth.sgml,v 1.12 2003/11/29 19:51:39 pgsql Exp $ -->
+<!-- $PostgreSQL: pgsql/doc/src/sgml/ref/set_session_auth.sgml,v 1.13 2005/07/25 22:12:31 tgl Exp $ -->
 <refentry id="SQL-SET-SESSION-AUTHORIZATION">
  <refmeta>
   <refentrytitle id="sql-set-session-authorization-title">SET SESSION AUTHORIZATION</refentrytitle>
@@ -31,7 +31,7 @@ RESET SESSION AUTHORIZATION
    class="parameter">username</replaceable>.  The user name may be
    written as either an identifier or a string literal.  Using this
    command, it is possible, for example, to temporarily become an
-   unprivileged user and later switch back to become a superuser.
+   unprivileged user and later switch back to being a superuser.
   </para>
 
   <para>
@@ -39,8 +39,9 @@ RESET SESSION AUTHORIZATION
    authenticated) user name provided by the client.  The current user
    identifier is normally equal to the session user identifier, but
    may change temporarily in the context of <quote>setuid</quote>
-   functions and similar mechanisms.  The current user identifier is
-   relevant for permission checking.
+   functions and similar mechanisms; it can also be changed by
+   <xref linkend="sql-set-role" endterm="sql-set-role-title">.
+   The current user identifier is relevant for permission checking.
   </para>
 
   <para>
@@ -93,10 +94,24 @@ SELECT SESSION_USER, CURRENT_USER;
    allows identifier syntax (<literal>"username"</literal>), which SQL
    does not.  SQL does not allow this command during a transaction;
    <productname>PostgreSQL</productname> does not make this
-   restriction because there is no reason to.  The privileges
-   necessary to execute this command are left implementation-defined
-   by the standard.
+   restriction because there is no reason to.
+   The <literal>SESSION</> and <literal>LOCAL</> modifiers are a
+   <productname>PostgreSQL</productname> extension, as is the
+   <literal>RESET</> syntax.
   </para>
+
+  <para>
+   The privileges necessary to execute this command are left
+   implementation-defined by the standard.
+  </para>
+ </refsect1>
+
+ <refsect1>
+  <title>See Also</title>
+
+  <simplelist type="inline">
+   <member><xref linkend="sql-set-role" endterm="sql-set-role-title"></member>
+  </simplelist>
  </refsect1>
 </refentry>
 
index 4edec85c1225d17abcd7fd3859ebc07c42a8b71a..63ecfe12041f5aeffab9754d18d7d8029eefac8c 100644 (file)
@@ -1,5 +1,5 @@
 <!-- reference.sgml
-$PostgreSQL: pgsql/doc/src/sgml/reference.sgml,v 1.53 2005/06/17 22:32:42 tgl Exp $
+$PostgreSQL: pgsql/doc/src/sgml/reference.sgml,v 1.54 2005/07/25 22:12:30 tgl Exp $
 
 PostgreSQL Reference Manual
 -->
@@ -134,6 +134,7 @@ PostgreSQL Reference Manual
    &selectInto;
    &set;
    &setConstraints;
+   &setRole;
    &setSessionAuth;
    &setTransaction;
    &show;
index c75da3d432c42177f61a9c8c8b4bf0351b57ea99..ee33030292f771843c874ff53946c34044dc15b9 100644 (file)
@@ -10,7 +10,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/access/transam/xact.c,v 1.210 2005/07/13 22:46:09 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/access/transam/xact.c,v 1.211 2005/07/25 22:12:31 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1865,7 +1865,7 @@ AbortTransaction(void)
 
        /*
         * Reset user id which might have been changed transiently.  We cannot
-        * use s->currentUser, but must get the session userid from
+        * use s->currentUser, but must get the session outer-level userid from
         * miscinit.c.
         *
         * (Note: it is not necessary to restore session authorization here
@@ -1874,7 +1874,7 @@ AbortTransaction(void)
         * DEFINER function could send control here with the wrong current
         * userid.)
         */
-       SetUserId(GetSessionUserId());
+       SetUserId(GetOuterUserId());
 
        /*
         * do abort processing
index 4a46343d5d873d001decfad52a938b41089d1602..5f8eeae30df436b34395b8db202db813cade540f 100644 (file)
@@ -6,7 +6,7 @@
  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/backend/commands/user.c,v 1.156 2005/07/07 20:39:58 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/commands/user.c,v 1.157 2005/07/25 22:12:31 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -227,7 +227,8 @@ CreateRole(CreateRoleStmt *stmt)
                                         errmsg("permission denied to create role")));
        }
 
-       if (strcmp(stmt->role, "public") == 0)
+       if (strcmp(stmt->role, "public") == 0 ||
+               strcmp(stmt->role, "none") == 0)
                ereport(ERROR,
                                (errcode(ERRCODE_RESERVED_NAME),
                                 errmsg("role name \"%s\" is reserved",
@@ -760,11 +761,15 @@ DropRole(DropRoleStmt *stmt)
                if (roleid == GetUserId())
                        ereport(ERROR,
                                        (errcode(ERRCODE_OBJECT_IN_USE),
-                                        errmsg("current role cannot be dropped")));
+                                        errmsg("current user cannot be dropped")));
+               if (roleid == GetOuterUserId())
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_OBJECT_IN_USE),
+                                        errmsg("current user cannot be dropped")));
                if (roleid == GetSessionUserId())
                        ereport(ERROR,
                                        (errcode(ERRCODE_OBJECT_IN_USE),
-                                        errmsg("session role cannot be dropped")));
+                                        errmsg("session user cannot be dropped")));
 
                /*
                 * For safety's sake, we allow createrole holders to drop ordinary
@@ -893,7 +898,8 @@ RenameRole(const char *oldname, const char *newname)
         * XXX Client applications probably store the session user somewhere,
         * so renaming it could cause confusion.  On the other hand, there may
         * not be an actual problem besides a little confusion, so think about
-        * this and decide.
+        * this and decide.  Same for SET ROLE ... we don't restrict renaming
+        * the current effective userid, though.
         */
 
        roleid = HeapTupleGetOid(oldtuple);
@@ -901,7 +907,11 @@ RenameRole(const char *oldname, const char *newname)
        if (roleid == GetSessionUserId())
                ereport(ERROR,
                                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-                                errmsg("session role may not be renamed")));
+                                errmsg("session user may not be renamed")));
+       if (roleid == GetOuterUserId())
+               ereport(ERROR,
+                               (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                                errmsg("current user may not be renamed")));
 
        /* make sure the new name doesn't exist */
        if (SearchSysCacheExists(AUTHNAME,
@@ -911,6 +921,13 @@ RenameRole(const char *oldname, const char *newname)
                                (errcode(ERRCODE_DUPLICATE_OBJECT),
                                 errmsg("role \"%s\" already exists", newname)));
 
+       if (strcmp(newname, "public") == 0 ||
+               strcmp(newname, "none") == 0)
+               ereport(ERROR,
+                               (errcode(ERRCODE_RESERVED_NAME),
+                                errmsg("role name \"%s\" is reserved",
+                                               newname)));
+
        /*
         * createrole is enough privilege unless you want to mess with a superuser
         */
index 494ab6b491e10bebfa9cdd1531b4643644f492fe..9254d57e345bcaf99727be68cd3cc4106e420865 100644 (file)
@@ -9,7 +9,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/commands/variable.c,v 1.111 2005/07/21 03:56:10 momjian Exp $
+ *       $PostgreSQL: pgsql/src/backend/commands/variable.c,v 1.112 2005/07/25 22:12:32 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -24,6 +24,7 @@
 #include "miscadmin.h"
 #include "parser/scansup.h"
 #include "pgtime.h"
+#include "utils/acl.h"
 #include "utils/builtins.h"
 #include "utils/guc.h"
 #include "utils/syscache.h"
@@ -684,3 +685,142 @@ show_session_authorization(void)
 
        return endptr + 1;
 }
+
+
+/*
+ * SET ROLE
+ *
+ * When resetting session auth after an error, we can't expect to do catalog
+ * lookups.  Hence, the stored form of the value must provide a numeric oid
+ * that can be re-used directly.  We implement this exactly like SET
+ * SESSION AUTHORIZATION.
+ *
+ * The SQL spec requires "SET ROLE NONE" to unset the role, so we hardwire
+ * a translation of "none" to InvalidOid.
+ */
+extern char *role_string;              /* in guc.c */
+
+const char *
+assign_role(const char *value, bool doit, GucSource source)
+{
+       Oid             roleid = InvalidOid;
+       bool            is_superuser = false;
+       const char *actual_rolename = value;
+       char       *result;
+
+       if (strspn(value, "x") == NAMEDATALEN &&
+               (value[NAMEDATALEN] == 'T' || value[NAMEDATALEN] == 'F'))
+       {
+               /* might be a saved userid string */
+               Oid             savedoid;
+               char       *endptr;
+
+               savedoid = (Oid) strtoul(value + NAMEDATALEN + 1, &endptr, 10);
+
+               if (endptr != value + NAMEDATALEN + 1 && *endptr == ',')
+               {
+                       /* syntactically valid, so break out the data */
+                       roleid = savedoid;
+                       is_superuser = (value[NAMEDATALEN] == 'T');
+                       actual_rolename = endptr + 1;
+               }
+       }
+
+       if (roleid == InvalidOid &&
+               strcmp(actual_rolename, "none") != 0)
+       {
+               /* not a saved ID, so look it up */
+               HeapTuple       roleTup;
+
+               if (!IsTransactionState())
+               {
+                       /*
+                        * Can't do catalog lookups, so fail.  The upshot of this is
+                        * that role cannot be set in postgresql.conf, which seems 
+                        * like a good thing anyway.
+                        */
+                       return NULL;
+               }
+
+               roleTup = SearchSysCache(AUTHNAME,
+                                                                PointerGetDatum(value),
+                                                                0, 0, 0);
+               if (!HeapTupleIsValid(roleTup))
+               {
+                       if (source >= PGC_S_INTERACTIVE)
+                               ereport(ERROR,
+                                               (errcode(ERRCODE_UNDEFINED_OBJECT),
+                                                errmsg("role \"%s\" does not exist", value)));
+                       return NULL;
+               }
+
+               roleid = HeapTupleGetOid(roleTup);
+               is_superuser = ((Form_pg_authid) GETSTRUCT(roleTup))->rolsuper;
+
+               ReleaseSysCache(roleTup);
+
+               /*
+                * Verify that session user is allowed to become this role
+                */
+               if (!is_member_of_role(GetSessionUserId(), roleid))
+               {
+                       if (source >= PGC_S_INTERACTIVE)
+                               ereport(ERROR,
+                                               (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+                                                errmsg("permission denied to set role \"%s\"",
+                                                               value)));
+                       return NULL;
+               }
+       }
+
+       if (doit)
+               SetCurrentRoleId(roleid, is_superuser);
+
+       result = (char *) malloc(NAMEDATALEN + 32 + strlen(actual_rolename));
+       if (!result)
+               return NULL;
+
+       memset(result, 'x', NAMEDATALEN);
+
+       sprintf(result + NAMEDATALEN, "%c%u,%s",
+                       is_superuser ? 'T' : 'F',
+                       roleid,
+                       actual_rolename);
+
+       return result;
+}
+
+const char *
+show_role(void)
+{
+       /*
+        * Extract the role name from the stored string; see
+        * assign_role
+        */
+       const char *value = role_string;
+       Oid             savedoid;
+       char       *endptr;
+
+       /* This special case only applies if no SET ROLE has been done */
+       if (value == NULL || strcmp(value, "none") == 0)
+               return "none";
+
+       Assert(strspn(value, "x") == NAMEDATALEN &&
+                  (value[NAMEDATALEN] == 'T' || value[NAMEDATALEN] == 'F'));
+
+       savedoid = (Oid) strtoul(value + NAMEDATALEN + 1, &endptr, 10);
+
+       Assert(endptr != value + NAMEDATALEN + 1 && *endptr == ',');
+
+       /*
+        * Check that the stored string still matches the effective setting,
+        * else return "none".  This is a kluge to deal with the fact that
+        * SET SESSION AUTHORIZATION logically resets SET ROLE to NONE, but
+        * we cannot set the GUC role variable from assign_session_authorization
+        * (because we haven't got enough info to call set_config_option).
+        */
+       if (savedoid != GetCurrentRoleId())
+               return "none";
+
+       return endptr + 1;
+}
index 8afc948a07a88ccf049837124ef7cb4246f7784a..3730068915f16b03530cbe733a997fb0b53fc0e0 100644 (file)
@@ -11,7 +11,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.501 2005/06/29 20:34:13 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.502 2005/07/25 22:12:32 tgl Exp $
  *
  * HISTORY
  *       AUTHOR                        DATE                    MAJOR EVENT
@@ -1004,6 +1004,13 @@ set_rest:  var_name TO var_list_or_default
                                                n->args = list_make1(makeStringConst($2, NULL));
                                        $$ = n;
                                }
+                       | ROLE ColId_or_Sconst
+                               {
+                                       VariableSetStmt *n = makeNode(VariableSetStmt);
+                                       n->name = "role";
+                                       n->args = list_make1(makeStringConst($2, NULL));
+                                       $$ = n;
+                               }
                        | SESSION AUTHORIZATION ColId_or_Sconst
                                {
                                        VariableSetStmt *n = makeNode(VariableSetStmt);
index 389ad06f2fe8a8560ddea4cd2de85fc9a395e525..66d6d1725e082c810e27ec8bb8b4ba5e101ef69e 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/utils/init/miscinit.c,v 1.146 2005/07/14 05:13:41 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/utils/init/miscinit.c,v 1.147 2005/07/25 22:12:33 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -270,24 +270,44 @@ make_absolute_path(const char *path)
 
 
 /* ----------------------------------------------------------------
- *     Role ID things
+ *     User ID state
  *
- * The authenticated user is determined at connection start and never
- * changes.  The session user can be changed only by SET SESSION
- * AUTHORIZATION.  The current user may change when "setuid" functions
- * are implemented.  Conceptually there is a stack, whose bottom
- * is the session user.  You are yourself responsible to save and
- * restore the current user id if you need to change it.
+ * We have to track several different values associated with the concept
+ * of "user ID".
+ *
+ * AuthenticatedUserId is determined at connection start and never changes.
+ *
+ * SessionUserId is initially the same as AuthenticatedUserId, but can be
+ * changed by SET SESSION AUTHORIZATION (if AuthenticatedUserIsSuperuser).
+ * This is the ID reported by the SESSION_USER SQL function.
+ *
+ * OuterUserId is the current user ID in effect at the "outer level" (outside
+ * any transaction or function).  This is initially the same as SessionUserId,
+ * but can be changed by SET ROLE to any role that SessionUserId is a
+ * member of.  We store this mainly so that AbortTransaction knows what to
+ * reset CurrentUserId to.
+ *
+ * CurrentUserId is the current effective user ID; this is the one to use
+ * for all normal permissions-checking purposes.  At outer level this will
+ * be the same as OuterUserId, but it changes during calls to SECURITY
+ * DEFINER functions, as well as locally in some specialized commands.
  * ----------------------------------------------------------------
  */
 static Oid AuthenticatedUserId = InvalidOid;
 static Oid SessionUserId = InvalidOid;
+static Oid OuterUserId = InvalidOid;
 static Oid CurrentUserId = InvalidOid;
 
+/* We also have to remember the superuser state of some of these levels */
 static bool AuthenticatedUserIsSuperuser = false;
+static bool SessionUserIsSuperuser = false;
+
+/* We also remember if a SET ROLE is currently active */
+static bool SetRoleIsActive = false;
+
 
 /*
- * This function is relevant for all privilege checks.
+ * GetUserId/SetUserId - get/set the current effective user ID.
  */
 Oid
 GetUserId(void)
@@ -298,15 +318,37 @@ GetUserId(void)
 
 
 void
-SetUserId(Oid roleid)
+SetUserId(Oid userid)
 {
-       AssertArg(OidIsValid(roleid));
-       CurrentUserId = roleid;
+       AssertArg(OidIsValid(userid));
+       CurrentUserId = userid;
 }
 
 
 /*
- * This value is only relevant for informational purposes.
+ * GetOuterUserId/SetOuterUserId - get/set the outer-level user ID.
+ */
+Oid
+GetOuterUserId(void)
+{
+       AssertState(OidIsValid(OuterUserId));
+       return OuterUserId;
+}
+
+
+static void
+SetOuterUserId(Oid userid)
+{
+       AssertArg(OidIsValid(userid));
+       OuterUserId = userid;
+
+       /* We force the effective user ID to match, too */
+       CurrentUserId = userid;
+}
+
+
+/*
+ * GetSessionUserId/SetSessionUserId - get/set the session user ID.
  */
 Oid
 GetSessionUserId(void)
@@ -316,17 +358,23 @@ GetSessionUserId(void)
 }
 
 
-void
-SetSessionUserId(Oid roleid)
+static void
+SetSessionUserId(Oid userid, bool is_superuser)
 {
-       AssertArg(OidIsValid(roleid));
-       SessionUserId = roleid;
-       /* Current user defaults to session user. */
-       if (!OidIsValid(CurrentUserId))
-               CurrentUserId = roleid;
+       AssertArg(OidIsValid(userid));
+       SessionUserId = userid;
+       SessionUserIsSuperuser = is_superuser;
+       SetRoleIsActive = false;
+
+       /* We force the effective user IDs to match, too */
+       OuterUserId = userid;
+       CurrentUserId = userid;
 }
 
 
+/*
+ * Initialize user identity during normal backend startup
+ */
 void
 InitializeSessionUserId(const char *rolename)
 {
@@ -364,7 +412,8 @@ InitializeSessionUserId(const char *rolename)
        AuthenticatedUserId = roleid;
        AuthenticatedUserIsSuperuser = rform->rolsuper;
 
-       SetSessionUserId(roleid);       /* sets CurrentUserId too */
+       /* This sets OuterUserId/CurrentUserId too */
+       SetSessionUserId(roleid, AuthenticatedUserIsSuperuser);
 
        /* Record username and superuser status as GUC settings too */
        SetConfigOption("session_authorization", rolename,
@@ -391,6 +440,9 @@ InitializeSessionUserId(const char *rolename)
 }
 
 
+/*
+ * Initialize user identity during special backend startup
+ */
 void
 InitializeSessionUserIdStandalone(void)
 {
@@ -403,7 +455,7 @@ InitializeSessionUserIdStandalone(void)
        AuthenticatedUserId = BOOTSTRAP_SUPERUSERID;
        AuthenticatedUserIsSuperuser = true;
 
-       SetSessionUserId(BOOTSTRAP_SUPERUSERID);
+       SetSessionUserId(BOOTSTRAP_SUPERUSERID, true);
 }
 
 
@@ -414,21 +466,82 @@ InitializeSessionUserIdStandalone(void)
  * that in case of multiple SETs in a single session, the original userid's
  * superuserness is what matters.  But we set the GUC variable is_superuser
  * to indicate whether the *current* session userid is a superuser.
+ *
+ * Note: this is not an especially clean place to do the permission check.
+ * It's OK because the check does not require catalog access and can't
+ * fail during an end-of-transaction GUC reversion, but we may someday
+ * have to push it up into assign_session_authorization.
  */
 void
-SetSessionAuthorization(Oid roleid, bool is_superuser)
+SetSessionAuthorization(Oid userid, bool is_superuser)
 {
        /* Must have authenticated already, else can't make permission check */
        AssertState(OidIsValid(AuthenticatedUserId));
 
-       if (roleid != AuthenticatedUserId &&
+       if (userid != AuthenticatedUserId &&
                !AuthenticatedUserIsSuperuser)
                ereport(ERROR,
                                (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
                          errmsg("permission denied to set session authorization")));
 
-       SetSessionUserId(roleid);
-       SetUserId(roleid);
+       SetSessionUserId(userid, is_superuser);
+
+       SetConfigOption("is_superuser",
+                                       is_superuser ? "on" : "off",
+                                       PGC_INTERNAL, PGC_S_OVERRIDE);
+}
+
+/*
+ * Report current role id
+ *             This follows the semantics of SET ROLE, ie return the outer-level ID
+ *             not the current effective ID, and return InvalidOid when the setting
+ *             is logically SET ROLE NONE.
+ */
+Oid
+GetCurrentRoleId(void)
+{
+       if (SetRoleIsActive)
+               return OuterUserId;
+       else
+               return InvalidOid;
+}
+
+/*
+ * Change Role ID while running (SET ROLE)
+ *
+ * If roleid is InvalidOid, we are doing SET ROLE NONE: revert to the
+ * session user authorization.  In this case the is_superuser argument
+ * is ignored.
+ *
+ * When roleid is not InvalidOid, the caller must have checked whether
+ * the session user has permission to become that role.  (We cannot check
+ * here because this routine must be able to execute in a failed transaction
+ * to restore a prior value of the ROLE GUC variable.)
+ */
+void
+SetCurrentRoleId(Oid roleid, bool is_superuser)
+{
+       /*
+        * Get correct info if it's SET ROLE NONE
+        *
+        * If SessionUserId hasn't been set yet, just do nothing --- the eventual
+        * SetSessionUserId call will fix everything.  This is needed since we
+        * will get called during GUC initialization.
+        */
+       if (!OidIsValid(roleid))
+       {
+               if (!OidIsValid(SessionUserId))
+                       return;
+
+               roleid = SessionUserId;
+               is_superuser = SessionUserIsSuperuser;
+
+               SetRoleIsActive = false;
+       }
+       else
+               SetRoleIsActive = true;
+
+       SetOuterUserId(roleid);
 
        SetConfigOption("is_superuser",
                                        is_superuser ? "on" : "off",
index 5b545d5f4368586191e588951b2bd49197f384ca..3332e636427bc8479ee206393538d9d0fe1404b1 100755 (executable)
@@ -18,7 +18,7 @@
 ## can be ignored
 INTENTIONALLY_NOT_INCLUDED="autocommit debug_deadlocks exit_on_error \
 is_superuser lc_collate lc_ctype lc_messages lc_monetary lc_numeric lc_time \
-pre_auth_delay seed server_encoding server_version session_authorization \
+pre_auth_delay role seed server_encoding server_version session_authorization \
 trace_lock_oidmin trace_lock_table trace_locks trace_lwlocks trace_notify \
 trace_userlocks transaction_isolation transaction_read_only \
 zero_damaged_pages"
index 6400ef566b2b9f3a865b93980e3951cf6a763775..726a093d0d7427b89da4fd73a49dbe576838deaa 100644 (file)
@@ -10,7 +10,7 @@
  * Written by Peter Eisentraut <peter_e@gmx.net>.
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.277 2005/07/23 21:05:47 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.278 2005/07/25 22:12:33 tgl Exp $
  *
  *--------------------------------------------------------------------
  */
@@ -195,7 +195,8 @@ static int  block_size;
 static bool    integer_datetimes;
 static bool    standard_compliant_strings;
 
-/* should be static, but commands/variable.c needs to get at it */
+/* should be static, but commands/variable.c needs to get at these */
+char      *role_string;
 char      *session_authorization_string;
 
 
@@ -1828,6 +1829,17 @@ static struct config_string ConfigureNamesString[] =
                PG_VERSION, NULL, NULL
        },
 
+       {
+               /* Not for general use --- used by SET ROLE */
+               {"role", PGC_USERSET, UNGROUPED,
+                       gettext_noop("Sets the current role."),
+                       NULL,
+                       GUC_NO_SHOW_ALL | GUC_NO_RESET_ALL | GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE
+               },
+               &role_string,
+               "none", assign_role, show_role
+       },
+
        {
                /* Not for general use --- used by SET SESSION AUTHORIZATION */
                {"session_authorization", PGC_USERSET, UNGROUPED,
@@ -2048,8 +2060,6 @@ static bool guc_dirty;                    /* TRUE if need to do commit/abort work */
 
 static bool reporting_enabled; /* TRUE to enable GUC_REPORT */
 
-static char *guc_string_workspace;             /* for avoiding memory leaks */
-
 
 static int     guc_var_compare(const void *a, const void *b);
 static int     guc_name_compare(const char *namea, const char *nameb);
@@ -2576,8 +2586,6 @@ InitializeGUCOptions(void)
 
        reporting_enabled = false;
 
-       guc_string_workspace = NULL;
-
        /*
         * Prevent any attempt to override the transaction modes from
         * non-interactive sources.
@@ -2976,13 +2984,6 @@ AtEOXact_GUC(bool isCommit, bool isSubXact)
        if (!guc_dirty)
                return;
 
-       /* Prevent memory leak if ereport during an assign_hook */
-       if (guc_string_workspace)
-       {
-               free(guc_string_workspace);
-               guc_string_workspace = NULL;
-       }
-
        my_level = GetCurrentTransactionNestLevel();
        Assert(isSubXact ? (my_level > 1) : (my_level == 1));
 
@@ -3389,6 +3390,33 @@ parse_real(const char *value, double *result)
 }
 
 
+/*
+ * Call a GucStringAssignHook function, being careful to free the
+ * "newval" string if the hook ereports.
+ *
+ * This is split out of set_config_option just to avoid the "volatile"
+ * qualifiers that would otherwise have to be plastered all over.
+ */
+static const char *
+call_string_assign_hook(GucStringAssignHook assign_hook,
+                                               char *newval, bool doit, GucSource source)
+{
+       const char *result;
+
+       PG_TRY();
+       {
+               result = (*assign_hook) (newval, doit, source);
+       }
+       PG_CATCH();
+       {
+               free(newval);
+               PG_RE_THROW();
+       }
+       PG_END_TRY();
+
+       return result;
+}
+
 
 /*
  * Sets option `name' to given value. The value should be a string
@@ -3833,21 +3861,18 @@ set_config_option(const char *name, const char *value,
                                        break;
                                }
 
-                               /*
-                                * Remember string in workspace, so that we can free it
-                                * and avoid a permanent memory leak if hook ereports.
-                                */
-                               if (guc_string_workspace)
-                                       free(guc_string_workspace);
-                               guc_string_workspace = newval;
-
                                if (conf->assign_hook)
                                {
                                        const char *hookresult;
 
-                                       hookresult = (*conf->assign_hook) (newval,
-                                                                                                          changeVal, source);
-                                       guc_string_workspace = NULL;
+                                       /*
+                                        * If the hook ereports, we have to make sure we free
+                                        * newval, else it will be a permanent memory leak.
+                                        */
+                                       hookresult = call_string_assign_hook(conf->assign_hook,
+                                                                                                                newval,
+                                                                                                                changeVal,
+                                                                                                                source);
                                        if (hookresult == NULL)
                                        {
                                                free(newval);
@@ -3874,8 +3899,6 @@ set_config_option(const char *name, const char *value,
                                        }
                                }
 
-                               guc_string_workspace = NULL;
-
                                if (changeVal || makeDefault)
                                {
                                        /* Save old value to support transaction abort */
@@ -4305,8 +4328,7 @@ init_custom_variable(struct config_generic * gen,
 }
 
 void
-DefineCustomBoolVariable(
-                                                const char *name,
+DefineCustomBoolVariable(const char *name,
                                                 const char *short_desc,
                                                 const char *long_desc,
                                                 bool *valueAddr,
@@ -4328,8 +4350,7 @@ DefineCustomBoolVariable(
 }
 
 void
-DefineCustomIntVariable(
-                                               const char *name,
+DefineCustomIntVariable(const char *name,
                                                const char *short_desc,
                                                const char *long_desc,
                                                int *valueAddr,
@@ -4355,8 +4376,7 @@ DefineCustomIntVariable(
 }
 
 void
-DefineCustomRealVariable(
-                                                const char *name,
+DefineCustomRealVariable(const char *name,
                                                 const char *short_desc,
                                                 const char *long_desc,
                                                 double *valueAddr,
@@ -4382,8 +4402,7 @@ DefineCustomRealVariable(
 }
 
 void
-DefineCustomStringVariable(
-                                                  const char *name,
+DefineCustomStringVariable(const char *name,
                                                   const char *short_desc,
                                                   const char *long_desc,
                                                   char **valueAddr,
index f3d4dc681c82727a808e978b51768c09cf1adb91..9814336325dfe1e234c1ea2f429692e773e02b6f 100644 (file)
@@ -5,7 +5,7 @@
  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/commands/variable.h,v 1.25 2004/12/31 22:03:28 pgsql Exp $
+ * $PostgreSQL: pgsql/src/include/commands/variable.h,v 1.26 2005/07/25 22:12:34 tgl Exp $
  */
 #ifndef VARIABLE_H
 #define VARIABLE_H
@@ -26,6 +26,9 @@ extern bool assign_random_seed(double value,
 extern const char *show_random_seed(void);
 extern const char *assign_client_encoding(const char *value,
                                           bool doit, GucSource source);
+extern const char *assign_role(const char *value,
+                                                        bool doit, GucSource source);
+extern const char *show_role(void);
 extern const char *assign_session_authorization(const char *value,
                                                         bool doit, GucSource source);
 extern const char *show_session_authorization(void);
index 1ee085e51a00343ee55f4b28394dd2971ac98152..5697a691e63a2636114d616cda7cec9bb3d80b4f 100644 (file)
@@ -13,7 +13,7 @@
  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/miscadmin.h,v 1.177 2005/07/04 04:51:52 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/miscadmin.h,v 1.178 2005/07/25 22:12:34 tgl Exp $
  *
  * NOTES
  *       some of the information in this file should be moved to other files.
@@ -230,12 +230,14 @@ extern void SetDatabasePath(const char *path);
 
 extern char *GetUserNameFromId(Oid roleid);
 extern Oid GetUserId(void);
-extern void SetUserId(Oid roleid);
+extern void SetUserId(Oid userid);
+extern Oid GetOuterUserId(void);
 extern Oid GetSessionUserId(void);
-extern void SetSessionUserId(Oid roleid);
 extern void InitializeSessionUserId(const char *rolename);
 extern void InitializeSessionUserIdStandalone(void);
-extern void SetSessionAuthorization(Oid roleid, bool is_superuser);
+extern void SetSessionAuthorization(Oid userid, bool is_superuser);
+extern Oid GetCurrentRoleId(void);
+extern void SetCurrentRoleId(Oid roleid, bool is_superuser);
 
 extern void SetDataDir(const char *dir);
 extern void ChangeToDataDir(void);