1 # BEGIN BPS TAGGED BLOCK {{{
5 # This software is Copyright (c) 1996-2011 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
70 no warnings qw(redefine);
75 $self->{'table'} = 'Users';
76 $self->{'primary_key'} = 'id';
77 $self->{'with_disabled_column'} = 1;
79 my @result = $self->SUPER::_Init(@_);
80 # By default, order by name
81 $self->OrderBy( ALIAS => 'main',
85 $self->{'princalias'} = $self->NewAlias('Principals');
87 # XXX: should be generalized
88 $self->Join( ALIAS1 => 'main',
90 ALIAS2 => $self->{'princalias'},
92 $self->Limit( ALIAS => $self->{'princalias'},
93 FIELD => 'PrincipalType',
102 =head2 PrincipalsAlias
104 Returns the string that represents this Users object's primary "Principals" alias.
108 # XXX: should be generalized
109 sub PrincipalsAlias {
111 return($self->{'princalias'});
116 =head2 LimitToEnabled
118 Only find items that haven\'t been disabled
122 # XXX: should be generalized
126 $self->{'handled_disabled_column'} = 1;
128 ALIAS => $self->PrincipalsAlias,
134 =head2 LimitToDeleted
136 Only find items that have been deleted.
143 $self->{'handled_disabled_column'} = $self->{'find_disabled_rows'} = 1;
145 ALIAS => $self->PrincipalsAlias,
156 Takes one argument. an email address. limits the returned set to
164 $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 # {{{ LimitToPrivileged
205 =head2 LimitToPrivileged
207 Limits to users who can be made members of ACLs and groups
211 sub LimitToPrivileged {
214 my $priv = RT::Group->new( $self->CurrentUser );
215 $priv->LoadSystemInternalGroup('Privileged');
216 unless ( $priv->Id ) {
217 $RT::Logger->crit("Couldn't find a privileged users group");
219 $self->MemberOfGroup( $priv->PrincipalId );
226 =head2 WhoHaveRight { Right => 'name', Object => $rt_object , IncludeSuperusers => undef, IncludeSubgroupMembers => undef, IncludeSystemRights => undef, EquivObjects => [ ] }
229 find all users who the right Right for this group, either individually
230 or as members of groups
232 If passed a queue object, with no id, it will find users who have that right for _any_ queue
236 # XXX: should be generalized
237 sub _JoinGroupMembers
241 IncludeSubgroupMembers => 1,
245 my $principals = $self->PrincipalsAlias;
247 # The cachedgroupmembers table is used for unrolling group memberships
248 # to allow fast lookups. if we bind to CachedGroupMembers, we'll find
249 # all members of groups recursively. if we don't we'll find only 'direct'
250 # members of the group in question
252 if ( $args{'IncludeSubgroupMembers'} ) {
253 $group_members = $self->NewAlias('CachedGroupMembers');
256 $group_members = $self->NewAlias('GroupMembers');
260 ALIAS1 => $group_members,
261 FIELD1 => 'MemberId',
262 ALIAS2 => $principals,
266 ALIAS => $group_members,
269 ) if $args{'IncludeSubgroupMembers'};
271 return $group_members;
274 # XXX: should be generalized
280 my $group_members = $self->_JoinGroupMembers( %args );
281 my $groups = $self->NewAlias('Groups');
285 ALIAS2 => $group_members,
292 # XXX: should be generalized
298 IncludeSuperusers => undef,
302 if ( $args{'Right'} ) {
303 my $canonic = RT::ACE->CanonicalizeRightName( $args{'Right'} );
304 unless ( $canonic ) {
305 $RT::Logger->error("Invalid right. Couldn't canonicalize right '$args{'Right'}'");
308 $args{'Right'} = $canonic;
312 my $acl = $self->NewAlias('ACL');
315 FIELD => 'RightName',
316 OPERATOR => ( $args{Right} ? '=' : 'IS NOT' ),
317 VALUE => $args{Right} || 'NULL',
318 ENTRYAGGREGATOR => 'OR'
320 if ( $args{'IncludeSuperusers'} and $args{'Right'} ) {
323 FIELD => 'RightName',
325 VALUE => 'SuperUser',
326 ENTRYAGGREGATOR => 'OR'
332 # XXX: should be generalized
338 IncludeSystemRights => undef,
342 return () unless $args{'Object'};
344 my @objects = ($args{'Object'});
345 if ( UNIVERSAL::isa( $args{'Object'}, 'RT::Ticket' ) ) {
346 # If we're looking at ticket rights, we also want to look at the associated queue rights.
347 # this is a little bit hacky, but basically, now that we've done the ticket roles magic,
348 # we load the queue object and ask all the rest of our questions about the queue.
350 # XXX: This should be abstracted into object itself
351 if( $args{'Object'}->id ) {
352 push @objects, $args{'Object'}->ACLEquivalenceObjects;
354 push @objects, 'RT::Queue';
358 if( $args{'IncludeSystemRights'} ) {
359 push @objects, 'RT::System';
361 push @objects, @{ $args{'EquivObjects'} };
362 return grep $_, @objects;
365 # XXX: should be generalized
371 IncludeSystemRights => undef,
372 IncludeSuperusers => undef,
373 IncludeSubgroupMembers => 1,
378 if ( defined $args{'ObjectType'} || defined $args{'ObjectId'} ) {
379 $RT::Logger->crit( "WhoHaveRight called with the Obsolete ObjectId/ObjectType API");
383 my $from_role = $self->Clone;
384 $from_role->WhoHaveRoleRight( %args );
386 my $from_group = $self->Clone;
387 $from_group->WhoHaveGroupRight( %args );
390 use DBIx::SearchBuilder 1.50; #no version on ::Union :(
391 use DBIx::SearchBuilder::Union;
392 my $union = new DBIx::SearchBuilder::Union;
393 $union->add( $from_group );
394 $union->add( $from_role );
396 bless $self, ref($union);
402 # XXX: should be generalized
409 IncludeSystemRights => undef,
410 IncludeSuperusers => undef,
411 IncludeSubgroupMembers => 1,
416 my @objects = $self->_GetEquivObjects( %args );
418 # RT::Principal->RolesWithRight only expects EquivObjects, so we need to
419 # fill it. At the very least it needs $args{Object}, which
420 # _GetEquivObjects above does for us.
421 unshift @{$args{'EquivObjects'}}, @objects;
423 my @roles = RT::Principal->RolesWithRight( %args );
425 $self->_AddSubClause( "WhichRole", "(main.id = 0)" );
429 my $groups = $self->_JoinGroups( %args );
432 $self->Limit( ALIAS => $self->PrincipalsAlias,
435 VALUE => $RT::SystemUser->id
438 $self->_AddSubClause( "WhichRole", "(". join( ' OR ', map "$groups.Type = '$_'", @roles ) .")" );
440 my @groups_clauses = $self->_RoleClauses( $groups, @objects );
441 $self->_AddSubClause( "WhichObject", "(". join( ' OR ', @groups_clauses ) .")" )
453 foreach my $obj ( @objects ) {
454 my $type = ref($obj)? ref($obj): $obj;
456 $id = $obj->id if ref($obj) && UNIVERSAL::can($obj, 'id') && $obj->id;
458 my $role_clause = "$groups.Domain = '$type-Role'";
459 # XXX: Groups.Instance is VARCHAR in DB, we should quote value
460 # if we want mysql 4.0 use indexes here. we MUST convert that
461 # field to integer and drop this quotes.
462 $role_clause .= " AND $groups.Instance = '$id'" if $id;
463 push @groups_clauses, "($role_clause)";
465 return @groups_clauses;
468 # XXX: should be generalized
469 sub _JoinGroupMembersForGroupRights
473 my $group_members = $self->_JoinGroupMembers( %args );
474 $self->Limit( ALIAS => $args{'ACLAlias'},
475 FIELD => 'PrincipalId',
476 VALUE => "$group_members.GroupId",
481 # XXX: should be generalized
482 sub WhoHaveGroupRight
488 IncludeSystemRights => undef,
489 IncludeSuperusers => undef,
490 IncludeSubgroupMembers => 1,
495 # Find only rows where the right granted is
496 # the one we're looking up or _possibly_ superuser
497 my $acl = $self->_JoinACL( %args );
499 my ($check_objects) = ('');
500 my @objects = $self->_GetEquivObjects( %args );
504 foreach my $obj ( @objects ) {
505 my $type = ref($obj)? ref($obj): $obj;
507 $id = $obj->id if ref($obj) && UNIVERSAL::can($obj, 'id') && $obj->id;
509 my $object_clause = "$acl.ObjectType = '$type'";
510 $object_clause .= " AND $acl.ObjectId = $id" if $id;
511 push @object_clauses, "($object_clause)";
514 $check_objects = join ' OR ', @object_clauses;
516 if( !$args{'IncludeSystemRights'} ) {
517 $check_objects = "($acl.ObjectType != 'RT::System')";
520 $self->_AddSubClause( "WhichObject", "($check_objects)" );
522 $self->_JoinGroupMembersForGroupRights( %args, ACLAlias => $acl );
523 # Find only members of groups that have the right.
524 $self->Limit( ALIAS => $acl,
525 FIELD => 'PrincipalType',
530 $self->Limit( ALIAS => $self->PrincipalsAlias,
533 VALUE => $RT::SystemUser->id
538 # {{{ WhoBelongToGroups
540 =head2 WhoBelongToGroups { Groups => ARRAYREF, IncludeSubgroupMembers => 1 }
544 # XXX: should be generalized
545 sub WhoBelongToGroups {
547 my %args = ( Groups => undef,
548 IncludeSubgroupMembers => 1,
551 # Unprivileged users can't be granted real system rights.
552 # is this really the right thing to be saying?
553 $self->LimitToPrivileged();
555 my $group_members = $self->_JoinGroupMembers( %args );
557 foreach my $groupid (@{$args{'Groups'}}) {
558 $self->Limit( ALIAS => $group_members,
562 ENTRYAGGREGATOR => 'OR',