/* * updateAclDependencies * Update the pg_shdepend info for an object's ACL during GRANT/REVOKE. * * classId, objectId: identify the object whose ACL this is * ownerId: role owning the object * isGrant: are we adding or removing ACL entries? * noldmembers, oldmembers: array of roleids appearing in old ACL * nnewmembers, newmembers: array of roleids appearing in new ACL * * We calculate the difference between the new and old lists of roles, * and then insert (if it's a grant) or delete (if it's a revoke) from * pg_shdepend as appropiate. * * Note that we can't insert blindly at grant, because we would end up with * duplicate registered dependencies. We could check for existence of the * tuple before inserting, but that seems to be more expensive than what we are * doing now. On the other hand, we can't just delete the tuples blindly at * revoke, because the user may still have other privileges. * * NOTE: Both input arrays must be sorted and de-duped. They are pfreed * before return. */ void updateAclDependencies(Oid classId, Oid objectId, Oid ownerId, bool isGrant, int noldmembers, Oid *oldmembers, int nnewmembers, Oid *newmembers) { Relation sdepRel; Oid *diff; int ndiff, i; /* * Calculate the differences between the old and new lists. */ if (isGrant) ndiff = getOidListDiff(newmembers, nnewmembers, oldmembers, noldmembers, &diff); else ndiff = getOidListDiff(oldmembers, noldmembers, newmembers, nnewmembers, &diff); if (ndiff > 0) { sdepRel = heap_open(SharedDependRelationId, RowExclusiveLock); /* Add or drop the respective dependency */ for (i = 0; i < ndiff; i++) { Oid roleid = diff[i]; /* * Skip the owner: he has an OWNER shdep entry instead. (This is * not just a space optimization; it makes ALTER OWNER easier. See * notes in changeDependencyOnOwner.) */ if (roleid == ownerId) continue; /* Skip pinned roles */ if (isSharedObjectPinned(AuthIdRelationId, roleid, sdepRel)) continue; if (isGrant) shdepAddDependency(sdepRel, classId, objectId, AuthIdRelationId, roleid, SHARED_DEPENDENCY_ACL); else shdepDropDependency(sdepRel, classId, objectId, AuthIdRelationId, roleid, SHARED_DEPENDENCY_ACL); } heap_close(sdepRel, RowExclusiveLock); } pfree(diff); }
/* * changeDependencyOnOwner * * Update the shared dependencies to account for the new owner. * * Note: we don't need an objsubid argument because only whole objects * have owners. */ void changeDependencyOnOwner(Oid classId, Oid objectId, Oid newOwnerId) { Relation sdepRel; sdepRel = heap_open(SharedDependRelationId, RowExclusiveLock); /* Adjust the SHARED_DEPENDENCY_OWNER entry */ shdepChangeDep(sdepRel, classId, objectId, 0, AuthIdRelationId, newOwnerId, SHARED_DEPENDENCY_OWNER); /*---------- * There should never be a SHARED_DEPENDENCY_ACL entry for the owner, * so get rid of it if there is one. This can happen if the new owner * was previously granted some rights to the object. * * This step is analogous to aclnewowner's removal of duplicate entries * in the ACL. We have to do it to handle this scenario: * A grants some rights on an object to B * ALTER OWNER changes the object's owner to B * ALTER OWNER changes the object's owner to C * The third step would remove all mention of B from the object's ACL, * but we'd still have a SHARED_DEPENDENCY_ACL for B if we did not do * things this way. * * The rule against having a SHARED_DEPENDENCY_ACL entry for the owner * allows us to fix things up in just this one place, without having * to make the various ALTER OWNER routines each know about it. *---------- */ shdepDropDependency(sdepRel, classId, objectId, 0, true, AuthIdRelationId, newOwnerId, SHARED_DEPENDENCY_ACL); heap_close(sdepRel, RowExclusiveLock); }
/* * updateAclDependencies * Update the pg_shdepend info for an object's ACL during GRANT/REVOKE. * * classId, objectId, objsubId: identify the object whose ACL this is * ownerId: role owning the object * noldmembers, oldmembers: array of roleids appearing in old ACL * nnewmembers, newmembers: array of roleids appearing in new ACL * * We calculate the differences between the new and old lists of roles, * and then insert or delete from pg_shdepend as appropriate. * * Note that we can't just insert all referenced roles blindly during GRANT, * because we would end up with duplicate registered dependencies. We could * check for existence of the tuples before inserting, but that seems to be * more expensive than what we are doing here. Likewise we can't just delete * blindly during REVOKE, because the user may still have other privileges. * It is also possible that REVOKE actually adds dependencies, due to * instantiation of a formerly implicit default ACL (although at present, * all such dependencies should be for the owning role, which we ignore here). * * NOTE: Both input arrays must be sorted and de-duped. (Typically they * are extracted from an ACL array by aclmembers(), which takes care of * both requirements.) The arrays are pfreed before return. */ void updateAclDependencies(Oid classId, Oid objectId, int32 objsubId, Oid ownerId, int noldmembers, Oid *oldmembers, int nnewmembers, Oid *newmembers) { Relation sdepRel; int i; /* * Remove entries that are common to both lists; those represent existing * dependencies we don't need to change. * * OK to overwrite the inputs since we'll pfree them anyway. */ getOidListDiff(oldmembers, &noldmembers, newmembers, &nnewmembers); if (noldmembers > 0 || nnewmembers > 0) { sdepRel = heap_open(SharedDependRelationId, RowExclusiveLock); /* Add new dependencies that weren't already present */ for (i = 0; i < nnewmembers; i++) { Oid roleid = newmembers[i]; /* * Skip the owner: he has an OWNER shdep entry instead. (This is * not just a space optimization; it makes ALTER OWNER easier. See * notes in changeDependencyOnOwner.) */ if (roleid == ownerId) continue; /* Skip pinned roles; they don't need dependency entries */ if (isSharedObjectPinned(AuthIdRelationId, roleid, sdepRel)) continue; shdepAddDependency(sdepRel, classId, objectId, objsubId, AuthIdRelationId, roleid, SHARED_DEPENDENCY_ACL); } /* Drop no-longer-used old dependencies */ for (i = 0; i < noldmembers; i++) { Oid roleid = oldmembers[i]; /* Skip the owner, same as above */ if (roleid == ownerId) continue; /* Skip pinned roles */ if (isSharedObjectPinned(AuthIdRelationId, roleid, sdepRel)) continue; shdepDropDependency(sdepRel, classId, objectId, objsubId, false, /* exact match on objsubId */ AuthIdRelationId, roleid, SHARED_DEPENDENCY_ACL); } heap_close(sdepRel, RowExclusiveLock); } if (oldmembers) pfree(oldmembers); if (newmembers) pfree(newmembers); }