X-Git-Url: http://git.freeside.biz/gitweb/?a=blobdiff_plain;f=rt%2Flib%2FRT%2FPrincipal_Overlay.pm;h=f46525269439cbff71693988cfe960d568ca53d3;hb=90edd8a914fd484e649fb0aa051dce7927bd6881;hp=c444a1bb5031316a7840380bacc9bf28bb1966a3;hpb=2dfda73eeb3eae2d4f894099754794ef07d060dd;p=freeside.git diff --git a/rt/lib/RT/Principal_Overlay.pm b/rt/lib/RT/Principal_Overlay.pm index c444a1bb5..f46525269 100644 --- a/rt/lib/RT/Principal_Overlay.pm +++ b/rt/lib/RT/Principal_Overlay.pm @@ -1,40 +1,40 @@ # BEGIN BPS TAGGED BLOCK {{{ -# +# # COPYRIGHT: -# -# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC -# -# +# +# 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. -# +# # 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 @@ -43,8 +43,9 @@ # 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; @@ -52,12 +53,10 @@ package RT::Principal; use strict; use warnings; -no warnings qw(redefine); - use Cache::Simple::TimedExpiry; - +use RT; use RT::Group; use RT::User; @@ -76,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; } # }}} @@ -118,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'}); @@ -154,29 +152,27 @@ A helper function which calls RT::ACE->Create sub GrantRight { my $self = shift; - my %args = ( Right => undef, - Object => undef, - @_); - - - unless ($args{'Right'}) { - return(0, $self->loc("Invalid Right")); - } - + 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, + ); } # }}} @@ -197,7 +193,7 @@ sub RevokeRight { my $self = shift; my %args = ( - Right => undef, + Right => undef, Object => undef, @_ ); @@ -210,17 +206,16 @@ 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; } # }}} @@ -303,11 +298,18 @@ sub HasRight { 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 ( $self->Disabled ) { - $RT::Logger->error( "Disabled User #" + $RT::Logger->debug( "Disabled User #" . $self->id . " failed access check for " . $args{'Right'} ); @@ -325,21 +327,13 @@ sub HasRight { return (undef); } - # If this object is a ticket, we care about ticket roles and queue roles - if ( UNIVERSAL::isa( $args{'Object'} => 'RT::Ticket' ) ) { - # 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. - unshift @{ $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 hashkeys to cache decisions: @@ -458,17 +452,18 @@ sub _HasRoleRight EquivObjects => [], @_ ); + + my @roles = $self->RolesWithRight( %args ); + return 0 unless @roles; + my $right = $args{'Right'}; my $query = - "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') " + "SELECT Groups.id " + . "FROM Groups, Principals, CachedGroupMembers WHERE " # Never find disabled things - . "AND Principals.Disabled = 0 " + . "Principals.Disabled = 0 " . "AND CachedGroupMembers.Disabled = 0 " # We always grant rights to Groups @@ -481,7 +476,9 @@ sub _HasRoleRight # as is the case when we want to look up group rights . "AND Principals.id = CachedGroupMembers.GroupId " . "AND CachedGroupMembers.MemberId = ". $self->Id ." " - . "AND ACL.PrincipalType = Groups.Type "; + + . "AND (". join(' OR ', map "Groups.Type = '$_'", @roles ) .")" + ; my (@object_clauses); foreach my $obj ( @{ $args{'EquivObjects'} } ) { @@ -489,33 +486,85 @@ sub _HasRoleRight my $id; $id = $obj->id if ref($obj) && UNIVERSAL::can($obj, 'id') && $obj->id; - my $object_clause = "ACL.ObjectType = '$type'"; - $object_clause .= " AND ACL.ObjectId = $id" if $id; - push @object_clauses, "($object_clause)"; + 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)"; } - # find ACLs that are related to our objects only $query .= " AND (". join( ' OR ', @object_clauses ) .")"; - # because of mysql bug in versions up to 5.0.45 we do one query per object - # each query should be faster on any DB as it uses indexes more effective + $self->_Handle->ApplyLimits( \$query, 1 ); + my ($hit) = $self->_Handle->FetchResult( $query ); + return (1) if $hit; + + return 0; +} + +=head2 RolesWithRight + +Returns list with names of roles that have right on +set of objects. Takes Right, EquiveObjects, +IncludeSystemRights and IncludeSuperusers arguments. + +IncludeSystemRights is true by default, rights +granted on system level are not accouned when option +is set to false value. + +IncludeSuperusers is true by default, SuperUser right +is not checked if it's set to false value. + +=cut + +sub RolesWithRight { + my $self = shift; + my %args = ( + Right => undef, + IncludeSystemRights => 1, + IncludeSuperusers => 1, + EquivObjects => [], + @_ + ); + my $right = $args{'Right'}; + + 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 (@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 $tmp = $query; - $tmp .= " AND 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. - $tmp .= " AND Groups.Instance = '$id'" if $id; - - $self->_Handle->ApplyLimits( \$tmp, 1 ); - my ($hit) = $self->_Handle->FetchResult( $tmp ); - return (1) if $hit; + my $object_clause = "ObjectType = '$type'"; + $object_clause .= " AND ObjectId = $id" if $id; + push @object_clauses, "($object_clause)"; } - - return 0; + # 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; } # }}} @@ -535,8 +584,9 @@ Cleans out and reinitializes the user rights cache sub InvalidateACLCache { $_ACL_CACHE = Cache::Simple::TimedExpiry->new(); - $_ACL_CACHE->expire_after($RT::ACLCacheLifetime||60); - + my $lifetime; + $lifetime = $RT::Config->Get('ACLCacheLifetime') if $RT::Config; + $_ACL_CACHE->expire_after( $lifetime || 60 ); } # }}}