|
|
|
@ -407,6 +407,262 @@ RemovePolicyById(Oid policy_id)
|
|
|
|
|
heap_close(pg_policy_rel, RowExclusiveLock);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* RemoveRoleFromObjectPolicy -
|
|
|
|
|
* remove a role from a policy by its OID. If the role is not a member of
|
|
|
|
|
* the policy then an error is raised. False is returned to indicate that
|
|
|
|
|
* the role could not be removed due to being the only role on the policy
|
|
|
|
|
* and therefore the entire policy should be removed.
|
|
|
|
|
*
|
|
|
|
|
* Note that a warning will be thrown and true will be returned on a
|
|
|
|
|
* permission error, as the policy should not be removed in that case.
|
|
|
|
|
*
|
|
|
|
|
* roleid - the oid of the role to remove
|
|
|
|
|
* classid - should always be PolicyRelationId
|
|
|
|
|
* policy_id - the oid of the policy.
|
|
|
|
|
*/
|
|
|
|
|
bool
|
|
|
|
|
RemoveRoleFromObjectPolicy(Oid roleid, Oid classid, Oid policy_id)
|
|
|
|
|
{
|
|
|
|
|
Relation pg_policy_rel;
|
|
|
|
|
SysScanDesc sscan;
|
|
|
|
|
ScanKeyData skey[1];
|
|
|
|
|
HeapTuple tuple;
|
|
|
|
|
Oid relid;
|
|
|
|
|
Relation rel;
|
|
|
|
|
ArrayType *policy_roles;
|
|
|
|
|
int num_roles;
|
|
|
|
|
Datum roles_datum;
|
|
|
|
|
bool attr_isnull;
|
|
|
|
|
bool noperm = true;
|
|
|
|
|
|
|
|
|
|
Assert(classid == PolicyRelationId);
|
|
|
|
|
|
|
|
|
|
pg_policy_rel = heap_open(PolicyRelationId, RowExclusiveLock);
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Find the policy to update.
|
|
|
|
|
*/
|
|
|
|
|
ScanKeyInit(&skey[0],
|
|
|
|
|
ObjectIdAttributeNumber,
|
|
|
|
|
BTEqualStrategyNumber, F_OIDEQ,
|
|
|
|
|
ObjectIdGetDatum(policy_id));
|
|
|
|
|
|
|
|
|
|
sscan = systable_beginscan(pg_policy_rel, PolicyOidIndexId, true,
|
|
|
|
|
NULL, 1, skey);
|
|
|
|
|
|
|
|
|
|
tuple = systable_getnext(sscan);
|
|
|
|
|
|
|
|
|
|
/* Raise an error if we don't find the policy. */
|
|
|
|
|
if (!HeapTupleIsValid(tuple))
|
|
|
|
|
elog(ERROR, "could not find tuple for policy %u", policy_id);
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Open and exclusive-lock the relation the policy belongs to.
|
|
|
|
|
*/
|
|
|
|
|
relid = ((Form_pg_policy) GETSTRUCT(tuple))->polrelid;
|
|
|
|
|
|
|
|
|
|
rel = relation_open(relid, AccessExclusiveLock);
|
|
|
|
|
|
|
|
|
|
if (rel->rd_rel->relkind != RELKIND_RELATION)
|
|
|
|
|
ereport(ERROR,
|
|
|
|
|
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
|
|
|
|
|
errmsg("\"%s\" is not a table",
|
|
|
|
|
RelationGetRelationName(rel))));
|
|
|
|
|
|
|
|
|
|
if (!allowSystemTableMods && IsSystemRelation(rel))
|
|
|
|
|
ereport(ERROR,
|
|
|
|
|
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
|
|
|
|
|
errmsg("permission denied: \"%s\" is a system catalog",
|
|
|
|
|
RelationGetRelationName(rel))));
|
|
|
|
|
|
|
|
|
|
/* Get the current set of roles */
|
|
|
|
|
roles_datum = heap_getattr(tuple,
|
|
|
|
|
Anum_pg_policy_polroles,
|
|
|
|
|
RelationGetDescr(pg_policy_rel),
|
|
|
|
|
&attr_isnull);
|
|
|
|
|
|
|
|
|
|
Assert(!attr_isnull);
|
|
|
|
|
|
|
|
|
|
policy_roles = DatumGetArrayTypePCopy(roles_datum);
|
|
|
|
|
|
|
|
|
|
/* We should be removing exactly one entry from the roles array */
|
|
|
|
|
num_roles = ARR_DIMS(policy_roles)[0] - 1;
|
|
|
|
|
|
|
|
|
|
Assert(num_roles >= 0);
|
|
|
|
|
|
|
|
|
|
/* Must own relation. */
|
|
|
|
|
if (pg_class_ownercheck(relid, GetUserId()))
|
|
|
|
|
noperm = false; /* user is allowed to modify this policy */
|
|
|
|
|
else
|
|
|
|
|
ereport(WARNING,
|
|
|
|
|
(errcode(ERRCODE_WARNING_PRIVILEGE_NOT_REVOKED),
|
|
|
|
|
errmsg("role \"%s\" could not be removed from policy \"%s\" on \"%s\"",
|
|
|
|
|
GetUserNameFromId(roleid, false),
|
|
|
|
|
NameStr(((Form_pg_policy) GETSTRUCT(tuple))->polname),
|
|
|
|
|
RelationGetRelationName(rel))));
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* If multiple roles exist on this policy, then remove the one we were
|
|
|
|
|
* asked to and leave the rest.
|
|
|
|
|
*/
|
|
|
|
|
if (!noperm && num_roles > 0)
|
|
|
|
|
{
|
|
|
|
|
int i, j;
|
|
|
|
|
Oid *roles = (Oid *) ARR_DATA_PTR(policy_roles);
|
|
|
|
|
Datum *role_oids;
|
|
|
|
|
char *qual_value;
|
|
|
|
|
Node *qual_expr;
|
|
|
|
|
List *qual_parse_rtable = NIL;
|
|
|
|
|
char *with_check_value;
|
|
|
|
|
Node *with_check_qual;
|
|
|
|
|
List *with_check_parse_rtable = NIL;
|
|
|
|
|
Datum values[Natts_pg_policy];
|
|
|
|
|
bool isnull[Natts_pg_policy];
|
|
|
|
|
bool replaces[Natts_pg_policy];
|
|
|
|
|
Datum value_datum;
|
|
|
|
|
ArrayType *role_ids;
|
|
|
|
|
HeapTuple new_tuple;
|
|
|
|
|
ObjectAddress target;
|
|
|
|
|
ObjectAddress myself;
|
|
|
|
|
|
|
|
|
|
/* zero-clear */
|
|
|
|
|
memset(values, 0, sizeof(values));
|
|
|
|
|
memset(replaces, 0, sizeof(replaces));
|
|
|
|
|
memset(isnull, 0, sizeof(isnull));
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* All of the dependencies will be removed from the policy and then
|
|
|
|
|
* re-added. In order to get them correct, we need to extract out
|
|
|
|
|
* the expressions in the policy and construct a parsestate just
|
|
|
|
|
* enough to build the range table(s) to then pass to
|
|
|
|
|
* recordDependencyOnExpr().
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/* Get policy qual, to update dependencies */
|
|
|
|
|
value_datum = heap_getattr(tuple, Anum_pg_policy_polqual,
|
|
|
|
|
RelationGetDescr(pg_policy_rel), &attr_isnull);
|
|
|
|
|
if (!attr_isnull)
|
|
|
|
|
{
|
|
|
|
|
ParseState *qual_pstate;
|
|
|
|
|
|
|
|
|
|
/* parsestate is built just to build the range table */
|
|
|
|
|
qual_pstate = make_parsestate(NULL);
|
|
|
|
|
|
|
|
|
|
qual_value = TextDatumGetCString(value_datum);
|
|
|
|
|
qual_expr = stringToNode(qual_value);
|
|
|
|
|
|
|
|
|
|
/* Add this rel to the parsestate's rangetable, for dependencies */
|
|
|
|
|
addRangeTableEntryForRelation(qual_pstate, rel, NULL, false, false);
|
|
|
|
|
|
|
|
|
|
qual_parse_rtable = qual_pstate->p_rtable;
|
|
|
|
|
free_parsestate(qual_pstate);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
qual_expr = NULL;
|
|
|
|
|
|
|
|
|
|
/* Get WITH CHECK qual, to update dependencies */
|
|
|
|
|
value_datum = heap_getattr(tuple, Anum_pg_policy_polwithcheck,
|
|
|
|
|
RelationGetDescr(pg_policy_rel), &attr_isnull);
|
|
|
|
|
if (!attr_isnull)
|
|
|
|
|
{
|
|
|
|
|
ParseState *with_check_pstate;
|
|
|
|
|
|
|
|
|
|
/* parsestate is built just to build the range table */
|
|
|
|
|
with_check_pstate = make_parsestate(NULL);
|
|
|
|
|
|
|
|
|
|
with_check_value = TextDatumGetCString(value_datum);
|
|
|
|
|
with_check_qual = stringToNode(with_check_value);
|
|
|
|
|
|
|
|
|
|
/* Add this rel to the parsestate's rangetable, for dependencies */
|
|
|
|
|
addRangeTableEntryForRelation(with_check_pstate, rel, NULL, false,
|
|
|
|
|
false);
|
|
|
|
|
|
|
|
|
|
with_check_parse_rtable = with_check_pstate->p_rtable;
|
|
|
|
|
free_parsestate(with_check_pstate);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
with_check_qual = NULL;
|
|
|
|
|
|
|
|
|
|
/* Rebuild the roles array to then update the pg_policy tuple with */
|
|
|
|
|
role_oids = (Datum *) palloc(num_roles * sizeof(Datum));
|
|
|
|
|
for (i = 0, j = 0; i < ARR_DIMS(policy_roles)[0]; i++)
|
|
|
|
|
/* Copy over all of the roles which are not the one being removed */
|
|
|
|
|
if (roles[i] != roleid)
|
|
|
|
|
role_oids[j++] = ObjectIdGetDatum(roles[i]);
|
|
|
|
|
|
|
|
|
|
/* We should have only removed the one role */
|
|
|
|
|
Assert(j == num_roles);
|
|
|
|
|
|
|
|
|
|
/* This is the array for the new tuple */
|
|
|
|
|
role_ids = construct_array(role_oids, num_roles, OIDOID,
|
|
|
|
|
sizeof(Oid), true, 'i');
|
|
|
|
|
|
|
|
|
|
replaces[Anum_pg_policy_polroles - 1] = true;
|
|
|
|
|
values[Anum_pg_policy_polroles - 1] = PointerGetDatum(role_ids);
|
|
|
|
|
|
|
|
|
|
new_tuple = heap_modify_tuple(tuple,
|
|
|
|
|
RelationGetDescr(pg_policy_rel),
|
|
|
|
|
values, isnull, replaces);
|
|
|
|
|
simple_heap_update(pg_policy_rel, &new_tuple->t_self, new_tuple);
|
|
|
|
|
|
|
|
|
|
/* Update Catalog Indexes */
|
|
|
|
|
CatalogUpdateIndexes(pg_policy_rel, new_tuple);
|
|
|
|
|
|
|
|
|
|
/* Remove all old dependencies. */
|
|
|
|
|
deleteDependencyRecordsFor(PolicyRelationId, policy_id, false);
|
|
|
|
|
|
|
|
|
|
/* Record the new set of dependencies */
|
|
|
|
|
target.classId = RelationRelationId;
|
|
|
|
|
target.objectId = relid;
|
|
|
|
|
target.objectSubId = 0;
|
|
|
|
|
|
|
|
|
|
myself.classId = PolicyRelationId;
|
|
|
|
|
myself.objectId = policy_id;
|
|
|
|
|
myself.objectSubId = 0;
|
|
|
|
|
|
|
|
|
|
recordDependencyOn(&myself, &target, DEPENDENCY_AUTO);
|
|
|
|
|
|
|
|
|
|
if (qual_expr)
|
|
|
|
|
recordDependencyOnExpr(&myself, qual_expr, qual_parse_rtable,
|
|
|
|
|
DEPENDENCY_NORMAL);
|
|
|
|
|
|
|
|
|
|
if (with_check_qual)
|
|
|
|
|
recordDependencyOnExpr(&myself, with_check_qual,
|
|
|
|
|
with_check_parse_rtable,
|
|
|
|
|
DEPENDENCY_NORMAL);
|
|
|
|
|
|
|
|
|
|
/* Remove all the old shared dependencies (roles) */
|
|
|
|
|
deleteSharedDependencyRecordsFor(PolicyRelationId, policy_id, 0);
|
|
|
|
|
|
|
|
|
|
/* Record the new shared dependencies (roles) */
|
|
|
|
|
target.classId = AuthIdRelationId;
|
|
|
|
|
target.objectSubId = 0;
|
|
|
|
|
for (i = 0; i < num_roles; i++)
|
|
|
|
|
{
|
|
|
|
|
target.objectId = DatumGetObjectId(role_oids[i]);
|
|
|
|
|
/* no need for dependency on the public role */
|
|
|
|
|
if (target.objectId != ACL_ID_PUBLIC)
|
|
|
|
|
recordSharedDependencyOn(&myself, &target,
|
|
|
|
|
SHARED_DEPENDENCY_POLICY);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
InvokeObjectPostAlterHook(PolicyRelationId, policy_id, 0);
|
|
|
|
|
|
|
|
|
|
heap_freetuple(new_tuple);
|
|
|
|
|
|
|
|
|
|
/* Invalidate Relation Cache */
|
|
|
|
|
CacheInvalidateRelcache(rel);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Clean up. */
|
|
|
|
|
systable_endscan(sscan);
|
|
|
|
|
relation_close(rel, AccessExclusiveLock);
|
|
|
|
|
heap_close(pg_policy_rel, RowExclusiveLock);
|
|
|
|
|
|
|
|
|
|
return(noperm || num_roles > 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* CreatePolicy -
|
|
|
|
|
* handles the execution of the CREATE POLICY command.
|
|
|
|
|