[Rt-commit] r2446 - in rt/branches/PLATANO-EXPERIMENTAL-CSS: .
lib/RT lib/t/regression
jesse at bestpractical.com
jesse at bestpractical.com
Mon Mar 14 03:02:30 EST 2005
Author: jesse
Date: Mon Mar 14 03:02:28 2005
New Revision: 2446
Added:
rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/t/regression/18stale_delegations_cleanup.t
Modified:
rt/branches/PLATANO-EXPERIMENTAL-CSS/ (props changed)
rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/ACE_Overlay.pm
rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/ACL_Overlay.pm
rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/GroupMember_Overlay.pm
rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Group_Overlay.pm
rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Principal_Overlay.pm
rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/User_Overlay.pm
Log:
r8606 at hualien: jesse | 2005-03-14 02:42:52 -0500
r8494 at hualien: jesse | 2005-03-14 02:28:08 -0500
r6277 at hualien: jesse | 2005-03-05 17:06:08 -0500
RT-Ticket: 6184
RT-Status: resolved
RT-Update: correspond
Much better coverage of delegation revocation when users' group memberships are changed - Mike Whitson (BPS)
Modified: rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/ACE_Overlay.pm
==============================================================================
--- rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/ACE_Overlay.pm (original)
+++ rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/ACE_Overlay.pm Mon Mar 14 03:02:28 2005
@@ -710,18 +710,23 @@
my ( $val, $msg ) = $self->SUPER::Delete(@_);
- #Clear the key cache. TODO someday we may want to just clear a little bit of the keycache space.
- # TODO what about the groups key cache?
- RT::Principal->InvalidateACLCache();
+ # If we're revoking delegation rights (see above), we may need to
+ # revoke all rights delegated by the recipient.
+ if ($val and ($self->RightName() eq 'DelegateRights' or
+ $self->RightName() eq 'SuperUser')) {
+ $val = $self->PrincipalObj->_CleanupInvalidDelegations( InsideTransaction => 1 );
+ }
if ($val) {
+ #Clear the key cache. TODO someday we may want to just clear a little bit of the keycache space.
+ # TODO what about the groups key cache?
+ RT::Principal->InvalidateACLCache();
$RT::Handle->Commit() unless $InsideTransaction;
return ( $val, $self->loc('Right revoked') );
}
- else {
- $RT::Handle->Rollback() unless $InsideTransaction;
- return ( 0, $self->loc('Right could not be revoked') );
- }
+
+ $RT::Handle->Rollback() unless $InsideTransaction;
+ return ( 0, $self->loc('Right could not be revoked') );
}
# }}}
Modified: rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/ACL_Overlay.pm
==============================================================================
--- rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/ACL_Overlay.pm (original)
+++ rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/ACL_Overlay.pm Mon Mar 14 03:02:28 2005
@@ -115,6 +115,42 @@
# }}}
+# {{{ LimitNotObject
+
+=head2 LimitNotObject $object
+
+Limit the ACL to rights NOT on the object $object. $object needs to be
+an RT::Record class.
+
+=cut
+
+sub LimitNotObject {
+ my $self = shift;
+ my $obj = shift;
+ unless ( defined($obj)
+ && ref($obj)
+ && UNIVERSAL::can( $obj, 'id' )
+ && $obj->id )
+ {
+ return undef;
+ }
+ $self->Limit( FIELD => 'ObjectType',
+ OPERATOR => '!=',
+ VALUE => ref($obj),
+ ENTRYAGGREGATOR => 'OR',
+ SUBCLAUSE => $obj->id
+ );
+ $self->Limit( FIELD => 'ObjectId',
+ OPERATOR => '!=',
+ VALUE => $obj->id,
+ ENTRYAGGREGATOR => 'OR',
+ QUOTEVALUE => 0,
+ SUBCLAUSE => $obj->id
+ );
+}
+
+# }}}
+
# {{{ LimitToPrincipal
=head2 LimitToPrincipal { Type => undef, Id => undef, IncludeGroupMembership => undef }
Modified: rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/GroupMember_Overlay.pm
==============================================================================
--- rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/GroupMember_Overlay.pm (original)
+++ rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/GroupMember_Overlay.pm Mon Mar 14 03:02:28 2005
@@ -306,9 +306,6 @@
VALUE => $self->GroupObj->Id
);
- #Clear the key cache. TODO someday we may want to just clear a little bit of the keycache space.
- # TODO what about the groups key cache?
- RT::Principal->InvalidateACLCache();
@@ -328,6 +325,21 @@
$RT::Handle->Rollback();
return (undef);
}
+
+ # Since this deletion may have changed the former member's
+ # delegation rights, we need to ensure that no invalid delegations
+ # remain.
+ $err = $self->MemberObj->_CleanupInvalidDelegations(InsideTransaction => 1);
+ unless ($err) {
+ $RT::Logger->warning("Unable to revoke delegated rights for principal ".$self->Id);
+ $RT::Handle->Rollback();
+ return (undef);
+ }
+
+ #Clear the key cache. TODO someday we may want to just clear a little bit of the keycache space.
+ # TODO what about the groups key cache?
+ RT::Principal->InvalidateACLCache();
+
$RT::Handle->Commit();
return ($err);
Modified: rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Group_Overlay.pm
==============================================================================
--- rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Group_Overlay.pm (original)
+++ rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Group_Overlay.pm Mon Mar 14 03:02:28 2005
@@ -1174,6 +1174,58 @@
# }}}
+# {{{ sub _CleanupInvalidDelegations
+
+=head2 sub _CleanupInvalidDelegations { InsideTransaction => undef }
+
+Revokes all ACE entries delegated by members of this group which are
+inconsistent with their current delegation rights. Does not perform
+permission checks. Should only ever be called from inside the RT
+library.
+
+If called from inside a transaction, specify a true value for the
+InsideTransaction parameter.
+
+Returns a true value if the deletion succeeded; returns a false value
+and logs an internal error if the deletion fails (should not happen).
+
+=cut
+
+# XXX Currently there is a _CleanupInvalidDelegations method in both
+# RT::User and RT::Group. If the recursive cleanup call for groups is
+# ever unrolled and merged, this code will probably want to be
+# factored out into RT::Principal.
+
+sub _CleanupInvalidDelegations {
+ my $self = shift;
+ my %args = ( InsideTransaction => undef,
+ @_ );
+
+ unless ( $self->Id ) {
+ $RT::Logger->warning("Group not loaded.");
+ return (undef);
+ }
+
+ my $in_trans = $args{InsideTransaction};
+
+ # TODO: Can this be unrolled such that the number of DB queries is constant rather than linear in exploded group size?
+ my $members = $self->DeepMembersObj();
+ $members->LimitToUsers();
+ $RT::Handle->BeginTransaction() unless $in_trans;
+ while ( my $member = $members->Next()) {
+ my $ret = $member->MemberObj->_CleanupInvalidDelegations(InsideTransaction => 1,
+ Object => $args{Object});
+ unless ($ret) {
+ $RT::Handle->Rollback() unless $in_trans;
+ return (undef);
+ }
+ }
+ $RT::Handle->Commit() unless $in_trans;
+ return(1);
+}
+
+# }}}
+
# {{{ ACL Related routines
# {{{ sub _Set
Modified: rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Principal_Overlay.pm
==============================================================================
--- rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Principal_Overlay.pm (original)
+++ rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/Principal_Overlay.pm Mon Mar 14 03:02:28 2005
@@ -228,7 +228,40 @@
# }}}
+# {{{ sub _CleanupInvalidDelegations
+=head2 sub _CleanupInvalidDelegations { InsideTransaction => undef }
+
+Revokes all ACE entries delegated by this principal which are
+inconsistent with this principal's current delegation rights. Does
+not perform permission checks, but takes no action and returns success
+if this principal still retains DelegateRights. Should only ever be
+called from inside the RT library.
+
+If this principal is a group, recursively calls this method on each
+cached user member of itself.
+
+If called from inside a transaction, specify a true value for the
+InsideTransaction parameter.
+
+Returns a true value if the deletion succeeded; returns a false value
+and logs an internal error if the deletion fails (should not happen).
+
+=cut
+
+# This is currently just a stub for the methods of the same name in
+# RT::User and RT::Group.
+
+sub _CleanupInvalidDelegations {
+ my $self = shift;
+ unless ( $self->Id ) {
+ $RT::Logger->warning("Principal not loaded.");
+ return (undef);
+ }
+ return ($self->Object->_CleanupInvalidDelegations(@_));
+}
+
+# }}}
# {{{ sub HasRight
Modified: rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/User_Overlay.pm
==============================================================================
--- rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/User_Overlay.pm (original)
+++ rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/RT/User_Overlay.pm Mon Mar 14 03:02:28 2005
@@ -1579,6 +1579,77 @@
# }}}
+# {{{ sub _CleanupInvalidDelegations
+
+=head2 sub _CleanupInvalidDelegations { InsideTransaction => undef }
+
+Revokes all ACE entries delegated by this user which are inconsistent
+with their current delegation rights. Does not perform permission
+checks. Should only ever be called from inside the RT library.
+
+If called from inside a transaction, specify a true value for the
+InsideTransaction parameter.
+
+Returns a true value if the deletion succeeded; returns a false value
+and logs an internal error if the deletion fails (should not happen).
+
+=cut
+
+# XXX Currently there is a _CleanupInvalidDelegations method in both
+# RT::User and RT::Group. If the recursive cleanup call for groups is
+# ever unrolled and merged, this code will probably want to be
+# factored out into RT::Principal.
+
+sub _CleanupInvalidDelegations {
+ my $self = shift;
+ my %args = ( InsideTransaction => undef,
+ @_ );
+
+ unless ( $self->Id ) {
+ $RT::Logger->warning("User not loaded.");
+ return (undef);
+ }
+
+ my $in_trans = $args{InsideTransaction};
+
+ return(1) if ($self->HasRight(Right => 'DelegateRights',
+ Object => $RT::System));
+
+ # Look up all delegation rights currently posessed by this user.
+ my $deleg_acl = RT::ACL->new($RT::SystemUser);
+ $deleg_acl->LimitToPrincipal(Type => 'User',
+ Id => $self->PrincipalId,
+ IncludeGroupMembership => 1);
+ $deleg_acl->Limit( FIELD => 'RightName',
+ OPERATOR => '=',
+ VALUE => 'DelegateRights' );
+ my @allowed_deleg_objects = map {$_->Object()}
+ @{$deleg_acl->ItemsArrayRef()};
+
+ # Look up all rights delegated by this principal which are
+ # inconsistent with the allowed delegation objects.
+ my $acl_to_del = RT::ACL->new($RT::SystemUser);
+ $acl_to_del->DelegatedBy(Id => $self->Id);
+ foreach (@allowed_deleg_objects) {
+ $acl_to_del->LimitNotObject($_);
+ }
+
+ # Delete all disallowed delegations
+ while ( my $ace = $acl_to_del->Next() ) {
+ my $ret = $ace->_Delete(InsideTransaction => 1);
+ unless ($ret) {
+ $RT::Handle->Rollback() unless $in_trans;
+ $RT::Logger->warning("Couldn't delete delegated ACL entry ".$ace->Id);
+ return (undef);
+ }
+ }
+
+ $RT::Handle->Commit() unless $in_trans;
+ return (1);
+}
+
+# }}}
+
# {{{ sub _Set
sub _Set {
Added: rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/t/regression/18stale_delegations_cleanup.t
==============================================================================
--- (empty file)
+++ rt/branches/PLATANO-EXPERIMENTAL-CSS/lib/t/regression/18stale_delegations_cleanup.t Mon Mar 14 03:02:28 2005
@@ -0,0 +1,458 @@
+#!/usr/bin/perl -w
+
+# Regression test suite for http://rt3.fsck.com/Ticket/Display.html?id=6184
+# and related corner cases related to cleanup of delegated ACEs when
+# the delegator loses the right to delegate. This causes complexities
+# due to the fact that multiple ACEs can grant different delegation
+# rights to a principal, and because DelegateRights and SuperUser can
+# themselves be delegated.
+
+# The case where the "parent" delegated ACE is removed is handled in
+# the embedded regression tests in lib/RT/ACE_Overlay.pm .
+
+use Test::More qw(no_plan);
+
+use RT;
+
+ok( RT::LoadConfig, "Locating config files" );
+ok( RT::Init, "Basic initialization and DB connectivity" );
+
+my ($u1, $u2, $g1, $g2, $g3, $pg1, $pg2, $ace, @groups, @users, @principals);
+ at groups = (\$g1, \$g2, \$g3, \$pg1, \$pg2);
+ at users = (\$u1, \$u2);
+ at principals = (@groups, @users);
+
+my($ret, $msg);
+
+$u1 = RT::User->new($RT::SystemUser);
+( $ret, $msg ) = $u1->LoadOrCreateByEmail('delegtest1 at example.com');
+ok( $ret, "Load / Create test user 1: $msg" );
+$u1->SetPrivileged(1);
+$u2 = RT::User->new($RT::SystemUser);
+( $ret, $msg ) = $u2->LoadOrCreateByEmail('delegtest2 at example.com');
+ok( $ret, "Load / Create test user 2: $msg" );
+$u2->SetPrivileged(1);
+$g1 = RT::Group->new($RT::SystemUser);
+( $ret, $msg) = $g1->LoadUserDefinedGroup('dg1');
+unless ($ret) {
+ ( $ret, $msg ) = $g1->CreateUserDefinedGroup( Name => 'dg1' );
+}
+ok( $ret, "Load / Create test group 1: $msg" );
+$g2 = RT::Group->new($RT::SystemUser);
+( $ret, $msg) = $g2->LoadUserDefinedGroup('dg2');
+unless ($ret) {
+ ( $ret, $msg ) = $g2->CreateUserDefinedGroup( Name => 'dg2' );
+}
+ok( $ret, "Load / Create test group 2: $msg" );
+$g3 = RT::Group->new($RT::SystemUser);
+( $ret, $msg) = $g3->LoadUserDefinedGroup('dg3');
+unless ($ret) {
+ ( $ret, $msg ) = $g3->CreateUserDefinedGroup( Name => 'dg3' );
+}
+ok( $ret, "Load / Create test group 3: $msg" );
+$pg1 = RT::Group->new($RT::SystemUser);
+( $ret, $msg ) = $pg1->LoadPersonalGroup( Name => 'dpg1',
+ User => $u1->PrincipalId );
+unless ($ret) {
+ ( $ret, $msg ) = $pg1->CreatePersonalGroup( Name => 'dpg1',
+ PrincipalId => $u1->PrincipalId );
+}
+ok( $ret, "Load / Create test personal group 1: $msg" );
+$pg2 = RT::Group->new($RT::SystemUser);
+( $ret, $msg ) = $pg2->LoadPersonalGroup( Name => 'dpg2',
+ User => $u2->PrincipalId );
+unless ($ret) {
+ ( $ret, $msg ) = $pg2->CreatePersonalGroup( Name => 'dpg2',
+ PrincipalId => $u2->PrincipalId );
+}
+ok( $ret, "Load / Create test personal group 2: $msg" );
+
+
+
+# Basic case: u has global DelegateRights through g1 and ShowConfigTab
+# through g2; then u is removed from g1.
+
+clear_acls_and_groups();
+
+( $ret, $msg ) = $g1->PrincipalObj->GrantRight( Right => 'DelegateRights' );
+ok( $ret, "Grant DelegateRights to g1: $msg" );
+( $ret, $msg ) = $g2->PrincipalObj->GrantRight( Right => 'ShowConfigTab' );
+ok( $ret, "Grant ShowConfigTab to g2: $msg" );
+( $ret, $msg ) = $g1->AddMember( $u1->PrincipalId );
+ok( $ret, "Add test user 1 to g1: $msg" );
+ok(
+ $u1->PrincipalObj->HasRight(
+ Right => 'DelegateRights',
+ Object => $RT::System
+ ),
+ "test user 1 has DelegateRights after joining g1"
+);
+( $ret, $msg ) = $g2->AddMember( $u1->PrincipalId );
+ok( $ret, "Add test user 1 to g2: $msg" );
+ok(
+ $u1->PrincipalObj->HasRight(
+ Right => 'ShowConfigTab',
+ Object => $RT::System
+ ),
+ "test user 1 has ShowConfigTab after joining g2"
+);
+
+$ace = RT::ACE->new($u1);
+( $ret, $msg ) = $ace->LoadByValues(
+ RightName => 'ShowConfigTab',
+ Object => $RT::System,
+ PrincipalType => 'Group',
+ PrincipalId => $g2->PrincipalId
+);
+ok( $ret, "Look up ACE to be delegated: $msg" );
+( $ret, $msg ) = $ace->Delegate( PrincipalId => $pg1->PrincipalId );
+ok( $ret, "Delegate ShowConfigTab to pg1: $msg" );
+ok(
+ $pg1->PrincipalObj->HasRight(
+ Right => 'ShowConfigTab',
+ Object => $RT::System
+ ),
+ "Test personal group 1 has ShowConfigTab right after delegation"
+);
+
+( $ret, $msg ) = $g1->DeleteMember( $u1->PrincipalId );
+ok( $ret, "Delete test user 1 from g1: $msg" );
+ok(
+ not(
+ $pg1->PrincipalObj->HasRight(
+ Right => 'ShowConfigTab',
+ Object => $RT::System
+ )
+ ),
+ "Test personal group 1 lacks ShowConfigTab right after user removed from g1"
+);
+
+# Basic case: u has global DelegateRights through g1 and ShowConfigTab
+# through g2; then DelegateRights revoked from g1.
+
+( $ret, $msg ) = $g1->AddMember( $u1->PrincipalId );
+ok( $ret, "Add test user 1 to g1: $msg" );
+( $ret, $msg ) = $ace->Delegate( PrincipalId => $pg1->PrincipalId );
+ok( $ret, "Delegate ShowConfigTab to pg1: $msg" );
+( $ret, $msg ) = $g1->PrincipalObj->RevokeRight( Right => 'DelegateRights' );
+ok( $ret, "Revoke DelegateRights from g1: $msg" );
+ok(
+ not(
+ $pg1->PrincipalObj->HasRight(
+ Right => 'ShowConfigTab',
+ Object => $RT::System
+ )
+ ),
+ "Test personal group 1 lacks ShowConfigTab right after DelegateRights revoked from g1"
+);
+
+
+
+# Corner case - restricted delegation: u has DelegateRights on pg1
+# through g1 and AdminGroup on pg1 through g2; then DelegateRights
+# revoked from g1.
+
+clear_acls_and_groups();
+
+( $ret, $msg ) = $g1->PrincipalObj->GrantRight( Right => 'DelegateRights',
+ Object => $pg1);
+ok( $ret, "Grant DelegateRights on pg1 to g1: $msg" );
+( $ret, $msg ) = $g2->PrincipalObj->GrantRight( Right => 'AdminGroup',
+ Object => $pg1);
+ok( $ret, "Grant AdminGroup on pg1 to g2: $msg" );
+( $ret, $msg ) = $g1->AddMember( $u1->PrincipalId );
+ok( $ret, "Add test user 1 to g1: $msg" );
+( $ret, $msg ) = $g2->AddMember( $u1->PrincipalId );
+ok( $ret, "Add test user 1 to g2: $msg" );
+ok( $u1->PrincipalObj->HasRight(
+ Right => 'DelegateRights',
+ Object => $pg1 ),
+ "test user 1 has DelegateRights on pg1 after joining g1" );
+ok( not( $u1->PrincipalObj->HasRight(
+ Right => 'DelegateRights',
+ Object => $RT::System )),
+ "Test personal group 1 lacks global DelegateRights after joining g1" );
+$ace = RT::ACE->new($u1);
+( $ret, $msg ) = $ace->LoadByValues(
+ RightName => 'AdminGroup',
+ Object => $pg1,
+ PrincipalType => 'Group',
+ PrincipalId => $g2->PrincipalId
+);
+ok( $ret, "Look up ACE to be delegated: $msg" );
+( $ret, $msg ) = $ace->Delegate( PrincipalId => $pg1->PrincipalId );
+ok( $ret, "Delegate AdminGroup on pg1 to pg1: $msg" );
+ok( $pg1->PrincipalObj->HasRight(
+ Right => 'AdminGroup',
+ Object => $pg1 ),
+ "Test personal group 1 has AdminGroup right on pg1 after delegation" );
+( $ret, $msg ) = $g1->PrincipalObj->RevokeRight ( Right => 'DelegateRights',
+ Object => $pg1 );
+ok( $ret, "Revoke DelegateRights on pg1 from g1: $msg" );
+ok( not( $pg1->PrincipalObj->HasRight(
+ Right => 'AdminGroup',
+ Object => $pg1 )),
+ "Test personal group 1 lacks AdminGroup right on pg1 after DelegateRights revoked from g1" );
+( $ret, $msg ) = $g1->PrincipalObj->GrantRight( Right => 'DelegateRights',
+ Object => $pg1);
+
+# Corner case - restricted delegation: u has DelegateRights on pg1
+# through g1 and AdminGroup on pg1 through g2; then u removed from g1.
+
+ok( $ret, "Grant DelegateRights on pg1 to g1: $msg" );
+( $ret, $msg ) = $ace->Delegate( PrincipalId => $pg1->PrincipalId );
+ok( $ret, "Delegate AdminGroup on pg1 to pg1: $msg" );
+ok( $pg1->PrincipalObj->HasRight(
+ Right => 'AdminGroup',
+ Object => $pg1 ),
+ "Test personal group 1 has AdminGroup right on pg1 after delegation" );
+( $ret, $msg ) = $g1->DeleteMember( $u1->PrincipalId );
+ok( $ret, "Delete test user 1 from g1: $msg" );
+ok( not( $pg1->PrincipalObj->HasRight(
+ Right => 'AdminGroup',
+ Object => $pg1 )),
+ "Test personal group 1 lacks AdminGroup right on pg1 after user removed from g1" );
+
+clear_acls_and_groups();
+
+
+
+# Corner case - multiple delegation rights: u has global
+# DelegateRights directly and DelegateRights on pg1 through g1, and
+# AdminGroup on pg1 through g2; then u removed from g1 (delegation
+# should remain); then DelegateRights revoked from u (delegation
+# should not remain).
+
+( $ret, $msg ) = $g1->PrincipalObj->GrantRight( Right => 'DelegateRights',
+ Object => $pg1);
+ok( $ret, "Grant DelegateRights on pg1 to g1: $msg" );
+( $ret, $msg ) = $g2->PrincipalObj->GrantRight( Right => 'AdminGroup',
+ Object => $pg1);
+ok( $ret, "Grant AdminGroup on pg1 to g2: $msg" );
+( $ret, $msg ) = $u1->PrincipalObj->GrantRight( Right => 'DelegateRights',
+ Object => $RT::System);
+ok( $ret, "Grant DelegateRights to user: $msg" );
+( $ret, $msg ) = $g1->AddMember( $u1->PrincipalId );
+ok( $ret, "Add test user 1 to g1: $msg" );
+( $ret, $msg ) = $g2->AddMember( $u1->PrincipalId );
+ok( $ret, "Add test user 1 to g2: $msg" );
+$ace = RT::ACE->new($u1);
+( $ret, $msg ) = $ace->LoadByValues(
+ RightName => 'AdminGroup',
+ Object => $pg1,
+ PrincipalType => 'Group',
+ PrincipalId => $g2->PrincipalId
+);
+ok( $ret, "Look up ACE to be delegated: $msg" );
+( $ret, $msg ) = $ace->Delegate( PrincipalId => $pg1->PrincipalId );
+ok( $ret, "Delegate AdminGroup on pg1 to pg1: $msg" );
+( $ret, $msg ) = $g1->DeleteMember( $u1->PrincipalId );
+ok( $ret, "Delete test user 1 from g1: $msg" );
+ok( $pg1->PrincipalObj->HasRight(Right => 'AdminGroup',
+ Object => $pg1),
+ "Test personal group 1 retains AdminGroup right on pg1 after user removed from g1" );
+( $ret, $msg ) = $u1->PrincipalObj->RevokeRight( Right => 'DelegateRights',
+ Object => $RT::System );
+ok( not ($pg1->PrincipalObj->HasRight(Right => 'AdminGroup',
+ Object => $pg1)),
+ "Test personal group 1 lacks AdminGroup right on pg1 after DelegateRights revoked");
+
+# Corner case - multiple delegation rights and selectivity: u has
+# DelegateRights globally and on g2 directly and DelegateRights on pg1
+# through g1, and AdminGroup on pg1 through g2; then global
+# DelegateRights revoked from u (delegation should remain),
+# DelegateRights on g2 revoked from u (delegation should remain), and
+# u removed from g1 (delegation should not remain).
+
+( $ret, $msg ) = $g1->AddMember( $u1->PrincipalId );
+ok( $ret, "Add test user 1 to g1: $msg" );
+( $ret, $msg ) = $u1->PrincipalObj->GrantRight( Right => 'DelegateRights',
+ Object => $RT::System);
+ok( $ret, "Grant DelegateRights to user: $msg" );
+( $ret, $msg ) = $u1->PrincipalObj->GrantRight( Right => 'DelegateRights',
+ Object => $g2);
+ok( $ret, "Grant DelegateRights on g2 to user: $msg" );
+( $ret, $msg ) = $ace->Delegate( PrincipalId => $pg1->PrincipalId );
+ok( $ret, "Delegate AdminGroup on pg1 to pg1: $msg" );
+( $ret, $msg ) = $u1->PrincipalObj->RevokeRight( Right => 'DelegateRights',
+ Object => $RT::System );
+ok( $pg1->PrincipalObj->HasRight(Right => 'AdminGroup',
+ Object => $pg1),
+ "Test personal group 1 retains AdminGroup right on pg1 after global DelegateRights revoked" );
+( $ret, $msg ) = $u1->PrincipalObj->RevokeRight( Right => 'DelegateRights',
+ Object => $g2 );
+ok( $pg1->PrincipalObj->HasRight(Right => 'AdminGroup',
+ Object => $pg1),
+ "Test personal group 1 retains AdminGroup right on pg1 after DelegateRights on g2 revoked" );
+( $ret, $msg ) = $g1->DeleteMember( $u1->PrincipalId );
+ok( $ret, "Delete test user 1 from g1: $msg" );
+ok( not ($pg1->PrincipalObj->HasRight(Right => 'AdminGroup',
+ Object => $pg1)),
+ "Test personal group 1 lacks AdminGroup right on pg1 after user removed from g1");
+
+
+
+# Corner case - indirect delegation rights: u has DelegateRights
+# through g1 via g3, and ShowConfigTab via g2; then g3 removed from
+# g1.
+
+clear_acls_and_groups();
+
+( $ret, $msg ) = $g1->PrincipalObj->GrantRight( Right => 'DelegateRights' );
+ok( $ret, "Grant DelegateRights to g1: $msg" );
+( $ret, $msg ) = $g2->PrincipalObj->GrantRight( Right => 'ShowConfigTab' );
+ok( $ret, "Grant ShowConfigTab to g2: $msg" );
+( $ret, $msg ) = $g1->AddMember( $g3->PrincipalId );
+ok( $ret, "Add g3 to g1: $msg" );
+( $ret, $msg ) = $g3->AddMember( $u1->PrincipalId );
+ok( $ret, "Add test user 1 to g3: $msg" );
+( $ret, $msg ) = $g2->AddMember( $u1->PrincipalId );
+ok( $ret, "Add test user 1 to g2: $msg" );
+
+$ace = RT::ACE->new($u1);
+( $ret, $msg ) = $ace->LoadByValues(
+ RightName => 'ShowConfigTab',
+ Object => $RT::System,
+ PrincipalType => 'Group',
+ PrincipalId => $g2->PrincipalId
+);
+ok( $ret, "Look up ACE to be delegated: $msg" );
+( $ret, $msg ) = $ace->Delegate( PrincipalId => $pg1->PrincipalId );
+ok( $ret, "Delegate ShowConfigTab to pg1: $msg" );
+
+( $ret, $msg ) = $g1->DeleteMember( $g3->PrincipalId );
+ok( $ret, "Delete g3 from g1: $msg" );
+ok( not ($pg1->PrincipalObj->HasRight(Right => 'ShowConfigTab',
+ Object => $RT::System)),
+ "Test personal group 1 lacks ShowConfigTab right after g3 removed from g1");
+
+# Corner case - indirect delegation rights: u has DelegateRights
+# through g1 via g3, and ShowConfigTab via g2; then DelegateRights
+# revoked from g1.
+
+( $ret, $msg ) = $g1->AddMember( $g3->PrincipalId );
+ok( $ret, "Add g3 to g1: $msg" );
+( $ret, $msg ) = $ace->Delegate( PrincipalId => $pg1->PrincipalId );
+ok( $ret, "Delegate ShowConfigTab to pg1: $msg" );
+( $ret, $msg ) = $g1->PrincipalObj->RevokeRight ( Right => 'DelegateRights' );
+ok( $ret, "Revoke DelegateRights from g1: $msg" );
+
+ok( not ($pg1->PrincipalObj->HasRight(Right => 'ShowConfigTab',
+ Object => $RT::System)),
+ "Test personal group 1 lacks ShowConfigTab right after DelegateRights revoked from g1");
+
+
+
+# Corner case - delegation of DelegateRights: u1 has DelegateRights
+# via g1 and delegates DelegateRights to pg1; u2 has DelegateRights
+# via pg1 and ShowConfigTab via g2; then u1 removed from g1.
+
+clear_acls_and_groups();
+
+( $ret, $msg ) = $g1->PrincipalObj->GrantRight( Right => 'DelegateRights' );
+ok( $ret, "Grant DelegateRights to g1: $msg" );
+( $ret, $msg ) = $g2->PrincipalObj->GrantRight( Right => 'ShowConfigTab' );
+ok( $ret, "Grant ShowConfigTab to g2: $msg" );
+( $ret, $msg ) = $g1->AddMember( $u1->PrincipalId );
+ok( $ret, "Add test user 1 to g1: $msg" );
+$ace = RT::ACE->new($u1);
+( $ret, $msg ) = $ace->LoadByValues(
+ RightName => 'DelegateRights',
+ Object => $RT::System,
+ PrincipalType => 'Group',
+ PrincipalId => $g1->PrincipalId
+);
+ok( $ret, "Look up ACE to be delegated: $msg" );
+( $ret, $msg ) = $ace->Delegate( PrincipalId => $pg1->PrincipalId );
+ok( $ret, "Delegate DelegateRights to pg1: $msg" );
+
+( $ret, $msg ) = $pg1->AddMember( $u2->PrincipalId );
+ok( $ret, "Add test user 2 to pg1: $msg" );
+( $ret, $msg ) = $g2->AddMember( $u2->PrincipalId );
+ok( $ret, "Add test user 2 to g2: $msg" );
+$ace = RT::ACE->new($u2);
+( $ret, $msg ) = $ace->LoadByValues(
+ RightName => 'ShowConfigTab',
+ Object => $RT::System,
+ PrincipalType => 'Group',
+ PrincipalId => $g2->PrincipalId
+);
+ok( $ret, "Look up ACE to be delegated: $msg" );
+( $ret, $msg ) = $ace->Delegate( PrincipalId => $pg2->PrincipalId );
+ok( $ret, "Delegate ShowConfigTab to pg2: $msg" );
+
+ok( $pg2->PrincipalObj->HasRight(Right => 'ShowConfigTab',
+ Object => $RT::System),
+ "Test personal group 2 has ShowConfigTab right after delegation");
+( $ret, $msg ) = $g1->DeleteMember( $u1->PrincipalId );
+ok( $ret, "Delete u1 from g1: $msg" );
+ok( not ($pg2->PrincipalObj->HasRight(Right => 'ShowConfigTab',
+ Object => $RT::System)),
+ "Test personal group 2 lacks ShowConfigTab right after u1 removed from g1");
+
+# Corner case - delegation of DelegateRights: u1 has DelegateRights
+# via g1 and delegates DelegateRights to pg1; u2 has DelegateRights
+# via pg1 and ShowConfigTab via g2; then DelegateRights revoked from
+# g1.
+
+( $ret, $msg ) = $g1->AddMember( $u1->PrincipalId );
+ok( $ret, "Add u1 to g1: $msg" );
+$ace = RT::ACE->new($u1);
+( $ret, $msg ) = $ace->LoadByValues(
+ RightName => 'DelegateRights',
+ Object => $RT::System,
+ PrincipalType => 'Group',
+ PrincipalId => $g1->PrincipalId
+);
+ok( $ret, "Look up ACE to be delegated: $msg" );
+( $ret, $msg ) = $ace->Delegate( PrincipalId => $pg1->PrincipalId );
+ok( $ret, "Delegate DelegateRights to pg1: $msg" );
+$ace = RT::ACE->new($u2);
+( $ret, $msg ) = $ace->LoadByValues(
+ RightName => 'ShowConfigTab',
+ Object => $RT::System,
+ PrincipalType => 'Group',
+ PrincipalId => $g2->PrincipalId
+);
+ok( $ret, "Look up ACE to be delegated: $msg" );
+( $ret, $msg ) = $ace->Delegate( PrincipalId => $pg2->PrincipalId );
+ok( $ret, "Delegate ShowConfigTab to pg2: $msg" );
+
+( $ret, $msg ) = $g1->PrincipalObj->RevokeRight ( Right => 'DelegateRights' );
+ok( $ret, "Revoke DelegateRights from g1: $msg" );
+ok( not ($pg2->PrincipalObj->HasRight(Right => 'ShowConfigTab',
+ Object => $RT::System)),
+ "Test personal group 2 lacks ShowConfigTab right after DelegateRights revoked from g1");
+
+
+
+
+#######
+
+sub clear_acls_and_groups {
+ # Revoke all rights granted to our cast
+ my $acl = RT::ACL->new($RT::SystemUser);
+ foreach (@principals) {
+ $acl->LimitToPrincipal(Type => $$_->PrincipalObj->PrincipalType,
+ Id => $$_->PrincipalObj->Id);
+ }
+ while (my $ace = $acl->Next()) {
+ $ace->Delete();
+ }
+
+ # Remove all group memberships
+ my $members = RT::GroupMembers->new($RT::SystemUser);
+ foreach (@groups) {
+ $members->LimitToMembersOfGroup( $$_->PrincipalId );
+ }
+ while (my $member = $members->Next()) {
+ $member->Delete();
+ }
+
+ $acl->RedoSearch();
+ ok( $acl->Count() == 0,
+ "All principals have no rights after clearing ACLs" );
+ $members->RedoSearch();
+ ok( $members->Count() == 0,
+ "All groups have no members after clearing groups" );
+}
More information about the Rt-commit
mailing list