1
0
mirror of https://github.com/postgres/postgres.git synced 2025-07-03 20:02:46 +03:00

Allow CURRENT/SESSION_USER to be used in certain commands

Commands such as ALTER USER, ALTER GROUP, ALTER ROLE, GRANT, and the
various ALTER OBJECT / OWNER TO, as well as ad-hoc clauses related to
roles such as the AUTHORIZATION clause of CREATE SCHEMA, the FOR clause
of CREATE USER MAPPING, and the FOR ROLE clause of ALTER DEFAULT
PRIVILEGES can now take the keywords CURRENT_USER and SESSION_USER as
user specifiers in place of an explicit user name.

This commit also fixes some quite ugly handling of special standards-
mandated syntax in CREATE USER MAPPING, which in particular would fail
to work in presence of a role named "current_user".

The special role specifiers PUBLIC and NONE also have more consistent
handling now.

Also take the opportunity to add location tracking to user specifiers.

Authors: Kyotaro Horiguchi.  Heavily reworked by Álvaro Herrera.
Reviewed by: Rushabh Lathia, Adam Brightwell, Marti Raudsepp.
This commit is contained in:
Alvaro Herrera
2015-03-09 15:41:54 -03:00
parent fa83f80945
commit 31eae6028e
52 changed files with 2000 additions and 347 deletions

View File

