diff options
Diffstat (limited to 'rt/lib/RT/Principal_Overlay.pm')
-rw-r--r-- | rt/lib/RT/Principal_Overlay.pm | 587 |
1 files changed, 0 insertions, 587 deletions
diff --git a/rt/lib/RT/Principal_Overlay.pm b/rt/lib/RT/Principal_Overlay.pm deleted file mode 100644 index 4783c5c..0000000 --- a/rt/lib/RT/Principal_Overlay.pm +++ /dev/null @@ -1,587 +0,0 @@ -# BEGIN BPS TAGGED BLOCK {{{ -# -# COPYRIGHT: -# -# This software is Copyright (c) 1996-2005 Best Practical Solutions, LLC -# <jesse@bestpractical.com> -# -# (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., 675 Mass Ave, Cambridge, MA 02139, USA. -# -# -# 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; - -no warnings qw(redefine); - -use Cache::Simple::TimedExpiry; - - - -use RT::Group; -use RT::User; - -# Set up the ACL cache on startup -our $_ACL_CACHE; -InvalidateACLCache(); - -# {{{ IsGroup - -=head2 IsGroup - -Returns true if this principal is a group. -Returns undef, otherwise - -=cut - -sub IsGroup { - my $self = shift; - if ($self->PrincipalType eq 'Group') { - return(1); - } - else { - return undef; - } -} - -# }}} - -# {{{ IsUser - -=head2 IsUser - -Returns true if this principal is a User. -Returns undef, otherwise - -=cut - -sub IsUser { - my $self = shift; - if ($self->PrincipalType eq 'User') { - return(1); - } - else { - return undef; - } -} - -# }}} - -# {{{ Object - -=head2 Object - -Returns the user or group associated with this principal - -=cut - -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()); - } - return ($self->{'object'}); - - -} -# }}} - -# {{{ ACL Related routines - -# {{{ GrantRight - -=head2 GrantRight { Right => RIGHTNAME, Object => undef } - -A helper function which calls RT::ACE->Create - - - - Returns a tuple of (STATUS, MESSAGE); If the call succeeded, STATUS is true. Otherwise it's - false. - -=cut - -sub GrantRight { - my $self = shift; - my %args = ( Right => undef, - Object => undef, - @_); - - - unless ($args{'Right'}) { - return(0, $self->loc("Invalid Right")); - } - - - #ACL check handled in ACE.pm - my $ace = RT::ACE->new( $self->CurrentUser ); - - - my $type = $self->_GetPrincipalTypeForACL(); - - # 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 - ) ); -} -# }}} - -# {{{ RevokeRight - -=head2 RevokeRight { Right => "RightName", Object => "object" } - -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, - 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; - } - #ACL check handled in ACE.pm - my $type = $self->_GetPrincipalTypeForACL(); - - my $ace = RT::ACE->new( $self->CurrentUser ); - $ace->LoadByValues( - RightName => $args{'Right'}, - Object => $args{'Object'}, - PrincipalType => $type, - PrincipalId => $self->Id - ); - - unless ( $ace->Id ) { - return ( 0, $self->loc("ACE not found") ); - } - 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. - -sub _CleanupInvalidDelegations { - my $self = shift; - unless ( $self->Id ) { - $RT::Logger->warning("Principal not loaded."); - return (undef); - } - return ($self->Object->_CleanupInvalidDelegations(@_)); -} - -# }}} - -# {{{ sub HasRight - -=head2 sub HasRight (Right => 'right' Object => undef) - - -Checks to see whether this principal has the right "Right" for the Object -specified. If the Object parameter is omitted, checks to see whether the -user has the right globally. - -This still hard codes to check to see if a user has queue-level rights -if we ask about a specific ticket. - - -This takes the params: - - Right => name of a right - - And either: - - 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. - -=cut - -sub HasRight { - - my $self = shift; - my %args = ( - Right => undef, - Object => undef, - EquivObjects => undef, - @_ - ); - - if ( $self->Disabled ) { - $RT::Logger->err( "Disabled User: " - . $self->id - . " failed access check for " - . $args{'Right'} ); - return (undef); - } - - if ( !defined $args{'Right'} ) { - $RT::Logger->crit("HasRight called without a right"); - return (undef); - } - - 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"); - 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 ); - - } - - # {{{ 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 - ); - }; - - # }}} - - # {{{ if we've cached a positive result for this query, return 1 - - my $cached_answer = $_ACL_CACHE->fetch($hashkey); - - # Returns undef on cache miss - if ( defined $cached_answer ) { - if ( $cached_answer == 1 ) { - return (1); - } - elsif ( $cached_answer == -1 ) { - return (0); - } - } - - 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 ) . ") "; - -# The groups query does the query based on group membership and individual user rights - - my $groups_query = $query_base . - -# 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'))" - . - - " ) "; - $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 ) ); - } - - # 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 - - } - - # }}} - - # {{{ Actually check the ACL by performing an SQL query - # $RT::Logger->debug("Now Trying $groups_query"); - my $hitcount = $self->_Handle->FetchResult($groups_query); - - # }}} - - # {{{ if there's a match, the right is granted - if ($hitcount) { - $_ACL_CACHE->set( $hashkey => 1 ); - return (1); - } - - # Now check the roles query - $hitcount = $self->_Handle->FetchResult($roles_query); - - if ($hitcount) { - $_ACL_CACHE->set( $hashkey => 1 ); - return (1); - } - - # We failed to find an acl hit - $_ACL_CACHE->set( $hashkey => -1 ); - return (undef); -} - -# }}} - -# {{{ _RolesForObject - - - -=head2 _RolesForObject( $object_type, $object_id) - -Returns an SQL clause finding role groups for Objects - -=cut - - -sub _RolesForObject { - my $self = shift; - my $type = shift; - my $id = shift; - - unless ($id) { - $id = '0'; - } - - # 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 $clause = "(Groups.Domain = '".$type."-Role' AND Groups.Instance = $id) "; - - return($clause); -} - -# }}} - -# }}} - -# {{{ ACL caching - - -# {{{ InvalidateACLCache - -=head2 InvalidateACLCache - -Cleans out and reinitializes the user rights cache - -=cut - -sub InvalidateACLCache { - $_ACL_CACHE = Cache::Simple::TimedExpiry->new(); - $_ACL_CACHE->expire_after($RT::ACLCacheLifetime||60); - -} - -# }}} - -# }}} - - -# {{{ _GetPrincipalTypeForACL - -=head2 _GetPrincipalTypeForACL - -Gets the principal type. if it's a user, it's a user. if it's a role group and it has a Type, -return that. if it has no type, return group. - -=cut - -sub _GetPrincipalTypeForACL { - my $self = shift; - my $type; - if ($self->PrincipalType eq 'Group' && $self->Object->Domain =~ /Role$/) { - $type = $self->Object->Type; - } - else { - $type = $self->PrincipalType; - } - - return($type); -} - -# }}} - -# {{{ _ReferenceId - -=head2 _ReferenceId - -Returns a list uniquely representing an object or normal scalar. - -For scalars, its string value is returned; for objects that has an -id() method, its class name and Id are returned as a string separated by a "-". - -=cut - -sub _ReferenceId { - my $scalar = shift; - - # just return the value for non-objects - return $scalar unless UNIVERSAL::can($scalar, 'id'); - - # an object -- return the class and id - return(ref($scalar)."-". $scalar->id); -} - -# }}} - -1; |