import rt 3.8.10
[freeside.git] / rt / lib / RT / Principal_Overlay.pm
index e0646a7..88e721e 100644 (file)
@@ -1,40 +1,40 @@
 # BEGIN BPS TAGGED BLOCK {{{
-# 
+#
 # COPYRIGHT:
-# 
-# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
-#                                          <jesse@bestpractical.com>
-# 
+#
+# This software is Copyright (c) 1996-2011 Best Practical Solutions, LLC
+#                                          <sales@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., 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,7 +43,7 @@
 # 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 }}}
 
 #
@@ -216,9 +216,9 @@ sub RevokeRight {
 
 # }}}
 
-# {{{ sub _CleanupInvalidDelegations
+# {{{ sub CleanupInvalidDelegations
 
-=head2 sub _CleanupInvalidDelegations { InsideTransaction => undef }
+=head2 sub CleanupInvalidDelegations { InsideTransaction => undef }
 
 Revokes all ACE entries delegated by this principal which are
 inconsistent with this principal's current delegation rights.  Does
@@ -240,15 +240,19 @@ and logs an internal error if the deletion fails (should not happen).
 # This is currently just a stub for the methods of the same name in
 # RT::User and RT::Group.
 
-sub _CleanupInvalidDelegations {
+# 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(@_));
+    return ($self->Object->CleanupInvalidDelegations(@_));
 }
 
+
 # }}}
 
 # {{{ sub HasRight
@@ -448,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
@@ -471,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'} } ) {
@@ -479,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;
 }
 
 # }}}