@ -699,7 +699,7 @@ AlterObjectNamespace_internal(Relation rel, Oid objid, Oid nspOid)
ObjectAddress
ExecAlterOwnerStmt(AlterOwnerStmt *stmt)
{
Oid newowner = get_role_oid(stmt->newowner, false);
Oid newowner = get_rolespec_oid(stmt->newowner, false);
switch (stmt->objectType)
{

View File

@ -1371,7 +1371,7 @@ CreateExtension(CreateExtensionStmt *stmt)
CreateSchemaStmt *csstmt = makeNode(CreateSchemaStmt);
csstmt->schemaname = schemaName;
csstmt->authid = NULL; /* will be created by current user */
csstmt->authrole = NULL; /* will be created by current user */
csstmt->schemaElts = NIL;
csstmt->if_not_exists = false;
CreateSchemaCommand(csstmt, NULL);

View File

@ -197,24 +197,6 @@ transformGenericOptions(Oid catalogId,
}
/*
* Convert the user mapping user name to OID
*/
static Oid
GetUserOidFromMapping(const char *username, bool missing_ok)
{
if (!username)
/* PUBLIC user mapping */
return InvalidOid;
if (strcmp(username, "current_user") == 0)
/* map to the owner */
return GetUserId();
/* map to provided user */
return get_role_oid(username, missing_ok);
}
/*
* Internal workhorse for changing a data wrapper's owner.
*
@ -1156,10 +1138,14 @@ CreateUserMapping(CreateUserMappingStmt *stmt)
ObjectAddress referenced;
ForeignServer *srv;
ForeignDataWrapper *fdw;
RoleSpec *role = (RoleSpec *) stmt->user;
rel = heap_open(UserMappingRelationId, RowExclusiveLock);
useId = GetUserOidFromMapping(stmt->username, false);
if (role->roletype == ROLESPEC_PUBLIC)
useId = ACL_ID_PUBLIC;
else
useId = get_rolespec_oid(stmt->user, false);
/* Check that the server exists. */
srv = GetForeignServerByName(stmt->servername, false);
@ -1252,10 +1238,15 @@ AlterUserMapping(AlterUserMappingStmt *stmt)
Oid umId;
ForeignServer *srv;
ObjectAddress address;
RoleSpec *role = (RoleSpec *) stmt->user;
rel = heap_open(UserMappingRelationId, RowExclusiveLock);
useId = GetUserOidFromMapping(stmt->username, false);
if (role->roletype == ROLESPEC_PUBLIC)
useId = ACL_ID_PUBLIC;
else
useId = get_rolespec_oid(stmt->user, false);
srv = GetForeignServerByName(stmt->servername, false);
umId = GetSysCacheOid2(USERMAPPINGUSERSERVER,
@ -1338,20 +1329,27 @@ RemoveUserMapping(DropUserMappingStmt *stmt)
Oid useId;
Oid umId;
ForeignServer *srv;
RoleSpec *role = (RoleSpec *) stmt->user;
useId = GetUserOidFromMapping(stmt->username, stmt->missing_ok);
srv = GetForeignServerByName(stmt->servername, true);
if (stmt->username && !OidIsValid(useId))
if (role->roletype == ROLESPEC_PUBLIC)
useId = ACL_ID_PUBLIC;
else
{
/*
* IF EXISTS specified, role not found and not public. Notice this and
* leave.
*/
elog(NOTICE, "role \"%s\" does not exist, skipping", stmt->username);
return InvalidOid;
useId = get_rolespec_oid(stmt->user, stmt->missing_ok);
if (!OidIsValid(useId))
{
/*
* IF EXISTS specified, role not found and not public. Notice this
* and leave.
*/
elog(NOTICE, "role \"%s\" does not exist, skipping",
role->rolename);
return InvalidOid;
}
}
srv = GetForeignServerByName(stmt->servername, true);
if (!srv)
{
if (!stmt->missing_ok)

View File

@ -129,13 +129,7 @@ parse_policy_command(const char *cmd_name)
/*
* policy_role_list_to_array
* helper function to convert a list of role names in to an array of
* role ids.
*
* Note: If PUBLIC is provided as a role name, then ACL_ID_PUBLIC is
* used as the role id.
*
* roles - the list of role names to convert.
* helper function to convert a list of RoleSpecs to an array of role ids.
*/
static ArrayType *
policy_role_list_to_array(List *roles)
@ -162,25 +156,25 @@ policy_role_list_to_array(List *roles)
foreach(cell, roles)
{
Oid roleid = get_role_oid_or_public(strVal(lfirst(cell)));
RoleSpec *spec = lfirst(cell);
/*
* PUBLIC covers all roles, so it only makes sense alone.
*/
if (roleid == ACL_ID_PUBLIC)
if (spec->roletype == ROLESPEC_PUBLIC)
{
if (num_roles != 1)
ereport(WARNING,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("ignoring roles specified other than public"),
errhint("All roles are members of the public role.")));
temp_array[0] = ObjectIdGetDatum(roleid);
temp_array[0] = ObjectIdGetDatum(ACL_ID_PUBLIC);
num_roles = 1;
break;
}
else
temp_array[i++] = ObjectIdGetDatum(roleid);
temp_array[i++] =
ObjectIdGetDatum(get_rolespec_oid((Node *) spec, false));
}
role_ids = construct_array(temp_array, num_roles, OIDOID, sizeof(Oid), true,

View File

@ -21,6 +21,7 @@
#include "catalog/dependency.h"
#include "catalog/indexing.h"
#include "catalog/namespace.h"
#include "catalog/pg_authid.h"
#include "catalog/objectaccess.h"
#include "catalog/pg_namespace.h"
#include "commands/dbcommands.h"
@ -42,8 +43,7 @@ static void AlterSchemaOwner_internal(HeapTuple tup, Relation rel, Oid newOwnerI
Oid
CreateSchemaCommand(CreateSchemaStmt *stmt, const char *queryString)
{
const char *schemaName = stmt->schemaname;
const char *authId = stmt->authid;
const char *schemaName = stmt->schemaname;
Oid namespaceId;
OverrideSearchPath *overridePath;
List *parsetree_list;
@ -58,11 +58,24 @@ CreateSchemaCommand(CreateSchemaStmt *stmt, const char *queryString)
/*
* Who is supposed to own the new schema?
*/
if (authId)
owner_uid = get_role_oid(authId, false);
if (stmt->authrole)
owner_uid = get_rolespec_oid(stmt->authrole, false);
else
owner_uid = saved_uid;
/* fill schema name with the user name if not specified */
if (!schemaName)
{
HeapTuple tuple;
tuple = SearchSysCache1(AUTHOID, ObjectIdGetDatum(owner_uid));
if (!HeapTupleIsValid(tuple))
elog(ERROR, "cache lookup failed for role %u", owner_uid);
schemaName =
pstrdup(NameStr(((Form_pg_authid) GETSTRUCT(tuple))->rolname));
ReleaseSysCache(tuple);
}
/*
* To create a schema, must have schema-create privilege on the current
* database and must be able to become the target role (this does not

View File

@ -3507,7 +3507,7 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
break;
case AT_ChangeOwner: /* ALTER OWNER */
ATExecChangeOwner(RelationGetRelid(rel),
get_role_oid(cmd->name, false),
get_rolespec_oid(cmd->newowner, false),
false, lockmode);
break;
case AT_ClusterOn: /* CLUSTER ON */
@ -9388,7 +9388,7 @@ AlterTableMoveAll(AlterTableMoveAllStmt *stmt)
HeapTuple tuple;
Oid orig_tablespaceoid;
Oid new_tablespaceoid;
List *role_oids = roleNamesToIds(stmt->roles);
List *role_oids = roleSpecsToIds(stmt->roles);
/* Ensure we were not asked to move something we can't */
if (stmt->objtype != OBJECT_TABLE && stmt->objtype != OBJECT_INDEX &&

View File

@ -252,7 +252,7 @@ CreateTableSpace(CreateTableSpaceStmt *stmt)
/* However, the eventual owner of the tablespace need not be */
if (stmt->owner)
ownerId = get_role_oid(stmt->owner, false);
ownerId = get_rolespec_oid(stmt->owner, false);
else
ownerId = GetUserId();

View File

@ -49,10 +49,10 @@ extern bool Password_encryption;
check_password_hook_type check_password_hook = NULL;
static void AddRoleMems(const char *rolename, Oid roleid,
List *memberNames, List *memberIds,
List *memberSpecs, List *memberIds,
Oid grantorId, bool admin_opt);
static void DelRoleMems(const char *rolename, Oid roleid,
List *memberNames, List *memberIds,
List *memberSpecs, List *memberIds,
bool admin_opt);
@ -443,10 +443,10 @@ CreateRole(CreateRoleStmt *stmt)
* option, rolemembers don't.
*/
AddRoleMems(stmt->role, roleid,
adminmembers, roleNamesToIds(adminmembers),
adminmembers, roleSpecsToIds(adminmembers),
GetUserId(), true);
AddRoleMems(stmt->role, roleid,
rolemembers, roleNamesToIds(rolemembers),
rolemembers, roleSpecsToIds(rolemembers),
GetUserId(), false);
/* Post creation hook for new role */
@ -478,7 +478,9 @@ AlterRole(AlterRoleStmt *stmt)
TupleDesc pg_authid_dsc;
HeapTuple tuple,
new_tuple;
Form_pg_authid authform;
ListCell *option;
char *rolename = NULL;
char *password = NULL; /* user password */
bool encrypt_password = Password_encryption; /* encrypt password? */
char encrypted_password[MD5_PASSWD_LEN + 1];
@ -647,33 +649,30 @@ AlterRole(AlterRoleStmt *stmt)
pg_authid_rel = heap_open(AuthIdRelationId, RowExclusiveLock);
pg_authid_dsc = RelationGetDescr(pg_authid_rel);
tuple = SearchSysCache1(AUTHNAME, PointerGetDatum(stmt->role));
if (!HeapTupleIsValid(tuple))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("role \"%s\" does not exist", stmt->role)));
tuple = get_rolespec_tuple(stmt->role);
authform = (Form_pg_authid) GETSTRUCT(tuple);
rolename = pstrdup(NameStr(authform->rolname));
roleid = HeapTupleGetOid(tuple);
/*
* To mess with a superuser you gotta be superuser; else you need
* createrole, or just want to change your own password
*/
if (((Form_pg_authid) GETSTRUCT(tuple))->rolsuper || issuper >= 0)
if (authform->rolsuper || issuper >= 0)
{
if (!superuser())
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("must be superuser to alter superusers")));
}
else if (((Form_pg_authid) GETSTRUCT(tuple))->rolreplication || isreplication >= 0)
else if (authform->rolreplication || isreplication >= 0)
{
if (!superuser())
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("must be superuser to alter replication users")));
}
else if (((Form_pg_authid) GETSTRUCT(tuple))->rolbypassrls || bypassrls >= 0)
else if (authform->rolbypassrls || bypassrls >= 0)
{
if (!superuser())
ereport(ERROR,
@ -718,11 +717,11 @@ AlterRole(AlterRoleStmt *stmt)
* Call the password checking hook if there is one defined
*/
if (check_password_hook && password)
(*check_password_hook) (stmt->role,
password,
isMD5(password) ? PASSWORD_TYPE_MD5 : PASSWORD_TYPE_PLAINTEXT,
validUntil_datum,
validUntil_null);
(*check_password_hook)(rolename ,
password,
isMD5(password) ? PASSWORD_TYPE_MD5 : PASSWORD_TYPE_PLAINTEXT,
validUntil_datum,
validUntil_null);
/*
* Build an updated tuple, perusing the information just obtained
@ -784,7 +783,7 @@ AlterRole(AlterRoleStmt *stmt)
CStringGetTextDatum(password);
else
{
if (!pg_md5_encrypt(password, stmt->role, strlen(stmt->role),
if (!pg_md5_encrypt(password, rolename, strlen(rolename),
encrypted_password))
elog(ERROR, "password encryption failed");
new_record[Anum_pg_authid_rolpassword - 1] =
@ -831,12 +830,12 @@ AlterRole(AlterRoleStmt *stmt)
CommandCounterIncrement();
if (stmt->action == +1) /* add members to role */
AddRoleMems(stmt->role, roleid,
rolemembers, roleNamesToIds(rolemembers),
AddRoleMems(rolename, roleid,
rolemembers, roleSpecsToIds(rolemembers),
GetUserId(), false);
else if (stmt->action == -1) /* drop members from role */
DelRoleMems(stmt->role, roleid,
rolemembers, roleNamesToIds(rolemembers),
DelRoleMems(rolename, roleid,
rolemembers, roleSpecsToIds(rolemembers),
false);
/*
@ -860,13 +859,7 @@ AlterRoleSet(AlterRoleSetStmt *stmt)
if (stmt->role)
{
roletuple = SearchSysCache1(AUTHNAME, PointerGetDatum(stmt->role));
if (!HeapTupleIsValid(roletuple))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("role \"%s\" does not exist", stmt->role)));
roletuple = get_rolespec_tuple(stmt->role);
roleid = HeapTupleGetOid(roletuple);
/*
@ -955,7 +948,8 @@ DropRole(DropRoleStmt *stmt)
foreach(item, stmt->roles)
{
const char *role = strVal(lfirst(item));
RoleSpec *rolspec = lfirst(item);
char *role;
HeapTuple tuple,
tmp_tuple;
ScanKeyData scankey;
@ -964,6 +958,12 @@ DropRole(DropRoleStmt *stmt)
SysScanDesc sscan;
Oid roleid;
if (rolspec->roletype != ROLESPEC_CSTRING)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("cannot use special role specifier in \"%s\"", "DROP ROLE")));
role = rolspec->rolename;
tuple = SearchSysCache1(AUTHNAME, PointerGetDatum(role));
if (!HeapTupleIsValid(tuple))
{
@ -1233,11 +1233,11 @@ GrantRole(GrantRoleStmt *stmt)
ListCell *item;
if (stmt->grantor)
grantor = get_role_oid(stmt->grantor, false);
grantor = get_rolespec_oid(stmt->grantor, false);
else
grantor = GetUserId();
grantee_ids = roleNamesToIds(stmt->grantee_roles);
grantee_ids = roleSpecsToIds(stmt->grantee_roles);
/* AccessShareLock is enough since we aren't modifying pg_authid */
pg_authid_rel = heap_open(AuthIdRelationId, AccessShareLock);
@ -1286,7 +1286,7 @@ GrantRole(GrantRoleStmt *stmt)
void
DropOwnedObjects(DropOwnedStmt *stmt)
{
List *role_ids = roleNamesToIds(stmt->roles);
List *role_ids = roleSpecsToIds(stmt->roles);
ListCell *cell;
/* Check privileges */
@ -1312,7 +1312,7 @@ DropOwnedObjects(DropOwnedStmt *stmt)
void
ReassignOwnedObjects(ReassignOwnedStmt *stmt)
{
List *role_ids = roleNamesToIds(stmt->roles);
List *role_ids = roleSpecsToIds(stmt->roles);
ListCell *cell;
Oid newrole;
@ -1328,7 +1328,7 @@ ReassignOwnedObjects(ReassignOwnedStmt *stmt)
}
/* Must have privileges on the receiving side too */
newrole = get_role_oid(stmt->newrole, false);
newrole = get_rolespec_oid(stmt->newrole, false);
if (!has_privs_of_role(GetUserId(), newrole))
ereport(ERROR,
@ -1340,22 +1340,24 @@ ReassignOwnedObjects(ReassignOwnedStmt *stmt)
}
/*
* roleNamesToIds
* roleSpecsToIds
*
* Given a list of role names (as String nodes), generate a list of role OIDs
* in the same order.
* Given a list of RoleSpecs, generate a list of role OIDs in the same order.
*
* ROLESPEC_PUBLIC is not allowed.
*/
List *
roleNamesToIds(List *memberNames)
roleSpecsToIds(List *memberNames)
{
List *result = NIL;
ListCell *l;
foreach(l, memberNames)
{
char *rolename = strVal(lfirst(l));
Oid roleid = get_role_oid(rolename, false);
Node *rolespec = (Node *) lfirst(l);
Oid roleid;
roleid = get_rolespec_oid(rolespec, false);
result = lappend_oid(result, roleid);
}
return result;
@ -1366,7 +1368,7 @@ roleNamesToIds(List *memberNames)
*
* rolename: name of role to add to (used only for error messages)
* roleid: OID of role to add to
* memberNames: list of names of roles to add (used only for error messages)
* memberSpecs: list of RoleSpec of roles to add (used only for error messages)
* memberIds: OIDs of roles to add
* grantorId: who is granting the membership
* admin_opt: granting admin option?
@ -1375,15 +1377,15 @@ roleNamesToIds(List *memberNames)
*/
static void
AddRoleMems(const char *rolename, Oid roleid,
List *memberNames, List *memberIds,
List *memberSpecs, List *memberIds,
Oid grantorId, bool admin_opt)
{
Relation pg_authmem_rel;
TupleDesc pg_authmem_dsc;
ListCell *nameitem;
ListCell *specitem;
ListCell *iditem;
Assert(list_length(memberNames) == list_length(memberIds));
Assert(list_length(memberSpecs) == list_length(memberIds));
/* Skip permission check if nothing to do */
if (!memberIds)
@ -1428,9 +1430,9 @@ AddRoleMems(const char *rolename, Oid roleid,
pg_authmem_rel = heap_open(AuthMemRelationId, RowExclusiveLock);
pg_authmem_dsc = RelationGetDescr(pg_authmem_rel);
forboth(nameitem, memberNames, iditem, memberIds)
forboth(specitem, memberSpecs, iditem, memberIds)
{
const char *membername = strVal(lfirst(nameitem));
RoleSpec *memberRole = lfirst(specitem);
Oid memberid = lfirst_oid(iditem);
HeapTuple authmem_tuple;
HeapTuple tuple;
@ -1449,7 +1451,7 @@ AddRoleMems(const char *rolename, Oid roleid,
ereport(ERROR,
(errcode(ERRCODE_INVALID_GRANT_OPERATION),
(errmsg("role \"%s\" is a member of role \"%s\"",
rolename, membername))));
rolename, get_rolespec_name((Node *) memberRole)))));
/*
* Check if entry for this role/member already exists; if so, give
@ -1464,7 +1466,7 @@ AddRoleMems(const char *rolename, Oid roleid,
{
ereport(NOTICE,
(errmsg("role \"%s\" is already a member of role \"%s\"",
membername, rolename)));
get_rolespec_name((Node *) memberRole), rolename)));
ReleaseSysCache(authmem_tuple);
continue;
}
@ -1513,7 +1515,7 @@ AddRoleMems(const char *rolename, Oid roleid,
*
* rolename: name of role to del from (used only for error messages)
* roleid: OID of role to del from
* memberNames: list of names of roles to del (used only for error messages)
* memberSpecs: list of RoleSpec of roles to del (used only for error messages)
* memberIds: OIDs of roles to del
* admin_opt: remove admin option only?
*
@ -1521,15 +1523,15 @@ AddRoleMems(const char *rolename, Oid roleid,
*/
static void
DelRoleMems(const char *rolename, Oid roleid,
List *memberNames, List *memberIds,
List *memberSpecs, List *memberIds,
bool admin_opt)
{
Relation pg_authmem_rel;
TupleDesc pg_authmem_dsc;
ListCell *nameitem;
ListCell *specitem;
ListCell *iditem;
Assert(list_length(memberNames) == list_length(memberIds));
Assert(list_length(memberSpecs) == list_length(memberIds));
/* Skip permission check if nothing to do */
if (!memberIds)
@ -1559,9 +1561,9 @@ DelRoleMems(const char *rolename, Oid roleid,
pg_authmem_rel = heap_open(AuthMemRelationId, RowExclusiveLock);
pg_authmem_dsc = RelationGetDescr(pg_authmem_rel);
forboth(nameitem, memberNames, iditem, memberIds)
forboth(specitem, memberSpecs, iditem, memberIds)
{
const char *membername = strVal(lfirst(nameitem));
RoleSpec *memberRole = lfirst(specitem);
Oid memberid = lfirst_oid(iditem);
HeapTuple authmem_tuple;
@ -1575,7 +1577,7 @@ DelRoleMems(const char *rolename, Oid roleid,
{
ereport(WARNING,
(errmsg("role \"%s\" is not a member of role \"%s\"",
membername, rolename)));
get_rolespec_name((Node *) memberRole), rolename)));
continue;
}