1 # BEGIN BPS TAGGED BLOCK {{{
5 # This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
6 # <sales@bestpractical.com>
8 # (Except where explicitly superseded by other copyright notices)
13 # This work is made available to you under the terms of Version 2 of
14 # the GNU General Public License. A copy of that license should have
15 # been provided with this software, but in any event can be snarfed
18 # This work is distributed in the hope that it will be useful, but
19 # WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 # General Public License for more details.
23 # You should have received a copy of the GNU General Public License
24 # along with this program; if not, write to the Free Software
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
26 # 02110-1301 or visit their web page on the internet at
27 # http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
30 # CONTRIBUTION SUBMISSION POLICY:
32 # (The following paragraph is not intended to limit the rights granted
33 # to you to modify and distribute this software under the terms of
34 # the GNU General Public License and is only of importance to you if
35 # you choose to contribute your changes and enhancements to the
36 # community by submitting them to Best Practical Solutions, LLC.)
38 # By intentionally submitting any modifications, corrections or
39 # derivatives to this work, or any other work intended for use with
40 # Request Tracker, to Best Practical Solutions, LLC, you confirm that
41 # you are the copyright holder for those contributions and you grant
42 # Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
43 # royalty-free, perpetual, license to use, copy, create derivative
44 # works based on those contributions, and sublicense and distribute
45 # those contributions and any derivatives thereof.
47 # END BPS TAGGED BLOCK }}}
51 RT::Users - Collection of RT::User objects
74 use base 'RT::SearchBuilder';
81 $self->{'with_disabled_column'} = 1;
83 my @result = $self->SUPER::_Init(@_);
84 # By default, order by name
85 $self->OrderBy( ALIAS => 'main',
89 $self->{'princalias'} = $self->NewAlias('Principals');
91 # XXX: should be generalized
92 $self->Join( ALIAS1 => 'main',
94 ALIAS2 => $self->{'princalias'},
96 $self->Limit( ALIAS => $self->{'princalias'},
97 FIELD => 'PrincipalType',
105 =head2 PrincipalsAlias
107 Returns the string that represents this Users object's primary "Principals" alias.
111 # XXX: should be generalized
112 sub PrincipalsAlias {
114 return($self->{'princalias'});
119 =head2 LimitToEnabled
121 Only find items that haven't been disabled
125 # XXX: should be generalized
129 $self->{'handled_disabled_column'} = 1;
131 ALIAS => $self->PrincipalsAlias,
137 =head2 LimitToDeleted
139 Only find items that have been deleted.
146 $self->{'handled_disabled_column'} = $self->{'find_disabled_rows'} = 1;
148 ALIAS => $self->PrincipalsAlias,
158 Takes one argument. an email address. limits the returned set to
166 $self->Limit( FIELD => 'EmailAddress', VALUE => "$addr" );
171 =head2 MemberOfGroup PRINCIPAL_ID
173 takes one argument, a group's principal id. Limits the returned set
174 to members of a given group
182 return $self->loc("No group specified") if ( !defined $group );
184 my $groupalias = $self->NewAlias('CachedGroupMembers');
186 # Join the principal to the groups table
187 $self->Join( ALIAS1 => $self->PrincipalsAlias,
189 ALIAS2 => $groupalias,
190 FIELD2 => 'MemberId' );
191 $self->Limit( ALIAS => $groupalias,
195 $self->Limit( ALIAS => "$groupalias",
203 =head2 LimitToPrivileged
205 Limits to users who can be made members of ACLs and groups
209 sub LimitToPrivileged {
211 $self->MemberOfGroup( RT->PrivilegedUsers->id );
214 =head2 LimitToUnprivileged
216 Limits to unprivileged users only
220 sub LimitToUnprivileged {
222 $self->MemberOfGroup( RT->UnprivilegedUsers->id);
229 $args{'CASESENSITIVE'} = 0 unless exists $args{'CASESENSITIVE'};
230 return $self->SUPER::Limit( %args );
233 =head2 WhoHaveRight { Right => 'name', Object => $rt_object , IncludeSuperusers => undef, IncludeSubgroupMembers => undef, IncludeSystemRights => undef, EquivObjects => [ ] }
236 find all users who the right Right for this group, either individually
237 or as members of groups
239 If passed a queue object, with no id, it will find users who have that right for _any_ queue
243 # XXX: should be generalized
244 sub _JoinGroupMembers
248 IncludeSubgroupMembers => 1,
252 my $principals = $self->PrincipalsAlias;
254 # The cachedgroupmembers table is used for unrolling group memberships
255 # to allow fast lookups. if we bind to CachedGroupMembers, we'll find
256 # all members of groups recursively. if we don't we'll find only 'direct'
257 # members of the group in question
259 if ( $args{'IncludeSubgroupMembers'} ) {
260 $group_members = $self->NewAlias('CachedGroupMembers');
263 $group_members = $self->NewAlias('GroupMembers');
267 ALIAS1 => $group_members,
268 FIELD1 => 'MemberId',
269 ALIAS2 => $principals,
273 ALIAS => $group_members,
276 ) if $args{'IncludeSubgroupMembers'};
278 return $group_members;
281 # XXX: should be generalized
287 my $group_members = $self->_JoinGroupMembers( %args );
288 my $groups = $self->NewAlias('Groups');
292 ALIAS2 => $group_members,
299 # XXX: should be generalized
305 IncludeSuperusers => undef,
309 if ( $args{'Right'} ) {
310 my $canonic = RT::ACE->CanonicalizeRightName( $args{'Right'} );
311 unless ( $canonic ) {
312 $RT::Logger->error("Invalid right. Couldn't canonicalize right '$args{'Right'}'");
315 $args{'Right'} = $canonic;
319 my $acl = $self->NewAlias('ACL');
322 FIELD => 'RightName',
323 OPERATOR => ( $args{Right} ? '=' : 'IS NOT' ),
324 VALUE => $args{Right} || 'NULL',
325 ENTRYAGGREGATOR => 'OR'
327 if ( $args{'IncludeSuperusers'} and $args{'Right'} ) {
330 FIELD => 'RightName',
332 VALUE => 'SuperUser',
333 ENTRYAGGREGATOR => 'OR'
339 # XXX: should be generalized
345 IncludeSystemRights => undef,
349 return () unless $args{'Object'};
351 my @objects = ($args{'Object'});
352 if ( UNIVERSAL::isa( $args{'Object'}, 'RT::Ticket' ) ) {
353 # If we're looking at ticket rights, we also want to look at the associated queue rights.
354 # this is a little bit hacky, but basically, now that we've done the ticket roles magic,
355 # we load the queue object and ask all the rest of our questions about the queue.
357 # XXX: This should be abstracted into object itself
358 if( $args{'Object'}->id ) {
359 push @objects, $args{'Object'}->ACLEquivalenceObjects;
361 push @objects, 'RT::Queue';
365 if( $args{'IncludeSystemRights'} ) {
366 push @objects, 'RT::System';
368 push @objects, @{ $args{'EquivObjects'} };
369 return grep $_, @objects;
372 # XXX: should be generalized
378 IncludeSystemRights => undef,
379 IncludeSuperusers => undef,
380 IncludeSubgroupMembers => 1,
385 if ( defined $args{'ObjectType'} || defined $args{'ObjectId'} ) {
386 $RT::Logger->crit( "WhoHaveRight called with the Obsolete ObjectId/ObjectType API");
390 my $from_role = $self->Clone;
391 $from_role->WhoHaveRoleRight( %args );
393 my $from_group = $self->Clone;
394 $from_group->WhoHaveGroupRight( %args );
397 use DBIx::SearchBuilder 1.50; #no version on ::Union :(
398 use DBIx::SearchBuilder::Union;
399 my $union = DBIx::SearchBuilder::Union->new();
400 $union->add( $from_group );
401 $union->add( $from_role );
403 bless $self, ref($union);
408 # XXX: should be generalized
415 IncludeSystemRights => undef,
416 IncludeSuperusers => undef,
417 IncludeSubgroupMembers => 1,
422 my @objects = $self->_GetEquivObjects( %args );
424 # RT::Principal->RolesWithRight only expects EquivObjects, so we need to
425 # fill it. At the very least it needs $args{Object}, which
426 # _GetEquivObjects above does for us.
427 unshift @{$args{'EquivObjects'}}, @objects;
429 my @roles = RT::Principal->RolesWithRight( %args );
431 $self->_AddSubClause( "WhichRole", "(main.id = 0)" );
435 my $groups = $self->_JoinGroups( %args );
438 $self->Limit( ALIAS => $self->PrincipalsAlias,
441 VALUE => RT->SystemUser->id
444 $self->_AddSubClause( "WhichRole", "(". join( ' OR ', map "$groups.Type = '$_'", @roles ) .")" );
446 my @groups_clauses = $self->_RoleClauses( $groups, @objects );
447 $self->_AddSubClause( "WhichObject", "(". join( ' OR ', @groups_clauses ) .")" )
459 foreach my $obj ( @objects ) {
460 my $type = ref($obj)? ref($obj): $obj;
462 $id = $obj->id if ref($obj) && UNIVERSAL::can($obj, 'id') && $obj->id;
464 my $role_clause = "$groups.Domain = '$type-Role'";
465 # XXX: Groups.Instance is VARCHAR in DB, we should quote value
466 # if we want mysql 4.0 use indexes here. we MUST convert that
467 # field to integer and drop this quotes.
468 $role_clause .= " AND $groups.Instance = '$id'" if $id;
469 push @groups_clauses, "($role_clause)";
471 return @groups_clauses;
474 # XXX: should be generalized
475 sub _JoinGroupMembersForGroupRights
479 my $group_members = $self->_JoinGroupMembers( %args );
480 $self->Limit( ALIAS => $args{'ACLAlias'},
481 FIELD => 'PrincipalId',
482 VALUE => "$group_members.GroupId",
485 return $group_members;
488 # XXX: should be generalized
489 sub WhoHaveGroupRight
495 IncludeSystemRights => undef,
496 IncludeSuperusers => undef,
497 IncludeSubgroupMembers => 1,
502 # Find only rows where the right granted is
503 # the one we're looking up or _possibly_ superuser
504 my $acl = $self->_JoinACL( %args );
506 my ($check_objects) = ('');
507 my @objects = $self->_GetEquivObjects( %args );
511 foreach my $obj ( @objects ) {
512 my $type = ref($obj)? ref($obj): $obj;
514 $id = $obj->id if ref($obj) && UNIVERSAL::can($obj, 'id') && $obj->id;
516 my $object_clause = "$acl.ObjectType = '$type'";
517 $object_clause .= " AND $acl.ObjectId = $id" if $id;
518 push @object_clauses, "($object_clause)";
521 $check_objects = join ' OR ', @object_clauses;
523 if( !$args{'IncludeSystemRights'} ) {
524 $check_objects = "($acl.ObjectType != 'RT::System')";
527 $self->_AddSubClause( "WhichObject", "($check_objects)" );
529 my $group_members = $self->_JoinGroupMembersForGroupRights( %args, ACLAlias => $acl );
530 # Find only members of groups that have the right.
531 $self->Limit( ALIAS => $acl,
532 FIELD => 'PrincipalType',
537 $self->Limit( ALIAS => $self->PrincipalsAlias,
540 VALUE => RT->SystemUser->id
542 return $group_members;
546 =head2 WhoBelongToGroups { Groups => ARRAYREF, IncludeSubgroupMembers => 1, IncludeUnprivileged => 0 }
548 Return members who belong to any of the groups passed in the groups whose IDs
549 are included in the Groups arrayref.
551 If IncludeSubgroupMembers is true (default) then members of any group that's a
552 member of one of the passed groups are returned. If it's cleared then only
553 direct member users are returned.
555 If IncludeUnprivileged is false (default) then only privileged members are
556 returned; otherwise either privileged or unprivileged group members may be
561 sub WhoBelongToGroups {
563 my %args = ( Groups => undef,
564 IncludeSubgroupMembers => 1,
565 IncludeUnprivileged => 0,
568 if (!$args{'IncludeUnprivileged'}) {
569 $self->LimitToPrivileged();
571 my $group_members = $self->_JoinGroupMembers( %args );
573 foreach my $groupid (@{$args{'Groups'}}) {
574 $self->Limit( ALIAS => $group_members,
578 ENTRYAGGREGATOR => 'OR',
586 Returns an empty new RT::User item
592 return(RT::User->new($self->CurrentUser));
594 RT::Base->_ImportOverlays();