Skip to content

Commit 3575a57

Browse files
committed
Preserve direct ACEs when migrating old rights
The 6.0.1 saved search and dashboard rights migration decided whether to rename an old name ACE or drop it as a duplicate by calling HasRight for the new right. HasRight honors group membership inheritance, so when several principals converge on one new right and a group was migrated before a principal that belongs to it, the member inherited the new right and its own old name ACE was deleted instead of renamed. The member then lost its direct grant entirely if it later left the group. This affects any inheriting principal: a subgroup or a user who is a member of the migrated group. Test instead for a direct ACE on the same principal and object with a new helper, _has_direct_ace, so a genuine duplicate is still deleted while a principal's own grant is always renamed and kept. Drop the InvalidateACLCache calls that followed each rename. They existed only so the in loop HasRight check would see prior renames. The direct ACE query reads the table directly and does not consult the cache, so they are no longer needed.
1 parent edee8c4 commit 3575a57

1 file changed

Lines changed: 27 additions & 8 deletions

File tree

etc/upgrade/6.0.1/content

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,25 @@
11
use strict;
22
use warnings;
33

4+
# Returns true if the ACE's principal already has a DIRECT ACE for $new_right
5+
# on the same object, ignoring inherited (group-membership) rights. Used to
6+
# collapse multiple old rights that converge on one new right for the SAME
7+
# principal (e.g. the three dashboard rights) without deleting a principal's
8+
# own ACE just because a group it belongs to was migrated first. This applies
9+
# to any principal that inherits through membership -- a subgroup of the
10+
# migrated group, or a user who is a member of it. Using HasRight here would
11+
# honor that inheritance and wrongly delete the principal's own direct grant.
12+
sub _has_direct_ace {
13+
my ( $ace, $new_right ) = @_;
14+
my $existing = RT::ACL->new( RT->SystemUser );
15+
$existing->Limit( FIELD => 'RightName', VALUE => $new_right );
16+
$existing->Limit( FIELD => 'PrincipalId', VALUE => $ace->__Value('PrincipalId') );
17+
$existing->Limit( FIELD => 'PrincipalType', VALUE => $ace->__Value('PrincipalType') );
18+
$existing->Limit( FIELD => 'ObjectType', VALUE => $ace->__Value('ObjectType') );
19+
$existing->Limit( FIELD => 'ObjectId', VALUE => $ace->__Value('ObjectId') );
20+
return $existing->Count ? 1 : 0;
21+
}
22+
423
our @Final = (
524
sub {
625
RT->Logger->debug("Migrating old saved search rights");
@@ -38,21 +57,21 @@ our @Final = (
3857
}
3958

4059
if ($new_right) {
41-
if ( $principal->HasRight( Object => $object, Right => $new_right ) ) {
42-
# If they already added the new right, just remove the old record
60+
if ( _has_direct_ace( $ace, $new_right ) ) {
61+
# This principal already has a direct ACE for the new right on
62+
# this object (a true duplicate), so just remove the old record.
4363
my ( $ret, $msg ) = $ace->DBIx::SearchBuilder::Record::Delete;
4464
if ( !$ret ) {
4565
RT->Logger->error( "Couldn't delete $right for principal #" . $principal->Id . ": $msg" );
4666
}
4767
}
4868
else {
49-
# No new right has been added, so update the old record with the new right name
69+
# No direct new right exists, so update the old record with the new right name
5070
my ( $ret, $msg ) = $ace->__Set( Field => 'RightName', Value => $new_right );
5171
if ( !$ret ) {
5272
RT->Logger->error(
5373
"Couldn't rename $right to $new_right for principal #" . $principal->Id . ": $msg" );
5474
}
55-
RT::Principal->InvalidateACLCache(); # Sync rights check
5675
}
5776
}
5877
}
@@ -75,21 +94,21 @@ our @Final = (
7594

7695
my $new_right = $right =~ /Group/ ? 'AdminGroupDashboard' : 'AdminDashboard';
7796

78-
if ( $principal->HasRight( Object => $object, Right => $new_right ) ) {
79-
# If they already added the new right, just remove the old record
97+
if ( _has_direct_ace( $ace, $new_right ) ) {
98+
# This principal already has a direct ACE for the new right on
99+
# this object (a true duplicate), so just remove the old record.
80100
my ( $ret, $msg ) = $ace->DBIx::SearchBuilder::Record::Delete;
81101
if ( !$ret ) {
82102
RT->Logger->error( "Couldn't delete $right for principal #" . $principal->Id . ": $msg" );
83103
}
84104
}
85105
else {
86-
# No new right has been added, so update the old record with the new right name
106+
# No direct new right exists, so update the old record with the new right name
87107
my ( $ret, $msg ) = $ace->__Set( Field => 'RightName', Value => $new_right );
88108
if ( !$ret ) {
89109
RT->Logger->error(
90110
"Couldn't rename $right to $new_right for principal #" . $principal->Id . ": $msg" );
91111
}
92-
RT::Principal->InvalidateACLCache(); # Sync rights check
93112
}
94113
}
95114
},

0 commit comments

Comments
 (0)