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
72 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 # XXX: should be generalized
90 $self->{'princalias'} = $self->Join(
93 TABLE2 => 'Principals',
95 $self->Limit( ALIAS => $self->{'princalias'},
96 FIELD => 'PrincipalType',
104 =head2 PrincipalsAlias
106 Returns the string that represents this Users object's primary "Principals" alias.
110 # XXX: should be generalized
111 sub PrincipalsAlias {
113 return($self->{'princalias'});
118 =head2 LimitToEnabled
120 Only find items that haven't been disabled
124 # XXX: should be generalized
128 $self->{'handled_disabled_column'} = 1;
130 ALIAS => $self->PrincipalsAlias,
136 =head2 LimitToDeleted
138 Only find items that have been deleted.
145 $self->{'handled_disabled_column'} = $self->{'find_disabled_rows'} = 1;
147 ALIAS => $self->PrincipalsAlias,
157 Takes one argument. an email address. limits the returned set to
165 $self->Limit( FIELD => 'EmailAddress', VALUE => $addr, CASESENSITIVE => 0 );
170 =head2 MemberOfGroup PRINCIPAL_ID
172 takes one argument, a group's principal id. Limits the returned set
173 to members of a given group
181 return $self->loc("No group specified") if ( !defined $group );
183 my $groupalias = $self->NewAlias('CachedGroupMembers');
185 # Join the principal to the groups table
186 $self->Join( ALIAS1 => $self->PrincipalsAlias,
188 ALIAS2 => $groupalias,
189 FIELD2 => 'MemberId' );
190 $self->Limit( ALIAS => $groupalias,
194 $self->Limit( ALIAS => "$groupalias",
202 =head2 LimitToPrivileged
204 Limits to users who can be made members of ACLs and groups
208 sub LimitToPrivileged {
210 $self->MemberOfGroup( RT->PrivilegedUsers->id );
213 =head2 LimitToUnprivileged
215 Limits to unprivileged users only
219 sub LimitToUnprivileged {
221 $self->MemberOfGroup( RT->UnprivilegedUsers->id);
228 $args{'CASESENSITIVE'} = 0 unless exists $args{'CASESENSITIVE'} or $args{'ALIAS'};
229 return $self->SUPER::Limit( %args );
232 =head2 WhoHaveRight { Right => 'name', Object => $rt_object , IncludeSuperusers => undef, IncludeSubgroupMembers => undef, IncludeSystemRights => undef, EquivObjects => [ ] }
235 find all users who the right Right for this group, either individually
236 or as members of groups
238 If passed a queue object, with no id, it will find users who have that right for _any_ queue
242 # XXX: should be generalized
243 sub _JoinGroupMembers
247 IncludeSubgroupMembers => 1,
251 my $principals = $self->PrincipalsAlias;
253 # The cachedgroupmembers table is used for unrolling group memberships
254 # to allow fast lookups. if we bind to CachedGroupMembers, we'll find
255 # all members of groups recursively. if we don't we'll find only 'direct'
256 # members of the group in question
258 if ( $args{'IncludeSubgroupMembers'} ) {
259 $group_members = $self->NewAlias('CachedGroupMembers');
262 $group_members = $self->NewAlias('GroupMembers');
266 ALIAS1 => $group_members,
267 FIELD1 => 'MemberId',
268 ALIAS2 => $principals,
272 ALIAS => $group_members,
275 ) if $args{'IncludeSubgroupMembers'};
277 return $group_members;
280 # XXX: should be generalized
286 my $group_members = $self->_JoinGroupMembers( %args );
287 my $groups = $self->NewAlias('Groups');
291 ALIAS2 => $group_members,
298 # XXX: should be generalized
304 IncludeSuperusers => undef,
308 if ( $args{'Right'} ) {
309 my $canonic = RT::ACE->CanonicalizeRightName( $args{'Right'} );
310 unless ( $canonic ) {
311 $RT::Logger->error("Invalid right. Couldn't canonicalize right '$args{'Right'}'");
314 $args{'Right'} = $canonic;
318 my $acl = $self->NewAlias('ACL');
321 FIELD => 'RightName',
322 OPERATOR => ( $args{Right} ? '=' : 'IS NOT' ),
323 VALUE => $args{Right} || 'NULL',
324 ENTRYAGGREGATOR => 'OR'
326 if ( $args{'IncludeSuperusers'} and $args{'Right'} ) {
329 FIELD => 'RightName',
331 VALUE => 'SuperUser',
332 ENTRYAGGREGATOR => 'OR'
338 # XXX: should be generalized
344 IncludeSystemRights => undef,
348 return () unless $args{'Object'};
350 my @objects = ($args{'Object'});
351 if ( UNIVERSAL::isa( $args{'Object'}, 'RT::Ticket' ) ) {
352 # If we're looking at ticket rights, we also want to look at the associated queue rights.
353 # this is a little bit hacky, but basically, now that we've done the ticket roles magic,
354 # we load the queue object and ask all the rest of our questions about the queue.
356 # XXX: This should be abstracted into object itself
357 if( $args{'Object'}->id ) {
358 push @objects, $args{'Object'}->ACLEquivalenceObjects;
360 push @objects, 'RT::Queue';
364 if( $args{'IncludeSystemRights'} ) {
365 push @objects, $RT::System;
367 push @objects, @{ $args{'EquivObjects'} };
368 return grep $_, @objects;
371 # XXX: should be generalized
377 IncludeSystemRights => undef,
378 IncludeSuperusers => undef,
379 IncludeSubgroupMembers => 1,
384 if ( defined $args{'ObjectType'} || defined $args{'ObjectId'} ) {
385 $RT::Logger->crit( "WhoHaveRight called with the Obsolete ObjectId/ObjectType API");
389 my $from_role = $self->Clone;
390 $from_role->WhoHaveRoleRight( %args );
392 my $from_group = $self->Clone;
393 $from_group->WhoHaveGroupRight( %args );
396 use DBIx::SearchBuilder::Union;
397 my $union = DBIx::SearchBuilder::Union->new();
398 $union->add( $from_group );
399 $union->add( $from_role );
401 bless $self, ref($union);
406 # XXX: should be generalized
413 IncludeSystemRights => undef,
414 IncludeSuperusers => undef,
415 IncludeSubgroupMembers => 1,
420 my @objects = $self->_GetEquivObjects( %args );
422 # RT::Principal->RolesWithRight only expects EquivObjects, so we need to
423 # fill it. At the very least it needs $args{Object}, which
424 # _GetEquivObjects above does for us.
425 unshift @{$args{'EquivObjects'}}, @objects;
427 my @roles = RT::Principal->RolesWithRight( %args );
429 $self->_AddSubClause( "WhichRole", "(main.id = 0)" );
433 my $groups = $self->_JoinGroups( %args );
436 $self->Limit( ALIAS => $self->PrincipalsAlias,
439 VALUE => RT->SystemUser->id
442 $self->_AddSubClause( "WhichRole", "(". join( ' OR ',
443 map $RT::Handle->__MakeClauseCaseInsensitive("$groups.Name", '=', "'$_'"), @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 my $role_clause = $RT::Handle->__MakeClauseCaseInsensitive("$groups.Domain", '=', "'$type-Role'");
464 if ( my $id = eval { $obj->id } ) {
465 $role_clause .= " AND $groups.Instance = $id";
467 push @groups_clauses, "($role_clause)";
469 return @groups_clauses;
472 # XXX: should be generalized
473 sub _JoinGroupMembersForGroupRights
477 my $group_members = $self->_JoinGroupMembers( %args );
478 $self->Limit( ALIAS => $args{'ACLAlias'},
479 FIELD => 'PrincipalId',
480 VALUE => "$group_members.GroupId",
483 return $group_members;
486 # XXX: should be generalized
487 sub WhoHaveGroupRight
493 IncludeSystemRights => undef,
494 IncludeSuperusers => undef,
495 IncludeSubgroupMembers => 1,
500 # Find only rows where the right granted is
501 # the one we're looking up or _possibly_ superuser
502 my $acl = $self->_JoinACL( %args );
504 my ($check_objects) = ('');
505 my @objects = $self->_GetEquivObjects( %args );
510 foreach my $obj ( @objects ) {
511 my $type = ref($obj)? ref($obj): $obj;
513 $id = $obj->id if ref($obj) && UNIVERSAL::can($obj, 'id') && $obj->id;
514 next if $seen{"$type-$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',
585 Does a 'simple' search of Users against a specified Term.
587 This Term is compared to a number of fields using various types of SQL
588 comparison operators.
590 Ensures that the returned collection of Users will have a value for Return.
592 This method is passed the following. You must specify a Term and a Return.
594 Privileged - Whether or not to limit to Privileged Users (0 or 1)
595 Fields - Hashref of data - defaults to C<$UserSearchFields> emulate that if you want to override
596 Term - String that is in the fields specified by Fields
597 Return - What field on the User you want to be sure isn't empty
598 Exclude - Array reference of ids to exclude
599 Max - What to limit this collection to
607 Fields => RT->Config->Get('UserSearchFields'),
615 return $self unless defined $args{Return}
616 and defined $args{Term}
617 and length $args{Term};
619 $self->RowsPerPage( $args{Max} );
621 $self->LimitToPrivileged() if $args{Privileged};
623 while (my ($name, $op) = each %{$args{Fields}}) {
625 unless $op =~ /^(?:LIKE|(?:START|END)SWITH|=|!=)$/i;
627 if ($name =~ /^CF\.(?:\{(.*)}|(.*))$/) {
628 my $cfname = $1 || $2;
629 $self->LimitCustomField(
630 CUSTOMFIELD => $cfname,
632 VALUE => $args{Term},
633 ENTRYAGGREGATOR => 'OR',
634 SUBCLAUSE => 'autocomplete',
640 VALUE => $args{Term},
641 ENTRYAGGREGATOR => 'OR',
642 SUBCLAUSE => 'autocomplete',
647 # Exclude users we don't want
648 foreach (@{$args{Exclude}}) {
649 $self->Limit(FIELD => 'id', VALUE => $_, OPERATOR => '!=', ENTRYAGGREGATOR => 'AND');
652 if ( RT->Config->Get('DatabaseType') eq 'Oracle' ) {
654 FIELD => $args{Return},
655 OPERATOR => 'IS NOT',
660 $self->Limit( FIELD => $args{Return}, OPERATOR => '!=', VALUE => '' );
662 FIELD => $args{Return},
663 OPERATOR => 'IS NOT',
665 ENTRYAGGREGATOR => 'AND'
672 RT::Base->_ImportOverlays();