[Rt-commit] r17031 - in rt/3.8/trunk: lib/RT

ruz at bestpractical.com ruz at bestpractical.com
Wed Nov 26 20:52:41 EST 2008


Author: ruz
Date: Wed Nov 26 20:52:41 2008
New Revision: 17031

Modified:
   rt/3.8/trunk/etc/RT_Config.pm.in
   rt/3.8/trunk/lib/RT/Tickets_Overlay.pm

Log:
* add UseSQLForACLChecks option

Modified: rt/3.8/trunk/etc/RT_Config.pm.in
==============================================================================
--- rt/3.8/trunk/etc/RT_Config.pm.in	(original)
+++ rt/3.8/trunk/etc/RT_Config.pm.in	Wed Nov 26 20:52:41 2008
@@ -160,6 +160,20 @@
 
 Set($DatabaseRequireSSL , undef);
 
+=item C<$UseSQLForACLChecks>
+
+In RT for ages ACL are checked after search what in some situtations
+result in empty search pages and wrong count of tickets.
+
+Set C<$UseSQLForACLChecks> to 1 to use SQL and get rid of these problems.
+
+However, this option is beta. In some cases it result in performance
+improvements, but some setups can not handle it.
+
+=cut
+
+Set($UseSQLForACLChecks, undef);
+
 =back
 
 =head1 Incoming Mail Gateway Configuration

Modified: rt/3.8/trunk/lib/RT/Tickets_Overlay.pm
==============================================================================
--- rt/3.8/trunk/lib/RT/Tickets_Overlay.pm	(original)
+++ rt/3.8/trunk/lib/RT/Tickets_Overlay.pm	Wed Nov 26 20:52:41 2008
@@ -243,6 +243,7 @@
         _sql_trattachalias
         _sql_u_watchers_alias_for_sort
         _sql_u_watchers_aliases
+        _sql_current_user_can_see_applied
     );
 }
 
@@ -2632,35 +2633,236 @@
 
     $self->_ProcessRestrictions() if ( $self->{'RecalcTicketLimits'} == 1 );
 
