Commit d6f8a76c authored by Tom Lane's avatar Tom Lane

Cause ALTER OWNER commands to update the object's ACL, replacing references

to the old owner with the new owner.  This is not necessarily right, but
it's sure a lot more likely to be what the user wants than doing nothing.
Christopher Kings-Lynne, some rework by Tom Lane.
parent 35ff782d
......@@ -9,7 +9,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/dbcommands.c,v 1.138 2004/08/01 06:19:22 momjian Exp $
* $PostgreSQL: pgsql/src/backend/commands/dbcommands.c,v 1.139 2004/08/01 20:30:48 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -768,8 +768,7 @@ AlterDatabaseSet(AlterDatabaseSetStmt *stmt)
void
AlterDatabaseOwner(const char *dbname, AclId newOwnerSysId)
{
HeapTuple tuple,
newtuple;
HeapTuple tuple;
Relation rel;
ScanKeyData scankey;
SysScanDesc scan;
......@@ -788,8 +787,7 @@ AlterDatabaseOwner(const char *dbname, AclId newOwnerSysId)
(errcode(ERRCODE_UNDEFINED_DATABASE),
errmsg("database \"%s\" does not exist", dbname)));
newtuple = heap_copytuple(tuple);
datForm = (Form_pg_database) GETSTRUCT(newtuple);
datForm = (Form_pg_database) GETSTRUCT(tuple);
/*
* If the new owner is the same as the existing owner, consider the
......@@ -797,6 +795,14 @@ AlterDatabaseOwner(const char *dbname, AclId newOwnerSysId)
*/
if (datForm->datdba != newOwnerSysId)
{
Datum repl_val[Natts_pg_database];
char repl_null[Natts_pg_database];
char repl_repl[Natts_pg_database];
Acl *newAcl;
Datum aclDatum;
bool isNull;
HeapTuple newtuple;
/* changing owner's database for someone else: must be superuser */
/* note that the someone else need not have any permissions */
if (!superuser())
......@@ -804,10 +810,33 @@ AlterDatabaseOwner(const char *dbname, AclId newOwnerSysId)
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("must be superuser to change owner")));
/* change owner */
datForm->datdba = newOwnerSysId;
memset(repl_null, ' ', sizeof(repl_null));
memset(repl_repl, ' ', sizeof(repl_repl));
repl_repl[Anum_pg_database_datdba - 1] = 'r';
repl_val[Anum_pg_database_datdba - 1] = Int32GetDatum(newOwnerSysId);
/*
* Determine the modified ACL for the new owner. This is only
* necessary when the ACL is non-null.
*/
aclDatum = heap_getattr(tuple,
Anum_pg_database_datacl,
RelationGetDescr(rel),
&isNull);
if (!isNull)
{
newAcl = aclnewowner(DatumGetAclP(aclDatum),
datForm->datdba, newOwnerSysId);
repl_repl[Anum_pg_database_datacl - 1] = 'r';
repl_val[Anum_pg_database_datacl - 1] = PointerGetDatum(newAcl);
}
newtuple = heap_modifytuple(tuple, rel, repl_val, repl_null, repl_repl);
simple_heap_update(rel, &newtuple->t_self, newtuple);
CatalogUpdateIndexes(rel, newtuple);
heap_freetuple(newtuple);
}
systable_endscan(scan);
......
......@@ -10,7 +10,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/functioncmds.c,v 1.49 2004/06/25 21:55:53 tgl Exp $
* $PostgreSQL: pgsql/src/backend/commands/functioncmds.c,v 1.50 2004/08/01 20:30:48 tgl Exp $
*
* DESCRIPTION
* These routines take the parse tree and pick out the
......@@ -738,7 +738,7 @@ AlterFunctionOwner(List *name, List *argtypes, AclId newOwnerSysId)
procOid = LookupFuncNameTypeNames(name, argtypes, false);
tup = SearchSysCacheCopy(PROCOID,
tup = SearchSysCache(PROCOID,
ObjectIdGetDatum(procOid),
0, 0, 0);
if (!HeapTupleIsValid(tup)) /* should not happen */
......@@ -758,22 +758,51 @@ AlterFunctionOwner(List *name, List *argtypes, AclId newOwnerSysId)
*/
if (procForm->proowner != newOwnerSysId)
{
Datum repl_val[Natts_pg_proc];
char repl_null[Natts_pg_proc];
char repl_repl[Natts_pg_proc];
Acl *newAcl;
Datum aclDatum;
bool isNull;
HeapTuple newtuple;
/* Otherwise, must be superuser to change object ownership */
if (!superuser())
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("must be superuser to change owner")));
/* Modify the owner --- okay to scribble on tup because it's a copy */
procForm->proowner = newOwnerSysId;
memset(repl_null, ' ', sizeof(repl_null));
memset(repl_repl, ' ', sizeof(repl_repl));
repl_repl[Anum_pg_proc_proowner - 1] = 'r';
repl_val[Anum_pg_proc_proowner - 1] = Int32GetDatum(newOwnerSysId);
/*
* Determine the modified ACL for the new owner. This is only
* necessary when the ACL is non-null.
*/
aclDatum = SysCacheGetAttr(PROCOID, tup,
Anum_pg_proc_proacl,
&isNull);
if (!isNull)
{
newAcl = aclnewowner(DatumGetAclP(aclDatum),
procForm->proowner, newOwnerSysId);
repl_repl[Anum_pg_proc_proacl - 1] = 'r';
repl_val[Anum_pg_proc_proacl - 1] = PointerGetDatum(newAcl);
}
newtuple = heap_modifytuple(tup, rel, repl_val, repl_null, repl_repl);
simple_heap_update(rel, &tup->t_self, tup);
simple_heap_update(rel, &newtuple->t_self, newtuple);
CatalogUpdateIndexes(rel, newtuple);
CatalogUpdateIndexes(rel, tup);
heap_freetuple(newtuple);
}
ReleaseSysCache(tup);
heap_close(rel, NoLock);
heap_freetuple(tup);
}
......
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/schemacmds.c,v 1.20 2004/06/25 21:55:53 tgl Exp $
* $PostgreSQL: pgsql/src/backend/commands/schemacmds.c,v 1.21 2004/08/01 20:30:48 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -320,7 +320,7 @@ AlterSchemaOwner(const char *name, AclId newOwnerSysId)
rel = heap_openr(NamespaceRelationName, RowExclusiveLock);
tup = SearchSysCacheCopy(NAMESPACENAME,
tup = SearchSysCache(NAMESPACENAME,
CStringGetDatum(name),
0, 0, 0);
if (!HeapTupleIsValid(tup))
......@@ -335,20 +335,49 @@ AlterSchemaOwner(const char *name, AclId newOwnerSysId)
*/
if (nspForm->nspowner != newOwnerSysId)
{
Datum repl_val[Natts_pg_namespace];
char repl_null[Natts_pg_namespace];
char repl_repl[Natts_pg_namespace];
Acl *newAcl;
Datum aclDatum;
bool isNull;
HeapTuple newtuple;
/* Otherwise, must be superuser to change object ownership */
if (!superuser())
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("must be superuser to change owner")));
/* Modify the owner --- okay to scribble on tup because it's a copy */
nspForm->nspowner = newOwnerSysId;
memset(repl_null, ' ', sizeof(repl_null));
memset(repl_repl, ' ', sizeof(repl_repl));
simple_heap_update(rel, &tup->t_self, tup);
repl_repl[Anum_pg_namespace_nspowner - 1] = 'r';
repl_val[Anum_pg_namespace_nspowner - 1] = Int32GetDatum(newOwnerSysId);
CatalogUpdateIndexes(rel, tup);
}
/*
* Determine the modified ACL for the new owner. This is only
* necessary when the ACL is non-null.
*/
aclDatum = SysCacheGetAttr(NAMESPACENAME, tup,
Anum_pg_namespace_nspacl,
&isNull);
if (!isNull)
{
newAcl = aclnewowner(DatumGetAclP(aclDatum),
nspForm->nspowner, newOwnerSysId);
repl_repl[Anum_pg_namespace_nspacl - 1] = 'r';
repl_val[Anum_pg_namespace_nspacl - 1] = PointerGetDatum(newAcl);
}
newtuple = heap_modifytuple(tup, rel, repl_val, repl_null, repl_repl);
simple_heap_update(rel, &newtuple->t_self, newtuple);
CatalogUpdateIndexes(rel, newtuple);
heap_freetuple(newtuple);
}
ReleaseSysCache(tup);
heap_close(rel, NoLock);
heap_freetuple(tup);
}
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.122 2004/07/21 22:31:21 tgl Exp $
* $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.123 2004/08/01 20:30:48 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -5115,7 +5115,7 @@ ATExecChangeOwner(Oid relationOid, int32 newOwnerSysId)
/* Get its pg_class tuple, too */
class_rel = heap_openr(RelationRelationName, RowExclusiveLock);
tuple = SearchSysCacheCopy(RELOID,
tuple = SearchSysCache(RELOID,
ObjectIdGetDatum(relationOid),
0, 0, 0);
if (!HeapTupleIsValid(tuple))
......@@ -5145,21 +5145,47 @@ ATExecChangeOwner(Oid relationOid, int32 newOwnerSysId)
*/
if (tuple_class->relowner != newOwnerSysId)
{
Datum repl_val[Natts_pg_class];
char repl_null[Natts_pg_class];
char repl_repl[Natts_pg_class];
Acl *newAcl;
Datum aclDatum;
bool isNull;
HeapTuple newtuple;
/* Otherwise, check that we are the superuser */
if (!superuser())
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("must be superuser to change owner")));
memset(repl_null, ' ', sizeof(repl_null));
memset(repl_repl, ' ', sizeof(repl_repl));
repl_repl[Anum_pg_class_relowner - 1] = 'r';
repl_val[Anum_pg_class_relowner - 1] = Int32GetDatum(newOwnerSysId);
/*
* Okay, this is a valid tuple: change its ownership and write to the
* heap.
* Determine the modified ACL for the new owner. This is only
* necessary when the ACL is non-null.
*/
tuple_class->relowner = newOwnerSysId;
simple_heap_update(class_rel, &tuple->t_self, tuple);
aclDatum = SysCacheGetAttr(RELOID, tuple,
Anum_pg_class_relacl,
&isNull);
if (!isNull)
{
newAcl = aclnewowner(DatumGetAclP(aclDatum),
tuple_class->relowner, newOwnerSysId);
repl_repl[Anum_pg_class_relacl - 1] = 'r';
repl_val[Anum_pg_class_relacl - 1] = PointerGetDatum(newAcl);
}
/* Keep the catalog indexes up to date */
CatalogUpdateIndexes(class_rel, tuple);
newtuple = heap_modifytuple(tuple, class_rel, repl_val, repl_null, repl_repl);
simple_heap_update(class_rel, &newtuple->t_self, newtuple);
CatalogUpdateIndexes(class_rel, newtuple);
heap_freetuple(newtuple);
/*
* If we are operating on a table, also change the ownership of any
......@@ -5190,7 +5216,7 @@ ATExecChangeOwner(Oid relationOid, int32 newOwnerSysId)
}
}
heap_freetuple(tuple);
ReleaseSysCache(tuple);
heap_close(class_rel, RowExclusiveLock);
relation_close(target_rel, NoLock);
}
......
......@@ -45,7 +45,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/tablespace.c,v 1.6 2004/07/11 19:52:49 tgl Exp $
* $PostgreSQL: pgsql/src/backend/commands/tablespace.c,v 1.7 2004/08/01 20:30:48 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -757,7 +757,6 @@ AlterTableSpaceOwner(const char *name, AclId newOwnerSysId)
HeapScanDesc scandesc;
Form_pg_tablespace spcForm;
HeapTuple tup;
HeapTuple newtuple;
/* Search pg_tablespace */
rel = heap_openr(TableSpaceRelationName, RowExclusiveLock);
......@@ -773,8 +772,7 @@ AlterTableSpaceOwner(const char *name, AclId newOwnerSysId)
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("tablespace \"%s\" does not exist", name)));
newtuple = heap_copytuple(tup);
spcForm = (Form_pg_tablespace) GETSTRUCT(newtuple);
spcForm = (Form_pg_tablespace) GETSTRUCT(tup);
/*
* If the new owner is the same as the existing owner, consider the
......@@ -782,16 +780,48 @@ AlterTableSpaceOwner(const char *name, AclId newOwnerSysId)
*/
if (spcForm->spcowner != newOwnerSysId)
{
Datum repl_val[Natts_pg_tablespace];
char repl_null[Natts_pg_tablespace];
char repl_repl[Natts_pg_tablespace];
Acl *newAcl;
Datum aclDatum;
bool isNull;
HeapTuple newtuple;
/* Otherwise, must be superuser to change object ownership */
if (!superuser())
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("must be superuser to change owner")));
/* Modify the owner */
spcForm->spcowner = newOwnerSysId;
memset(repl_null, ' ', sizeof(repl_null));
memset(repl_repl, ' ', sizeof(repl_repl));
repl_repl[Anum_pg_tablespace_spcowner - 1] = 'r';
repl_val[Anum_pg_tablespace_spcowner - 1] = Int32GetDatum(newOwnerSysId);
/*
* Determine the modified ACL for the new owner. This is only
* necessary when the ACL is non-null.
*/
aclDatum = heap_getattr(tup,
Anum_pg_tablespace_spcacl,
RelationGetDescr(rel),
&isNull);
if (!isNull)
{
newAcl = aclnewowner(DatumGetAclP(aclDatum),
spcForm->spcowner, newOwnerSysId);
repl_repl[Anum_pg_tablespace_spcacl - 1] = 'r';
repl_val[Anum_pg_tablespace_spcacl - 1] = PointerGetDatum(newAcl);
}
newtuple = heap_modifytuple(tup, rel, repl_val, repl_null, repl_repl);
simple_heap_update(rel, &newtuple->t_self, newtuple);
CatalogUpdateIndexes(rel, newtuple);
heap_freetuple(newtuple);
}
heap_endscan(scandesc);
......
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/adt/acl.c,v 1.107 2004/07/12 20:23:50 momjian Exp $
* $PostgreSQL: pgsql/src/backend/utils/adt/acl.c,v 1.108 2004/08/01 20:30:49 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -717,6 +717,109 @@ aclupdate(const Acl *old_acl, const AclItem *mod_aip,
return new_acl;
}
/*
* Update an ACL array to reflect a change of owner to the parent object
*
* old_acl: the input ACL array (must not be NULL)
* oldownerid: AclId of the old object owner
* newownerid: AclId of the new object owner
*
* The result is a modified copy; the input object is not changed.
*
* NB: caller is responsible for having detoasted the input ACL, if needed.
*/
Acl *
aclnewowner(const Acl *old_acl, AclId oldownerid, AclId newownerid)
{
Acl *new_acl;
AclItem *new_aip;
AclItem *old_aip;
AclItem *dst_aip;
AclItem *src_aip;
AclItem *targ_aip;
bool newpresent = false;
int dst,
src,
targ,
num;
/*
* Make a copy of the given ACL, substituting new owner ID for old
* wherever it appears as either grantor or grantee. Also note if
* the new owner ID is already present.
*/
num = ACL_NUM(old_acl);
old_aip = ACL_DAT(old_acl);
new_acl = allocacl(num);
new_aip = ACL_DAT(new_acl);
memcpy(new_aip, old_aip, num * sizeof(AclItem));
for (dst = 0, dst_aip = new_aip; dst < num; dst++, dst_aip++)
{
/* grantor is always a UID, but grantee might not be */
if (dst_aip->ai_grantor == oldownerid)
dst_aip->ai_grantor = newownerid;
else if (dst_aip->ai_grantor == newownerid)
newpresent = true;
if (ACLITEM_GET_IDTYPE(*dst_aip) == ACL_IDTYPE_UID)
{
if (dst_aip->ai_grantee == oldownerid)
dst_aip->ai_grantee = newownerid;
else if (dst_aip->ai_grantee == newownerid)
newpresent = true;
}
}
/*
* If the old ACL contained any references to the new owner, then we
* may now have generated an ACL containing duplicate entries. Find
* them and merge them so that there are not duplicates. (This is
* relatively expensive since we use a stupid O(N^2) algorithm, but
* it's unlikely to be the normal case.)
*
* To simplify deletion of duplicate entries, we temporarily leave them
* in the array but set their privilege masks to zero; when we reach
* such an entry it's just skipped. (Thus, a side effect of this code
* will be to remove privilege-free entries, should there be any in the
* input.) dst is the next output slot, targ is the currently considered
* input slot (always >= dst), and src scans entries to the right of targ
* looking for duplicates. Once an entry has been emitted to dst it is
* known duplicate-free and need not be considered anymore.
*/
if (newpresent)
{
dst = 0;
for (targ = 0, targ_aip = new_aip; targ < num; targ++, targ_aip++)
{
/* ignore if deleted in an earlier pass */
if (ACLITEM_GET_RIGHTS(*targ_aip) == ACL_NO_RIGHTS)
continue;
/* find and merge any duplicates */
for (src = targ + 1, src_aip = targ_aip + 1; src < num;
src++, src_aip++)
{
if (ACLITEM_GET_RIGHTS(*src_aip) == ACL_NO_RIGHTS)
continue;
if (aclitem_match(targ_aip, src_aip))
{
ACLITEM_SET_RIGHTS(*targ_aip,
ACLITEM_GET_RIGHTS(*targ_aip) |
ACLITEM_GET_RIGHTS(*src_aip));
/* mark the duplicate deleted */
ACLITEM_SET_RIGHTS(*src_aip, ACL_NO_RIGHTS);
}
}
/* and emit to output */
new_aip[dst] = *targ_aip;
dst++;
}
/* Adjust array size to be 'dst' items */
ARR_DIMS(new_acl)[0] = dst;
ARR_SIZE(new_acl) = ACL_N_SIZE(dst);
}
return new_acl;
}
/*
* When granting grant options, we must disallow attempts to set up circular
......@@ -2373,15 +2476,15 @@ convert_tablespace_name(text *tablespacename)
{
char *spcname;
Oid oid;
spcname = DatumGetCString(DirectFunctionCall1(textout,
PointerGetDatum(tablespacename)));
PointerGetDatum(tablespacename)));
oid = get_tablespace_oid(spcname);
if (!OidIsValid(oid))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("tablespace \"%s\" does not exist", spcname)));
if (!OidIsValid(oid))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("tablespace \"%s\" does not exist", spcname)));
return oid;
}
......
......@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/utils/acl.h,v 1.71 2004/06/18 06:14:21 tgl Exp $
* $PostgreSQL: pgsql/src/include/utils/acl.h,v 1.72 2004/08/01 20:30:49 tgl Exp $
*
* NOTES
* An ACL array is simply an array of AclItems, representing the union
......@@ -224,6 +224,8 @@ typedef enum AclObjectKind
extern Acl *acldefault(GrantObjectType objtype, AclId ownerid);
extern Acl *aclupdate(const Acl *old_acl, const AclItem *mod_aip,
int modechg, AclId ownerid, DropBehavior behavior);
extern Acl *aclnewowner(const Acl *old_acl, AclId oldownerid, AclId newownerid);
extern AclMode aclmask(const Acl *acl, AclId userid, AclId ownerid,
AclMode mask, AclMaskHow how);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment