mirror of
https://github.com/postgres/postgres.git
synced 2025-07-05 07:21:24 +03:00
Fix has_column_privilege function corner case
According to the comments, when an invalid or dropped column oid is passed to has_column_privilege(), the intention has always been to return NULL. However, when the caller had table level privilege the invalid/missing column was never discovered, because table permissions were checked first. Fix that by introducing extended versions of pg_attribute_acl(check|mask) and pg_class_acl(check|mask) which take a new argument, is_missing. When is_missing is NULL, the old behavior is preserved. But when is_missing is passed by the caller, no ERROR is thrown for dropped or missing columns/relations, and is_missing is flipped to true. This in turn allows has_column_privilege to check for column privileges first, providing the desired semantics. Not backpatched since it is a user visible behavioral change with no previous complaints, and the fix is a bit on the invasive side. Author: Joe Conway Reviewed-By: Tom Lane Reported by: Ian Barwick Discussion: https://postgr.es/m/flat/9b5f4311-157b-4164-7fe7-077b4fe8ed84%40joeconway.com
This commit is contained in:
@ -2444,8 +2444,7 @@ column_privilege_check(Oid tableoid, AttrNumber attnum,
|
||||
Oid roleid, AclMode mode)
|
||||
{
|
||||
AclResult aclresult;
|
||||
HeapTuple attTuple;
|
||||
Form_pg_attribute attributeForm;
|
||||
bool is_missing = false;
|
||||
|
||||
/*
|
||||
* If convert_column_name failed, we can just return -1 immediately.
|
||||
@ -2454,42 +2453,25 @@ column_privilege_check(Oid tableoid, AttrNumber attnum,
|
||||
return -1;
|
||||
|
||||
/*
|
||||
* First check if we have the privilege at the table level. We check
|
||||
* existence of the pg_class row before risking calling pg_class_aclcheck.
|
||||
* Note: it might seem there's a race condition against concurrent DROP,
|
||||
* but really it's safe because there will be no syscache flush between
|
||||
* here and there. So if we see the row in the syscache, so will
|
||||
* pg_class_aclcheck.
|
||||
* Check for column-level privileges first. This serves in
|
||||
* part as a check on whether the column even exists, so we
|
||||
* need to do it before checking table-level privilege.
|
||||
*/
|
||||
if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(tableoid)))
|
||||
return -1;
|
||||
|
||||
aclresult = pg_class_aclcheck(tableoid, roleid, mode);
|
||||
|
||||
aclresult = pg_attribute_aclcheck_ext(tableoid, attnum, roleid,
|
||||
mode, &is_missing);
|
||||
if (aclresult == ACLCHECK_OK)
|
||||
return true;
|
||||
|
||||
/*
|
||||
* No table privilege, so try per-column privileges. Again, we have to
|
||||
* check for dropped attribute first, and we rely on the syscache not to
|
||||
* notice a concurrent drop before pg_attribute_aclcheck fetches the row.
|
||||
*/
|
||||
attTuple = SearchSysCache2(ATTNUM,
|
||||
ObjectIdGetDatum(tableoid),
|
||||
Int16GetDatum(attnum));
|
||||
if (!HeapTupleIsValid(attTuple))
|
||||
return 1;
|
||||
else if (is_missing)
|
||||
return -1;
|
||||
attributeForm = (Form_pg_attribute) GETSTRUCT(attTuple);
|
||||
if (attributeForm->attisdropped)
|
||||
{
|
||||
ReleaseSysCache(attTuple);
|
||||
|
||||
/* Next check if we have the privilege at the table level */
|
||||
aclresult = pg_class_aclcheck_ext(tableoid, roleid, mode, &is_missing);
|
||||
if (aclresult == ACLCHECK_OK)
|
||||
return 1;
|
||||
else if (is_missing)
|
||||
return -1;
|
||||
}
|
||||
ReleaseSysCache(attTuple);
|
||||
|
||||
aclresult = pg_attribute_aclcheck(tableoid, attnum, roleid, mode);
|
||||
|
||||
return (aclresult == ACLCHECK_OK);
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
|
Reference in New Issue
Block a user