Fix cascading privilege revoke to notice when privileges are still held.

If we revoke a grant option from some role X, but X still holds the option
via another grant, we should not recursively revoke the privilege from
role(s) Y that X had granted it to.  This was supposedly fixed as one
aspect of commit 4b2dafcc0b1a579ef5daaa2728223006d1ff98e9, but I must not
have tested it, because in fact that code never worked: it forgot to shift
the grant-option bits back over when masking the bits being revoked.

Per bug #6728 from Daniel German.  Back-patch to all active branches,
since this has been wrong since 8.0.
This commit is contained in:
Tom Lane 2012-08-23 17:25:33 -04:00
parent dda44070cc
commit d6a3863506
3 changed files with 76 additions and 2 deletions

View File

@ -1030,11 +1030,11 @@ recursive_revoke(Acl *acl,
if (grantee == ownerId)
return acl;
/* The grantee might still have the privileges via another grantor */
/* The grantee might still have some grant options via another grantor */
still_has = aclmask(acl, grantee, ownerId,
ACL_GRANT_OPTION_FOR(revoke_privs),
ACLMASK_ALL);
revoke_privs &= ~still_has;
revoke_privs &= ~ACL_OPTION_TO_PRIVS(still_has);
if (revoke_privs == ACL_NO_RIGHTS)
return acl;

View File

@ -815,6 +815,56 @@ SELECT has_table_privilege('regressuser1', 'atest4', 'SELECT WITH GRANT OPTION')
t
(1 row)
-- test that dependent privileges are revoked (or not) properly
\c -
set session role regressuser1;
create table dep_priv_test (a int);
grant select on dep_priv_test to regressuser2 with grant option;
grant select on dep_priv_test to regressuser3 with grant option;
set session role regressuser2;
grant select on dep_priv_test to regressuser4 with grant option;
set session role regressuser3;
grant select on dep_priv_test to regressuser4 with grant option;
set session role regressuser4;
grant select on dep_priv_test to regressuser5;
\dp dep_priv_test
Access privileges
Schema | Name | Type | Access privileges | Column access privileges
--------+---------------+-------+-----------------------------------+--------------------------
public | dep_priv_test | table | regressuser1=arwdDxt/regressuser1 |
: regressuser2=r*/regressuser1
: regressuser3=r*/regressuser1
: regressuser4=r*/regressuser2
: regressuser4=r*/regressuser3
: regressuser5=r/regressuser4
(1 row)
set session role regressuser2;
revoke select on dep_priv_test from regressuser4 cascade;
\dp dep_priv_test
Access privileges
Schema | Name | Type | Access privileges | Column access privileges
--------+---------------+-------+-----------------------------------+--------------------------
public | dep_priv_test | table | regressuser1=arwdDxt/regressuser1 |
: regressuser2=r*/regressuser1
: regressuser3=r*/regressuser1
: regressuser4=r*/regressuser3
: regressuser5=r/regressuser4
(1 row)
set session role regressuser3;
revoke select on dep_priv_test from regressuser4 cascade;
\dp dep_priv_test
Access privileges
Schema | Name | Type | Access privileges | Column access privileges
--------+---------------+-------+-----------------------------------+--------------------------
public | dep_priv_test | table | regressuser1=arwdDxt/regressuser1 |
: regressuser2=r*/regressuser1
: regressuser3=r*/regressuser1
(1 row)
set session role regressuser1;
drop table dep_priv_test;
-- clean up
\c
DROP FUNCTION testfunc2(int);

View File

@ -469,6 +469,30 @@ SELECT has_table_privilege('regressuser3', 'atest4', 'SELECT'); -- false
SELECT has_table_privilege('regressuser1', 'atest4', 'SELECT WITH GRANT OPTION'); -- true
-- test that dependent privileges are revoked (or not) properly
\c -
set session role regressuser1;
create table dep_priv_test (a int);
grant select on dep_priv_test to regressuser2 with grant option;
grant select on dep_priv_test to regressuser3 with grant option;
set session role regressuser2;
grant select on dep_priv_test to regressuser4 with grant option;
set session role regressuser3;
grant select on dep_priv_test to regressuser4 with grant option;
set session role regressuser4;
grant select on dep_priv_test to regressuser5;
\dp dep_priv_test
set session role regressuser2;
revoke select on dep_priv_test from regressuser4 cascade;
\dp dep_priv_test
set session role regressuser3;
revoke select on dep_priv_test from regressuser4 cascade;
\dp dep_priv_test
set session role regressuser1;
drop table dep_priv_test;
-- clean up
\c