X-Git-Url: http://git.freeside.biz/gitweb/?a=blobdiff_plain;f=rt%2Flib%2FRT%2FPrincipal_Overlay.pm;h=a8e8f3c42716335e4ff5d1fd5e367979c4c2f2d3;hb=f9bc93ed7ac02a666d09a213d5093f3727738936;hp=1a14fa1b00d62f9495a39d592402d4bc37a4d385;hpb=2041a9143fac20b79ead4a1ae01224dedf5b27c2;p=freeside.git diff --git a/rt/lib/RT/Principal_Overlay.pm b/rt/lib/RT/Principal_Overlay.pm index 1a14fa1b0..a8e8f3c42 100644 --- a/rt/lib/RT/Principal_Overlay.pm +++ b/rt/lib/RT/Principal_Overlay.pm @@ -1,34 +1,69 @@ -# BEGIN LICENSE BLOCK -# -# Copyright (c) 1996-2003 Jesse Vincent -# -# (Except where explictly superceded by other copyright notices) -# +# BEGIN BPS TAGGED BLOCK {{{ +# +# COPYRIGHT: +# +# This software is Copyright (c) 1996-2011 Best Practical Solutions, LLC +# +# +# (Except where explicitly superseded by other copyright notices) +# +# +# LICENSE: +# # This work is made available to you under the terms of Version 2 of # the GNU General Public License. A copy of that license should have # been provided with this software, but in any event can be snarfed # from www.gnu.org. -# +# # This work is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. -# -# Unless otherwise specified, all modifications, corrections or -# extensions to this work which alter its source code become the -# property of Best Practical Solutions, LLC when submitted for -# inclusion in the work. -# -# -# END LICENSE BLOCK +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301 or visit their web page on the internet at +# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. +# +# +# CONTRIBUTION SUBMISSION POLICY: +# +# (The following paragraph is not intended to limit the rights granted +# to you to modify and distribute this software under the terms of +# the GNU General Public License and is only of importance to you if +# you choose to contribute your changes and enhancements to the +# community by submitting them to Best Practical Solutions, LLC.) +# +# By intentionally submitting any modifications, corrections or +# derivatives to this work, or any other work intended for use with +# Request Tracker, to Best Practical Solutions, LLC, you confirm that +# you are the copyright holder for those contributions and you grant +# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, +# royalty-free, perpetual, license to use, copy, create derivative +# works based on those contributions, and sublicense and distribute +# those contributions and any derivatives thereof. +# +# END BPS TAGGED BLOCK }}} + +# + +package RT::Principal; + use strict; +use warnings; + +use Cache::Simple::TimedExpiry; -no warnings qw(redefine); -use vars qw(%_ACL_KEY_CACHE); +use RT; use RT::Group; use RT::User; +# Set up the ACL cache on startup +our $_ACL_CACHE; +InvalidateACLCache(); + # {{{ IsGroup =head2 IsGroup @@ -40,12 +75,11 @@ Returns undef, otherwise sub IsGroup { my $self = shift; - if ($self->PrincipalType eq 'Group') { - return(1); - } - else { - return undef; + if ( defined $self->PrincipalType && + $self->PrincipalType eq 'Group' ) { + return 1; } + return undef; } # }}} @@ -82,18 +116,18 @@ Returns the user or group associated with this principal sub Object { my $self = shift; - unless ($self->{'object'}) { - if ($self->IsUser) { - $self->{'object'} = RT::User->new($self->CurrentUser); - } - elsif ($self->IsGroup) { - $self->{'object'} = RT::Group->new($self->CurrentUser); - } - else { - $RT::Logger->crit("Found a principal (".$self->Id.") that was neither a user nor a group"); - return(undef); - } - $self->{'object'}->Load($self->ObjectId()); + unless ( $self->{'object'} ) { + if ( $self->IsUser ) { + $self->{'object'} = RT::User->new($self->CurrentUser); + } + elsif ( $self->IsGroup ) { + $self->{'object'} = RT::Group->new($self->CurrentUser); + } + else { + $RT::Logger->crit("Found a principal (".$self->Id.") that was neither a user nor a group"); + return(undef); + } + $self->{'object'}->Load( $self->ObjectId() ); } return ($self->{'object'}); @@ -109,38 +143,36 @@ sub Object { A helper function which calls RT::ACE->Create -=cut - -sub GrantRight { - my $self = shift; - my %args = ( Right => undef, - Object => undef, - @_); - #if we haven't specified any sort of right, we're talking about a global right - if (!defined $args{'Object'} && !defined $args{'ObjectId'} && !defined $args{'ObjectType'}) { - $args{'Object'} = $RT::System; - } + Returns a tuple of (STATUS, MESSAGE); If the call succeeded, STATUS is true. Otherwise it's + false. - unless ($args{'Right'}) { - return(0, $self->loc("Invalid Right")); - } +=cut +sub GrantRight { + my $self = shift; + my %args = ( + Right => undef, + Object => undef, + @_ + ); #ACL check handled in ACE.pm my $ace = RT::ACE->new( $self->CurrentUser ); - my $type = $self->_GetPrincipalTypeForACL(); + RT->System->QueueCacheNeedsUpdate(1) if $args{'Right'} eq 'SeeQueue'; + # If it's a user, we really want to grant the right to their # user equivalence group - return ( $ace->Create(RightName => $args{'Right'}, - Object => $args{'Object'}, - PrincipalType => $type, - PrincipalId => $self->Id - ) ); + return $ace->Create( + RightName => $args{'Right'}, + Object => $args{'Object'}, + PrincipalType => $type, + PrincipalId => $self->Id, + ); } # }}} @@ -150,13 +182,18 @@ sub GrantRight { Delete a right that a user has + + Returns a tuple of (STATUS, MESSAGE); If the call succeeded, STATUS is true. Otherwise it's + false. + + =cut sub RevokeRight { my $self = shift; my %args = ( - Right => undef, + Right => undef, Object => undef, @_ ); @@ -169,22 +206,58 @@ sub RevokeRight { my $type = $self->_GetPrincipalTypeForACL(); my $ace = RT::ACE->new( $self->CurrentUser ); - $ace->LoadByValues( + my ($status, $msg) = $ace->LoadByValues( RightName => $args{'Right'}, - Object => $args{'Object'}, + Object => $args{'Object'}, PrincipalType => $type, PrincipalId => $self->Id ); - unless ( $ace->Id ) { - return ( 0, $self->loc("ACE not found") ); - } - return ( $ace->Delete ); + RT->System->QueueCacheNeedsUpdate(1) if $args{'Right'} eq 'SeeQueue'; + return ($status, $msg) unless $status; + return $ace->Delete; } # }}} +# {{{ 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. + +# backcompat for 3.8.8 and before +*_CleanupInvalidDelegations = \&CleanupInvalidDelegations; + +sub CleanupInvalidDelegations { + my $self = shift; + unless ( $self->Id ) { + $RT::Logger->warning("Principal not loaded."); + return (undef); + } + return ($self->Object->CleanupInvalidDelegations(@_)); +} + +# }}} # {{{ sub HasRight @@ -208,8 +281,6 @@ This takes the params: Object => an RT style object (->id will get its id) - - Returns 1 if a matching ACE was found. Returns undef if no ACE was found. @@ -219,270 +290,285 @@ Returns undef if no ACE was found. sub HasRight { my $self = shift; - my %args = ( Right => undef, - Object => undef, - EquivObjects => undef, - @_ ); + my %args = ( + Right => undef, + Object => undef, + EquivObjects => undef, + @_, + ); - if ( $self->Disabled ) { - $RT::Logger->err( "Disabled User: " . $self->id . " failed access check for " . $args{'Right'} ); + unless ( $args{'Right'} ) { + $RT::Logger->crit("HasRight called without a right"); return (undef); } - if ( !defined $args{'Right'} ) { - require Carp; - $RT::Logger->debug( Carp::cluck("HasRight called without a right") ); - return (undef); + my $canonic_name = RT::ACE->CanonicalizeRightName( $args{'Right'} ); + unless ( $canonic_name ) { + $RT::Logger->error("Invalid right. Couldn't canonicalize right '$args{'Right'}'"); + return undef; } + $args{'Right'} = $canonic_name; + + $args{'EquivObjects'} = [ @{ $args{'EquivObjects'} } ] + if $args{'EquivObjects'}; - if ( defined( $args{'Object'} )) { - return (undef) unless (UNIVERSAL::can( $args{'Object'}, 'id' ) ); - push(@{$args{'EquivObjects'}}, $args{Object}); + if ( $self->Disabled ) { + $RT::Logger->debug( "Disabled User #" + . $self->id + . " failed access check for " + . $args{'Right'} ); + return (undef); } - elsif ( $args{'ObjectId'} && $args{'ObjectType'} ) { - $RT::Logger->crit(Carp::cluck("API not supprted")); + + if ( defined( $args{'Object'} ) + && UNIVERSAL::can( $args{'Object'}, 'id' ) + && $args{'Object'}->id ) { + + push @{ $args{'EquivObjects'} }, $args{'Object'}; } else { - $RT::Logger->crit("$self HasRight called with no valid object"); + $RT::Logger->crit("HasRight called with no valid object"); return (undef); } - # If this object is a ticket, we care about ticket roles and queue roles - if ( (ref($args{'Object'}) eq 'RT::Ticket') && $args{'Object'}->Id) { - # this is a little bit hacky, but basically, now that we've done the ticket roles magic, we load the queue object - # and ask all the rest of our questions about the queue. - push (@{$args{'EquivObjects'}}, $args{'Object'}->QueueObj); - } + unshift @{ $args{'EquivObjects'} }, $args{'Object'}->ACLEquivalenceObjects; + unshift @{ $args{'EquivObjects'} }, $RT::System + unless $self->can('_IsOverrideGlobalACL') + && $self->_IsOverrideGlobalACL( $args{'Object'} ); # {{{ If we've cached a win or loss for this lookup say so - # {{{ Construct a hashkey to cache decisions in - my $hashkey = do { - no warnings 'uninitialized'; - - # We don't worry about the hash ordering, as this is only - # temporarily used; also if the key changes it would be - # invalidated anyway. - join ( - ";:;", $self->Id, map { - $_, # the key of each arguments - ($_ eq 'EquivObjects') # for object arrayref... - ? map(_ReferenceId($_), @{$args{$_}}) # calculate each - : _ReferenceId( $args{$_} ) # otherwise just the value - } keys %args - ); - }; - # }}} - - #Anything older than 60 seconds needs to be rechecked - my $cache_timeout = ( time - 60 ); - - # {{{ if we've cached a positive result for this query, return 1 - if ( ( defined $self->_ACLCache->{"$hashkey"} ) - && ( $self->_ACLCache->{"$hashkey"}{'val'} == 1 ) - && ( defined $self->_ACLCache->{"$hashkey"}{'set'} ) - && ( $self->_ACLCache->{"$hashkey"}{'set'} > $cache_timeout ) ) { - - #$RT::Logger->debug("Cached ACL win for ". $args{'Right'}.$args{'Scope'}. $args{'AppliesTo'}."\n"); - return ( 1); + # Construct a hashkeys to cache decisions: + # 1) full_hashkey - key for any result and for full combination of uid, right and objects + # 2) short_hashkey - one key for each object to store positive results only, it applies + # only to direct group rights and partly to role rights + my $self_id = $self->id; + my $full_hashkey = join ";:;", $self_id, $args{'Right'}; + foreach ( @{ $args{'EquivObjects'} } ) { + my $ref_id = _ReferenceId($_); + $full_hashkey .= ";:;$ref_id"; + + my $short_hashkey = join ";:;", $self_id, $args{'Right'}, $ref_id; + my $cached_answer = $_ACL_CACHE->fetch($short_hashkey); + return $cached_answer > 0 if defined $cached_answer; } - # }}} - - # {{{ if we've cached a negative result for this query return undef - elsif ( ( defined $self->_ACLCache->{"$hashkey"} ) - && ( $self->_ACLCache->{"$hashkey"}{'val'} == -1 ) - && ( defined $self->_ACLCache->{"$hashkey"}{'set'} ) - && ( $self->_ACLCache->{"$hashkey"}{'set'} > $cache_timeout ) ) { - #$RT::Logger->debug("Cached ACL loss decision for ". $args{'Right'}.$args{'Scope'}. $args{'AppliesTo'}."\n"); - - return (undef); + { + my $cached_answer = $_ACL_CACHE->fetch($full_hashkey); + return $cached_answer > 0 if defined $cached_answer; } - # }}} - - # }}} - - - - # {{{ Out of date docs - - # We want to grant the right if: - - - # # The user has the right as a member of a system-internal or - # # user-defined group - # - # Find all records from the ACL where they're granted to a group - # of type "UserDefined" or "System" - # for the object "System or the object "Queue N" and the group we're looking - # at has the recursive member $self->Id - # - # # The user has the right based on a role - # - # Find all the records from ACL where they're granted to the role "foo" - # for the object "System" or the object "Queue N" and the group we're looking - # at is of domain ("RT::Queue-Role" and applies to the right queue) - # or ("RT::Ticket-Role" and applies to the right ticket) - # and the type is the same as the type of the ACL and the group has - # the recursive member $self->Id - # - - # }}} - - my ( $or_look_at_object_rights, $or_check_roles ); - my $right = $args{'Right'}; - - # {{{ Construct Right Match - - # If an object is defined, we want to look at rights for that object - - my @look_at_objects; - push (@look_at_objects, "ACL.ObjectType = 'RT::System'") - unless $self->can('_IsOverrideGlobalACL') and $self->_IsOverrideGlobalACL($args{Object}); - - - - foreach my $obj (@{$args{'EquivObjects'}}) { - next unless (UNIVERSAL::can($obj, 'id')); - my $type = ref($obj); - my $id = $obj->id; - unless ($id) { - use Carp; - Carp::cluck("Trying to check $type rights for an unspecified $type"); - $RT::Logger->crit("Trying to check $type rights for an unspecified $type"); - } - push @look_at_objects, "(ACL.ObjectType = '$type' AND ACL.ObjectId = '$id')"; - } - - # }}} - - # {{{ Build that honkin-big SQL query - - - - my $query_base = "SELECT ACL.id from ACL, Groups, Principals, CachedGroupMembers WHERE ". - # Only find superuser or rights with the name $right - "(ACL.RightName = 'SuperUser' OR ACL.RightName = '$right') ". - # Never find disabled groups. - "AND Principals.Disabled = 0 " . - "AND CachedGroupMembers.Disabled = 0 ". - "AND Principals.id = Groups.id " . # We always grant rights to Groups - - # See if the principal is a member of the group recursively or _is the rightholder_ - # never find recursively disabled group members - # also, check to see if the right is being granted _directly_ to this principal, - # as is the case when we want to look up group rights - "AND Principals.id = CachedGroupMembers.GroupId AND CachedGroupMembers.MemberId = '" . $self->Id . "' ". - - # Make sure the rights apply to the entire system or to the object in question - "AND ( ".join(' OR ', @look_at_objects).") "; + my ($hitcount, $via_obj) = $self->_HasRight( %args ); + $_ACL_CACHE->set( $full_hashkey => $hitcount? 1: -1 ); + $_ACL_CACHE->set( "$self_id;:;$args{'Right'};:;$via_obj" => 1 ) + if $via_obj && $hitcount; + return ($hitcount); +} - # The groups query does the query based on group membership and individual user rights +=head2 _HasRight - my $groups_query = $query_base . +Low level HasRight implementation, use HasRight method instead. - # limit the result set to groups of types ACLEquivalence (user) UserDefined, SystemInternal and Personal - "AND ( ( ACL.PrincipalId = Principals.id AND ACL.PrincipalType = 'Group' AND ". - "(Groups.Domain = 'SystemInternal' OR Groups.Domain = 'UserDefined' OR Groups.Domain = 'ACLEquivalence' OR Groups.Domain = 'Personal'))". +=cut - " ) "; - $self->_Handle->ApplyLimits(\$groups_query, 1); #only return one result - - my @roles; - foreach my $object (@{$args{'EquivObjects'}}) { - push (@roles, $self->_RolesForObject(ref($object), $object->id)); +sub _HasRight +{ + my $self = shift; + { + my ($hit, @other) = $self->_HasGroupRight( @_ ); + return ($hit, @other) if $hit; } + { + my ($hit, @other) = $self->_HasRoleRight( @_ ); + return ($hit, @other) if $hit; + } + return (0); +} - # The roles query does the query based on roles - my $roles_query; - if (@roles) { - $roles_query = $query_base . "AND ". - " ( (".join (' OR ', @roles)." ) ". - " AND Groups.Type = ACL.PrincipalType AND Groups.Id = Principals.id AND Principals.PrincipalType = 'Group') "; - $self->_Handle->ApplyLimits(\$roles_query, 1); #only return one result +# this method handles role rights partly in situations +# where user plays role X on an object and as well the right is +# assigned to this role X of the object, for example right CommentOnTicket +# is granted to Cc role of a queue and user is in cc list of the queue +sub _HasGroupRight +{ + my $self = shift; + my %args = ( + Right => undef, + EquivObjects => [], + @_ + ); + my $right = $args{'Right'}; - } + my $query = + "SELECT ACL.id, ACL.ObjectType, ACL.ObjectId " . + "FROM ACL, Principals, CachedGroupMembers WHERE " . + + # Only find superuser or rights with the name $right + "(ACL.RightName = 'SuperUser' OR ACL.RightName = '$right') " + + # Never find disabled groups. + . "AND Principals.id = ACL.PrincipalId " + . "AND Principals.PrincipalType = 'Group' " + . "AND Principals.Disabled = 0 " + + # See if the principal is a member of the group recursively or _is the rightholder_ + # never find recursively disabled group members + # also, check to see if the right is being granted _directly_ to this principal, + # as is the case when we want to look up group rights + . "AND CachedGroupMembers.GroupId = ACL.PrincipalId " + . "AND CachedGroupMembers.GroupId = Principals.id " + . "AND CachedGroupMembers.MemberId = ". $self->Id ." " + . "AND CachedGroupMembers.Disabled = 0 "; + + my @clauses; + foreach my $obj ( @{ $args{'EquivObjects'} } ) { + my $type = ref( $obj ) || $obj; + my $clause = "ACL.ObjectType = '$type'"; + + if ( ref($obj) && UNIVERSAL::can($obj, 'id') && $obj->id ) { + $clause .= " AND ACL.ObjectId = ". $obj->id; + } + + push @clauses, "($clause)"; + } + if ( @clauses ) { + $query .= " AND (". join( ' OR ', @clauses ) .")"; + } + $self->_Handle->ApplyLimits( \$query, 1 ); + my ($hit, $obj, $id) = $self->_Handle->FetchResult( $query ); + return (0) unless $hit; + $obj .= "-$id" if $id; + return (1, $obj); +} - # }}} +sub _HasRoleRight +{ + my $self = shift; + my %args = ( + Right => undef, + EquivObjects => [], + @_ + ); - # {{{ Actually check the ACL by performing an SQL query - # $RT::Logger->debug("Now Trying $groups_query"); - my $hitcount = $self->_Handle->FetchResult($groups_query); + my @roles = $self->RolesWithRight( %args ); + return 0 unless @roles; - # }}} - - # {{{ if there's a match, the right is granted - if ($hitcount) { + my $right = $args{'Right'}; - # Cache a positive hit. - $self->_ACLCache->{"$hashkey"}{'set'} = time; - $self->_ACLCache->{"$hashkey"}{'val'} = 1; - return (1); + my $query = + "SELECT Groups.id " + . "FROM Groups, Principals, CachedGroupMembers WHERE " + + # Never find disabled things + . "Principals.Disabled = 0 " + . "AND CachedGroupMembers.Disabled = 0 " + + # We always grant rights to Groups + . "AND Principals.id = Groups.id " + . "AND Principals.PrincipalType = 'Group' " + + # See if the principal is a member of the group recursively or _is the rightholder_ + # never find recursively disabled group members + # also, check to see if the right is being granted _directly_ to this principal, + # as is the case when we want to look up group rights + . "AND Principals.id = CachedGroupMembers.GroupId " + . "AND CachedGroupMembers.MemberId = ". $self->Id ." " + + . "AND (". join(' OR ', map "Groups.Type = '$_'", @roles ) .")" + ; + + my (@object_clauses); + foreach my $obj ( @{ $args{'EquivObjects'} } ) { + my $type = ref($obj)? ref($obj): $obj; + my $id; + $id = $obj->id if ref($obj) && UNIVERSAL::can($obj, 'id') && $obj->id; + + my $clause = "Groups.Domain = '$type-Role'"; + # XXX: Groups.Instance is VARCHAR in DB, we should quote value + # if we want mysql 4.0 use indexes here. we MUST convert that + # field to integer and drop this quotes. + $clause .= " AND Groups.Instance = '$id'" if $id; + push @object_clauses, "($clause)"; } - # }}} - # {{{ If there's no match on groups, try it on roles - else { - - $hitcount = $self->_Handle->FetchResult($roles_query); + $query .= " AND (". join( ' OR ', @object_clauses ) .")"; - if ($hitcount) { + $self->_Handle->ApplyLimits( \$query, 1 ); + my ($hit) = $self->_Handle->FetchResult( $query ); + return (1) if $hit; - # Cache a positive hit. - $self->_ACLCache->{"$hashkey"}{'set'} = time; - $self->_ACLCache->{"$hashkey"}{'val'} = 1; - return (1); - } - - else { - # cache a negative hit - $self->_ACLCache->{"$hashkey"}{'set'} = time; - $self->_ACLCache->{"$hashkey"}{'val'} = -1; - - return (undef); - } - } - # }}} + return 0; } -# }}} - -# {{{ _RolesForObject - +=head2 RolesWithRight +Returns list with names of roles that have right on +set of objects. Takes Right, EquiveObjects, +IncludeSystemRights and IncludeSuperusers arguments. -=head2 _RolesForObject( $object_type, $object_id) +IncludeSystemRights is true by default, rights +granted on system level are not accouned when option +is set to false value. -Returns an SQL clause finding role groups for Objects +IncludeSuperusers is true by default, SuperUser right +is not checked if it's set to false value. =cut - -sub _RolesForObject { +sub RolesWithRight { my $self = shift; - my $type = shift; - my $id = shift; - - unless ($id) { - $id = '0'; - } + my %args = ( + Right => undef, + IncludeSystemRights => 1, + IncludeSuperusers => 1, + EquivObjects => [], + @_ + ); + my $right = $args{'Right'}; - # This should never be true. - unless ($id =~ /^\d+$/) { - $RT::Logger->crit("RT::Prinicipal::_RolesForObject called with type $type and a non-integer id: '$id'"); - $id = "'$id'"; - } + my $query = + "SELECT DISTINCT PrincipalType FROM ACL" + # Only find superuser or rights with the name $right + ." WHERE ( RightName = '$right' " + # Check SuperUser if we were asked to + . ($args{'IncludeSuperusers'}? "OR RightName = 'SuperUser' " : '' ) + .")" + # we need only roles + ." AND PrincipalType != 'Group'" + ; + + # skip rights granted on system level if we were asked to + unless ( $args{'IncludeSystemRights'} ) { + $query .= " AND ObjectType != 'RT::System'"; + } - my $clause = "(Groups.Domain = '".$type."-Role' AND Groups.Instance = $id) "; + my (@object_clauses); + foreach my $obj ( @{ $args{'EquivObjects'} } ) { + my $type = ref($obj)? ref($obj): $obj; + my $id; + $id = $obj->id if ref($obj) && UNIVERSAL::can($obj, 'id') && $obj->id; - return($clause); + my $object_clause = "ObjectType = '$type'"; + $object_clause .= " AND ObjectId = $id" if $id; + push @object_clauses, "($object_clause)"; + } + # find ACLs that are related to our objects only + $query .= " AND (". join( ' OR ', @object_clauses ) .")" + if @object_clauses; + + my $dbh = $RT::Handle->dbh; + my $roles = $dbh->selectcol_arrayref($query); + unless ( $roles ) { + $RT::Logger->warning( $dbh->errstr ); + return (); + } + return @$roles; } # }}} @@ -491,34 +577,20 @@ sub _RolesForObject { # {{{ ACL caching -# {{{ _ACLCache - -=head2 _ACLCache -# Function: _ACLCache -# Type : private instance -# Args : none -# Lvalue : hash: ACLCache -# Desc : Returns a reference to the Key cache hash +# {{{ InvalidateACLCache -=cut - -sub _ACLCache { - return(\%_ACL_KEY_CACHE); -} - -# }}} - -# {{{ _InvalidateACLCache +=head2 InvalidateACLCache -=head2 _InvalidateACLCache - -Cleans out and reinitializes the user rights key cache +Cleans out and reinitializes the user rights cache =cut -sub _InvalidateACLCache { - %_ACL_KEY_CACHE = (); +sub InvalidateACLCache { + $_ACL_CACHE = Cache::Simple::TimedExpiry->new(); + my $lifetime; + $lifetime = $RT::Config->Get('ACLCacheLifetime') if $RT::Config; + $_ACL_CACHE->expire_after( $lifetime || 60 ); } # }}} @@ -567,6 +639,8 @@ sub _ReferenceId { # just return the value for non-objects return $scalar unless UNIVERSAL::can($scalar, 'id'); + return ref($scalar) unless $scalar->id; + # an object -- return the class and id return(ref($scalar)."-". $scalar->id); }