-    my $Ticket = $self->SUPER::Next();
-    if ( ( defined($Ticket) ) and ( ref($Ticket) ) ) {
+    my $Ticket = $self->SUPER::Next;
+    return $Ticket unless $Ticket;
 
-        if ( $Ticket->__Value('Status') eq 'deleted'
-            && !$self->{'allow_deleted_search'} )
-        {
-            return ( $self->Next() );
-        }
+    if ( $Ticket->__Value('Status') eq 'deleted'
+        && !$self->{'allow_deleted_search'} )
+    {
+        return $self->Next;
+    }
+    elsif ( RT->Config->Get('UseSQLForACLChecks') ) {
+        # if we found a ticket with this option enabled then
+        # all tickets we found are ACLed, cache this fact
+        my $key = join ";:;", $self->CurrentUser->id, 'ShowTicket', 'RT::Ticket-'. $Ticket->id;
+        $RT::Principal::_ACL_CACHE->set( $key => 1 );
+        return $Ticket;
+    }
+    elsif ( $Ticket->CurrentUserHasRight('ShowTicket') ) {
+        # has rights
+        return $Ticket;
+    }
+    else {
+        # If the user doesn't have the right to show this ticket
+        return $self->Next;
+    }
+}
+
+sub _DoSearch {
+    my $self = shift;
+    $self->CurrentUserCanSee if RT->Config->Get('UseSQLForACLChecks');
+    return $self->SUPER::_DoSearch( @_ );
+}
+
+sub _DoCount {
+    my $self = shift;
+    $self->CurrentUserCanSee if RT->Config->Get('UseSQLForACLChecks');
+    return $self->SUPER::_DoCount( @_ );
+}
+
+sub _RolesCanSee {
+    my $self = shift;
 
-        # Since Ticket could be granted with more rights instead
-        # of being revoked, it's ok if queue rights allow
-        # ShowTicket.  It seems need another query, but we have
-        # rights cache in Principal::HasRight.
-        elsif ( $Ticket->CurrentUserHasRight('ShowTicket') )
-        {
-            return ($Ticket);
+    my $cache_key = 'RolesHasRight;:;ShowTicket';
+ 
+    if ( my $cached = $RT::Principal::_ACL_CACHE->fetch( $cache_key ) ) {
+        return %$cached;
+    }
+
+    my $ACL = RT::ACL->new( $RT::SystemUser );
+    $ACL->Limit( FIELD => 'RightName', VALUE => 'ShowTicket' );
+    $ACL->Limit( FIELD => 'PrincipalType', OPERATOR => '!=', VALUE => 'Group' );
+    my $principal_alias = $ACL->Join(
+        ALIAS1 => 'main',
+        FIELD1 => 'PrincipalId',
+        TABLE2 => 'Principals',
+        FIELD2 => 'id',
+    );
+    $ACL->Limit( ALIAS => $principal_alias, FIELD => 'Disabled', VALUE => 0 );
+
+    my %res = ();
+    while ( my $ACE = $ACL->Next ) {
+        my $role = $ACE->PrincipalType;
+        my $type = $ACE->ObjectType;
+        if ( $type eq 'RT::System' ) {
+            $res{ $role } = 1;
+        }
+        elsif ( $type eq 'RT::Queue' ) {
+            next if $res{ $role } && !ref $res{ $role };
+            push @{ $res{ $role } ||= [] }, $ACE->ObjectId;
+        }
+        else {
+            $RT::Logger->error('ShowTicket right is granted on unsupported object');
         }
+    }
+    $RT::Principal::_ACL_CACHE->set( $cache_key => \%res );
+    return %res;
+}
 
-        #If the user doesn't have the right to show this ticket
+sub _DirectlyCanSeeIn {
+    my $self = shift;
+    my $id = $self->CurrentUser->id;
+
+    my $cache_key = 'User-'. $id .';:;ShowTicket;:;DirectlyCanSeeIn';
+    if ( my $cached = $RT::Principal::_ACL_CACHE->fetch( $cache_key ) ) {
+        return @$cached;
+    }
+
+    my $ACL = RT::ACL->new( $RT::SystemUser );
+    $ACL->Limit( FIELD => 'RightName', VALUE => 'ShowTicket' );
+    my $principal_alias = $ACL->Join(
+        ALIAS1 => 'main',
+        FIELD1 => 'PrincipalId',
+        TABLE2 => 'Principals',
+        FIELD2 => 'id',
+    );
+    $ACL->Limit( ALIAS => $principal_alias, FIELD => 'Disabled', VALUE => 0 );
+    my $cgm_alias = $ACL->Join(
+        ALIAS1 => 'main',
+        FIELD1 => 'PrincipalId',
+        TABLE2 => 'CachedGroupMembers',
+        FIELD2 => 'GroupId',
+    );
+    $ACL->Limit( ALIAS => $cgm_alias, FIELD => 'MemberId', VALUE => $id );
+    $ACL->Limit( ALIAS => $cgm_alias, FIELD => 'Disabled', VALUE => 0 );
+
+    my @res = ();
+    while ( my $ACE = $ACL->Next ) {
+        my $type = $ACE->ObjectType;
+        if ( $type eq 'RT::System' ) {
+            # If user is direct member of a group that has the right
+            # on the system then he can see any ticket
+            $RT::Principal::_ACL_CACHE->set( $cache_key => [-1] );
+            return (-1);
+        }
+        elsif ( $type eq 'RT::Queue' ) {
+            push @res, $ACE->ObjectId;
+        }
         else {
-            return ( $self->Next() );
+            $RT::Logger->error('ShowTicket right is granted on unsupported object');
         }
     }
+    $RT::Principal::_ACL_CACHE->set( $cache_key => \@res );
+    return @res;
+}
 
-    #if there never was any ticket
-    else {
-        return (undef);
+sub CurrentUserCanSee {
+    my $self = shift;
+    return if $self->{'_sql_current_user_can_see_applied'};
+
+    return $self->{'_sql_current_user_can_see_applied'} = 1
+        if $self->CurrentUser->UserObj->HasRight(
+            Right => 'SuperUser', Object => $RT::System
+        );
+
+    my $id = $self->CurrentUser->id;
+
+    my @direct_queues = $self->_DirectlyCanSeeIn;
+    return $self->{'_sql_current_user_can_see_applied'} = 1
+        if @direct_queues && $direct_queues[0] == -1;
+
+    my %roles = $self->_RolesCanSee;
+    {
+        my %skip = map { $_ => 1 } @direct_queues;
+        foreach my $role ( keys %roles ) {
+            next unless ref $roles{ $role };
+
+            my @queues = grep !$skip{$_}, @{ $roles{ $role } };
+            if ( @queues ) {
+                $roles{ $role } = \@queues;
+            } else {
+                delete $roles{ $role };
+            }
+        }
     }
 
+    {
+        my $join_roles = keys %roles;
+        $join_roles = 0 if $join_roles == 1 && $roles{'Owner'};
+        my ($role_group_alias, $cgm_alias);
+        if ( $join_roles ) {
+            $role_group_alias = $self->_RoleGroupsJoin( New => 1 );
+            $cgm_alias = $self->_GroupMembersJoin( GroupsAlias => $role_group_alias );
+            $self->SUPER::Limit(
+                LEFTJOIN   => $cgm_alias,
+                FIELD      => 'MemberId',
+                OPERATOR   => '=',
+                VALUE      => $id,
+            );
+        }
+        my $limit_queues = sub {
+            my $ea = shift;
+            my @queues = @_;
+
+            return unless @queues;
+            if ( @queues == 1 ) {
+                $self->_SQLLimit(
+                    ALIAS => 'main',
+                    FIELD => 'Queue',
+                    VALUE => $_[0],
+                    ENTRYAGGREGATOR => $ea,
+                );
+            } else {
+                $self->_OpenParen;
+                foreach my $q ( @queues ) {
+                    $self->_SQLLimit(
+                        ALIAS => 'main',
+                        FIELD => 'Queue',
+                        VALUE => $q,
+                        ENTRYAGGREGATOR => $ea,
+                    );
+                    $ea = 'OR';
+                }
+                $self->_CloseParen;
+            }
+            return 1;
+        };
+
+        $self->_OpenParen;
+        my $ea = 'AND';
+        $ea = 'OR' if $limit_queues->( $ea, @direct_queues );
+        while ( my ($role, $queues) = each %roles ) {
+            $self->_OpenParen;
+            if ( $role eq 'Owner' ) {
+                $self->_SQLLimit(
+                    FIELD           => 'Owner',
+                    VALUE           => $id,
+                    ENTRYAGGREGATOR => $ea,
+                );
+            }
+            else {
+                $self->_SQLLimit(
+                    ALIAS           => $cgm_alias,
+                    FIELD           => 'MemberId',
+                    OPERATOR        => 'IS NOT',
+                    VALUE           => 'NULL',
+                    QUOTEVALUE      => 0,
+                    ENTRYAGGREGATOR => $ea,
+                );
+                $self->_SQLLimit(
+                    ALIAS           => $role_group_alias,
+                    FIELD           => 'Type',
+                    VALUE           => $role,
+                    ENTRYAGGREGATOR => 'AND',
+                );
+            }
+            $limit_queues->( 'AND', @$queues ) if ref $queues;
+            $ea = 'OR' if $ea eq 'AND';
+            $self->_CloseParen;
+        }
+        $self->_CloseParen;
+    }
+    return $self->{'_sql_current_user_can_see_applied'} = 1;
 }
 
 # }}}


More information about the Rt-commit mailing list