mirror of
https://git.postgresql.org/git/postgresql.git
synced 2024-09-06 21:09:21 +02:00
Make inherited TRUNCATE perform access permission checks on parent table only.
Previously, TRUNCATE command through a parent table checked the permissions on not only the parent table but also the children tables inherited from it. This was a bug and inherited queries should perform access permission checks on the parent table only. This commit fixes that bug. Back-patch to all supported branches. Author: Amit Langote Reviewed-by: Fujii Masao Discussion: https://postgr.es/m/CAHGQGwFHdSvifhJE+-GSNqUHSfbiKxaeQQ7HGcYz6SC2n_oDcg@mail.gmail.com
This commit is contained in:
parent
3228512b7b
commit
de0177788b
@ -302,6 +302,7 @@ struct DropRelationCallbackState
|
|||||||
((child_is_partition) ? DEPENDENCY_AUTO : DEPENDENCY_NORMAL)
|
((child_is_partition) ? DEPENDENCY_AUTO : DEPENDENCY_NORMAL)
|
||||||
|
|
||||||
static void truncate_check_rel(Oid relid, Form_pg_class reltuple);
|
static void truncate_check_rel(Oid relid, Form_pg_class reltuple);
|
||||||
|
static void truncate_check_perms(Oid relid, Form_pg_class reltuple);
|
||||||
static void truncate_check_activity(Relation rel);
|
static void truncate_check_activity(Relation rel);
|
||||||
static void RangeVarCallbackForTruncate(const RangeVar *relation,
|
static void RangeVarCallbackForTruncate(const RangeVar *relation,
|
||||||
Oid relId, Oid oldRelId, void *arg);
|
Oid relId, Oid oldRelId, void *arg);
|
||||||
@ -1590,6 +1591,12 @@ ExecuteTruncate(TruncateStmt *stmt)
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Inherited TRUNCATE commands perform access
|
||||||
|
* permission checks on the parent table only.
|
||||||
|
* So we skip checking the children's permissions
|
||||||
|
* and don't call truncate_check_perms() here.
|
||||||
|
*/
|
||||||
truncate_check_rel(RelationGetRelid(rel), rel->rd_rel);
|
truncate_check_rel(RelationGetRelid(rel), rel->rd_rel);
|
||||||
truncate_check_activity(rel);
|
truncate_check_activity(rel);
|
||||||
|
|
||||||
@ -1676,6 +1683,7 @@ ExecuteTruncateGuts(List *explicit_rels, List *relids, List *relids_logged,
|
|||||||
(errmsg("truncate cascades to table \"%s\"",
|
(errmsg("truncate cascades to table \"%s\"",
|
||||||
RelationGetRelationName(rel))));
|
RelationGetRelationName(rel))));
|
||||||
truncate_check_rel(relid, rel->rd_rel);
|
truncate_check_rel(relid, rel->rd_rel);
|
||||||
|
truncate_check_perms(relid, rel->rd_rel);
|
||||||
truncate_check_activity(rel);
|
truncate_check_activity(rel);
|
||||||
rels = lappend(rels, rel);
|
rels = lappend(rels, rel);
|
||||||
relids = lappend_oid(relids, relid);
|
relids = lappend_oid(relids, relid);
|
||||||
@ -1926,7 +1934,6 @@ ExecuteTruncateGuts(List *explicit_rels, List *relids, List *relids_logged,
|
|||||||
static void
|
static void
|
||||||
truncate_check_rel(Oid relid, Form_pg_class reltuple)
|
truncate_check_rel(Oid relid, Form_pg_class reltuple)
|
||||||
{
|
{
|
||||||
AclResult aclresult;
|
|
||||||
char *relname = NameStr(reltuple->relname);
|
char *relname = NameStr(reltuple->relname);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -1940,12 +1947,6 @@ truncate_check_rel(Oid relid, Form_pg_class reltuple)
|
|||||||
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
|
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
|
||||||
errmsg("\"%s\" is not a table", relname)));
|
errmsg("\"%s\" is not a table", relname)));
|
||||||
|
|
||||||
/* Permissions checks */
|
|
||||||
aclresult = pg_class_aclcheck(relid, GetUserId(), ACL_TRUNCATE);
|
|
||||||
if (aclresult != ACLCHECK_OK)
|
|
||||||
aclcheck_error(aclresult, get_relkind_objtype(reltuple->relkind),
|
|
||||||
relname);
|
|
||||||
|
|
||||||
if (!allowSystemTableMods && IsSystemClass(relid, reltuple))
|
if (!allowSystemTableMods && IsSystemClass(relid, reltuple))
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
|
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
|
||||||
@ -1953,6 +1954,22 @@ truncate_check_rel(Oid relid, Form_pg_class reltuple)
|
|||||||
relname)));
|
relname)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check that current user has the permission to truncate given relation.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
truncate_check_perms(Oid relid, Form_pg_class reltuple)
|
||||||
|
{
|
||||||
|
char *relname = NameStr(reltuple->relname);
|
||||||
|
AclResult aclresult;
|
||||||
|
|
||||||
|
/* Permissions checks */
|
||||||
|
aclresult = pg_class_aclcheck(relid, GetUserId(), ACL_TRUNCATE);
|
||||||
|
if (aclresult != ACLCHECK_OK)
|
||||||
|
aclcheck_error(aclresult, get_relkind_objtype(reltuple->relkind),
|
||||||
|
relname);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Set of extra sanity checks to check if a given relation is safe to
|
* Set of extra sanity checks to check if a given relation is safe to
|
||||||
* truncate. This is split with truncate_check_rel() as
|
* truncate. This is split with truncate_check_rel() as
|
||||||
@ -14886,6 +14903,7 @@ RangeVarCallbackForTruncate(const RangeVar *relation,
|
|||||||
elog(ERROR, "cache lookup failed for relation %u", relId);
|
elog(ERROR, "cache lookup failed for relation %u", relId);
|
||||||
|
|
||||||
truncate_check_rel(relId, (Form_pg_class) GETSTRUCT(tuple));
|
truncate_check_rel(relId, (Form_pg_class) GETSTRUCT(tuple));
|
||||||
|
truncate_check_perms(relId, (Form_pg_class) GETSTRUCT(tuple));
|
||||||
|
|
||||||
ReleaseSysCache(tuple);
|
ReleaseSysCache(tuple);
|
||||||
}
|
}
|
||||||
|
@ -695,6 +695,27 @@ SELECT tableoid FROM atestp2; -- ok
|
|||||||
----------
|
----------
|
||||||
(0 rows)
|
(0 rows)
|
||||||
|
|
||||||
|
-- child's permissions do not apply when operating on parent
|
||||||
|
SET SESSION AUTHORIZATION regress_priv_user1;
|
||||||
|
REVOKE ALL ON atestc FROM regress_priv_user2;
|
||||||
|
GRANT ALL ON atestp1 TO regress_priv_user2;
|
||||||
|
SET SESSION AUTHORIZATION regress_priv_user2;
|
||||||
|
SELECT f2 FROM atestp1; -- ok
|
||||||
|
f2
|
||||||
|
----
|
||||||
|
(0 rows)
|
||||||
|
|
||||||
|
SELECT f2 FROM atestc; -- fail
|
||||||
|
ERROR: permission denied for table atestc
|
||||||
|
DELETE FROM atestp1; -- ok
|
||||||
|
DELETE FROM atestc; -- fail
|
||||||
|
ERROR: permission denied for table atestc
|
||||||
|
UPDATE atestp1 SET f1 = 1; -- ok
|
||||||
|
UPDATE atestc SET f1 = 1; -- fail
|
||||||
|
ERROR: permission denied for table atestc
|
||||||
|
TRUNCATE atestp1; -- ok
|
||||||
|
TRUNCATE atestc; -- fail
|
||||||
|
ERROR: permission denied for table atestc
|
||||||
-- privileges on functions, languages
|
-- privileges on functions, languages
|
||||||
-- switch to superuser
|
-- switch to superuser
|
||||||
\c -
|
\c -
|
||||||
|
@ -446,6 +446,20 @@ SELECT fy FROM atestp2; -- ok
|
|||||||
SELECT atestp2 FROM atestp2; -- ok
|
SELECT atestp2 FROM atestp2; -- ok
|
||||||
SELECT tableoid FROM atestp2; -- ok
|
SELECT tableoid FROM atestp2; -- ok
|
||||||
|
|
||||||
|
-- child's permissions do not apply when operating on parent
|
||||||
|
SET SESSION AUTHORIZATION regress_priv_user1;
|
||||||
|
REVOKE ALL ON atestc FROM regress_priv_user2;
|
||||||
|
GRANT ALL ON atestp1 TO regress_priv_user2;
|
||||||
|
SET SESSION AUTHORIZATION regress_priv_user2;
|
||||||
|
SELECT f2 FROM atestp1; -- ok
|
||||||
|
SELECT f2 FROM atestc; -- fail
|
||||||
|
DELETE FROM atestp1; -- ok
|
||||||
|
DELETE FROM atestc; -- fail
|
||||||
|
UPDATE atestp1 SET f1 = 1; -- ok
|
||||||
|
UPDATE atestc SET f1 = 1; -- fail
|
||||||
|
TRUNCATE atestp1; -- ok
|
||||||
|
TRUNCATE atestc; -- fail
|
||||||
|
|
||||||
-- privileges on functions, languages
|
-- privileges on functions, languages
|
||||||
|
|
||||||
-- switch to superuser
|
-- switch to superuser
|
||||||
|
Loading…
Reference in New Issue
Block a user