From e5d6b91220d69c87f44e1ce0095516946abc6d6c Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Mon, 25 Jul 2005 22:12:34 +0000 Subject: [PATCH] Add SET ROLE. This is a partial commit of Stephen Frost's recent patch; I'm still working on the has_role function and information_schema changes. --- doc/src/sgml/func.sgml | 6 +- doc/src/sgml/ref/allfiles.sgml | 3 +- doc/src/sgml/ref/pg_dump.sgml | 6 +- doc/src/sgml/ref/pg_dumpall.sgml | 6 +- doc/src/sgml/ref/pg_restore.sgml | 6 +- doc/src/sgml/ref/set_role.sgml | 116 +++++++++++++++++ doc/src/sgml/ref/set_session_auth.sgml | 29 +++-- doc/src/sgml/reference.sgml | 3 +- src/backend/access/transam/xact.c | 6 +- src/backend/commands/user.c | 29 ++++- src/backend/commands/variable.c | 142 ++++++++++++++++++++- src/backend/parser/gram.y | 9 +- src/backend/utils/init/miscinit.c | 165 +++++++++++++++++++++---- src/backend/utils/misc/check_guc | 2 +- src/backend/utils/misc/guc.c | 87 ++++++++----- src/include/commands/variable.h | 5 +- src/include/miscadmin.h | 10 +- 17 files changed, 533 insertions(+), 97 deletions(-) create mode 100644 doc/src/sgml/ref/set_role.sgml diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml index 59813e16f1..3708735515 100644 --- a/doc/src/sgml/func.sgml +++ b/doc/src/sgml/func.sgml @@ -1,5 +1,5 @@ @@ -8266,7 +8266,9 @@ select current_date + s.a as dates from generate_series(0,14,7) as s(a); with . The current_user 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 + . + It also changes during the execution of functions with the attribute SECURITY DEFINER. In Unix parlance, the session user is the real user and the current user is the effective user. diff --git a/doc/src/sgml/ref/allfiles.sgml b/doc/src/sgml/ref/allfiles.sgml index 33e9e68b9d..d993b64ad0 100644 --- a/doc/src/sgml/ref/allfiles.sgml +++ b/doc/src/sgml/ref/allfiles.sgml @@ -1,5 +1,5 @@ @@ -102,6 +102,7 @@ Complete list of usable sgml source files in this directory. + diff --git a/doc/src/sgml/ref/pg_dump.sgml b/doc/src/sgml/ref/pg_dump.sgml index a6d8bb2407..288ae20a41 100644 --- a/doc/src/sgml/ref/pg_dump.sgml +++ b/doc/src/sgml/ref/pg_dump.sgml @@ -1,5 +1,5 @@ @@ -474,8 +474,8 @@ PostgreSQL documentation - 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. diff --git a/doc/src/sgml/ref/pg_dumpall.sgml b/doc/src/sgml/ref/pg_dumpall.sgml index c61ae09404..4cee1a4ed7 100644 --- a/doc/src/sgml/ref/pg_dumpall.sgml +++ b/doc/src/sgml/ref/pg_dumpall.sgml @@ -1,5 +1,5 @@ @@ -277,8 +277,8 @@ PostgreSQL documentation - 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. diff --git a/doc/src/sgml/ref/pg_restore.sgml b/doc/src/sgml/ref/pg_restore.sgml index 9b2b5fc3f2..d4a1a3e0f0 100644 --- a/doc/src/sgml/ref/pg_restore.sgml +++ b/doc/src/sgml/ref/pg_restore.sgml @@ -1,4 +1,4 @@ - + @@ -361,8 +361,8 @@ - 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. diff --git a/doc/src/sgml/ref/set_role.sgml b/doc/src/sgml/ref/set_role.sgml new file mode 100644 index 0000000000..6fbe40fabf --- /dev/null +++ b/doc/src/sgml/ref/set_role.sgml @@ -0,0 +1,116 @@ + + + + SET ROLE + SQL - Language Statements + + + + SET ROLE + set the current user identifier of the current session + + + + SET ROLE + + + + +SET [ SESSION | LOCAL ] ROLE rolename +SET [ SESSION | LOCAL ] ROLE NONE +RESET ROLE + + + + + Description + + + This command sets the current user + identifier of the current SQL-session context to be rolename. 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. + + + + The specified rolename + 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.) + + + + The SESSION and LOCAL modifiers act the same + as for the regular + command. + + + + The NONE and RESET forms reset the current + user identifier to be the current session user identifier. + These forms may be executed by any user. + + + + + Examples + + +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 + + + + + Compatibility + + + PostgreSQL + allows identifier syntax ("rolename"), while + the SQL standard requires the role name to be written as a string + literal. SQL does not allow this command during a transaction; + PostgreSQL does not make this + restriction because there is no reason to. + The SESSION and LOCAL modifiers are a + PostgreSQL extension, as is the + RESET syntax. + + + + + See Also + + + + + + + + diff --git a/doc/src/sgml/ref/set_session_auth.sgml b/doc/src/sgml/ref/set_session_auth.sgml index 7014b8d2ab..334847fb00 100644 --- a/doc/src/sgml/ref/set_session_auth.sgml +++ b/doc/src/sgml/ref/set_session_auth.sgml @@ -1,4 +1,4 @@ - + SET SESSION AUTHORIZATION @@ -31,7 +31,7 @@ RESET SESSION AUTHORIZATION class="parameter">username. 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. @@ -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 setuid - functions and similar mechanisms. The current user identifier is - relevant for permission checking. + functions and similar mechanisms; it can also be changed by + . + The current user identifier is relevant for permission checking. @@ -93,10 +94,24 @@ SELECT SESSION_USER, CURRENT_USER; allows identifier syntax ("username"), which SQL does not. SQL does not allow this command during a transaction; PostgreSQL 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 SESSION and LOCAL modifiers are a + PostgreSQL extension, as is the + RESET syntax. + + + The privileges necessary to execute this command are left + implementation-defined by the standard. + + + + + See Also + + + + diff --git a/doc/src/sgml/reference.sgml b/doc/src/sgml/reference.sgml index 4edec85c12..63ecfe1204 100644 --- a/doc/src/sgml/reference.sgml +++ b/doc/src/sgml/reference.sgml @@ -1,5 +1,5 @@ @@ -134,6 +134,7 @@ PostgreSQL Reference Manual &selectInto; &set; &setConstraints; + &setRole; &setSessionAuth; &setTransaction; &show; diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c index c75da3d432..ee33030292 100644 --- a/src/backend/access/transam/xact.c +++ b/src/backend/access/transam/xact.c @@ -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 diff --git a/src/backend/commands/user.c b/src/backend/commands/user.c index 4a46343d5d..5f8eeae30d 100644 --- a/src/backend/commands/user.c +++ b/src/backend/commands/user.c @@ -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 */ diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index 494ab6b491..9254d57e34 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -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; +} diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 8afc948a07..3730068915 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -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); diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index 389ad06f2f..66d6d1725e 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -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", diff --git a/src/backend/utils/misc/check_guc b/src/backend/utils/misc/check_guc index 5b545d5f43..3332e63642 100755 --- a/src/backend/utils/misc/check_guc +++ b/src/backend/utils/misc/check_guc @@ -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" diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c index 6400ef566b..726a093d0d 100644 --- a/src/backend/utils/misc/guc.c +++ b/src/backend/utils/misc/guc.c @@ -10,7 +10,7 @@ * Written by Peter Eisentraut . * * 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, diff --git a/src/include/commands/variable.h b/src/include/commands/variable.h index f3d4dc681c..9814336325 100644 --- a/src/include/commands/variable.h +++ b/src/include/commands/variable.h @@ -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); diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h index 1ee085e51a..5697a691e6 100644 --- a/src/include/miscadmin.h +++ b/src/include/miscadmin.h @@ -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);