1 # BEGIN BPS TAGGED BLOCK {{{
5 # This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
6 # <jesse@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';
80 my @result = $self->SUPER::_Init(@_);
81 # By default, order by name
82 $self->OrderBy( ALIAS => 'main',
86 $self->{'princalias'} = $self->NewAlias('Principals');
88 # XXX: should be generalized
89 $self->Join( ALIAS1 => 'main',
91 ALIAS2 => $self->{'princalias'},
93 $self->Limit( ALIAS => $self->{'princalias'},
94 FIELD => 'PrincipalType',
103 =head2 PrincipalsAlias
105 Returns the string that represents this Users object's primary "Principals" alias.
109 # XXX: should be generalized
110 sub PrincipalsAlias {
112 return($self->{'princalias'});
121 A subclass of DBIx::SearchBuilder::_DoSearch that makes sure that _Disabled rows never get seen unless
122 we're explicitly trying to see them.
129 #unless we really want to find disabled rows, make sure we\'re only finding enabled ones.
130 unless ( $self->{'find_disabled_rows'} ) {
131 $self->LimitToEnabled();
133 return ( $self->SUPER::_DoSearch(@_) );
138 # {{{ sub LimitToEnabled
140 =head2 LimitToEnabled
142 Only find items that haven\'t been disabled
146 # XXX: should be generalized
150 $self->Limit( ALIAS => $self->PrincipalsAlias,
162 Takes one argument. an email address. limits the returned set to
170 $self->Limit( FIELD => 'EmailAddress', VALUE => "$addr" );
177 =head2 MemberOfGroup PRINCIPAL_ID
179 takes one argument, a group's principal id. Limits the returned set
180 to members of a given group
188 return $self->loc("No group specified") if ( !defined $group );
190 my $groupalias = $self->NewAlias('CachedGroupMembers');
192 # Join the principal to the groups table
193 $self->Join( ALIAS1 => $self->PrincipalsAlias,
195 ALIAS2 => $groupalias,
196 FIELD2 => 'MemberId' );
198 $self->Limit( ALIAS => "$groupalias",
206 # {{{ LimitToPrivileged
208 =head2 LimitToPrivileged
210 Limits to users who can be made members of ACLs and groups
214 sub LimitToPrivileged {
217 my $priv = RT::Group->new( $self->CurrentUser );
218 $priv->LoadSystemInternalGroup('Privileged');
219 unless ( $priv->Id ) {
220 $RT::Logger->crit("Couldn't find a privileged users group");
222 $self->MemberOfGroup( $priv->PrincipalId );
229 =head2 WhoHaveRight { Right => 'name', Object => $rt_object , IncludeSuperusers => undef, IncludeSubgroupMembers => undef, IncludeSystemRights => undef, EquivObjects => [ ] }
232 find all users who the right Right for this group, either individually
233 or as members of groups
235 If passed a queue object, with no id, it will find users who have that right for _any_ queue
239 # XXX: should be generalized
240 sub _JoinGroupMembers
244 IncludeSubgroupMembers => 1,
248 my $principals = $self->PrincipalsAlias;
250 # The cachedgroupmembers table is used for unrolling group memberships
251 # to allow fast lookups. if we bind to CachedGroupMembers, we'll find
252 # all members of groups recursively. if we don't we'll find only 'direct'
253 # members of the group in question
255 if ( $args{'IncludeSubgroupMembers'} ) {
256 $group_members = $self->NewAlias('CachedGroupMembers');
259 $group_members = $self->NewAlias('GroupMembers');
263 ALIAS1 => $group_members,
264 FIELD1 => 'MemberId',
265 ALIAS2 => $principals,
269 return $group_members;
272 # XXX: should be generalized
278 my $group_members = $self->_JoinGroupMembers( %args );
279 my $groups = $self->NewAlias('Groups');
283 ALIAS2 => $group_members,
290 # XXX: should be generalized
296 IncludeSuperusers => undef,
300 if ( $args{'Right'} ) {
301 my $canonic = RT::ACE->CanonicalizeRightName( $args{'Right'} );
302 unless ( $canonic ) {
303 $RT::Logger->error("Invalid right. Couldn't canonicalize right '$args{'Right'}'");
306 $args{'Right'} = $canonic;
310 my $acl = $self->NewAlias('ACL');
313 FIELD => 'RightName',
314 OPERATOR => ( $args{Right} ? '=' : 'IS NOT' ),
315 VALUE => $args{Right} || 'NULL',
316 ENTRYAGGREGATOR => 'OR'
318 if ( $args{'IncludeSuperusers'} and $args{'Right'} ) {
321 FIELD => 'RightName',
323 VALUE => 'SuperUser',
324 ENTRYAGGREGATOR => 'OR'
330 # XXX: should be generalized
336 IncludeSystemRights => undef,
340 return () unless $args{'Object'};
342 my @objects = ($args{'Object'});
343 if ( UNIVERSAL::isa( $args{'Object'}, 'RT::Ticket' ) ) {
344 # If we're looking at ticket rights, we also want to look at the associated queue rights.
345 # this is a little bit hacky, but basically, now that we've done the ticket roles magic,
346 # we load the queue object and ask all the rest of our questions about the queue.
348 # XXX: This should be abstracted into object itself
349 if( $args{'Object'}->id ) {
350 push @objects, $args{'Object'}->ACLEquivalenceObjects;
352 push @objects, 'RT::Queue';
356 if( $args{'IncludeSystemRights'} ) {
357 push @objects, 'RT::System';
359 push @objects, @{ $args{'EquivObjects'} };
360 return grep $_, @objects;
363 # XXX: should be generalized
369 IncludeSystemRights => undef,
370 IncludeSuperusers => undef,
371 IncludeSubgroupMembers => 1,
376 if ( defined $args{'ObjectType'} || defined $args{'ObjectId'} ) {
377 $RT::Logger->crit( "WhoHaveRight called with the Obsolete ObjectId/ObjectType API");
381 my @from_role = $self->Clone->_WhoHaveRoleRightSplitted( %args );
383 my $from_group = $self->Clone;
384 $from_group->WhoHaveGroupRight( %args );
387 use DBIx::SearchBuilder::Union;
388 my $union = new DBIx::SearchBuilder::Union;
389 $union->add( $_ ) foreach @from_role;
390 $union->add( $from_group );
392 bless $self, ref($union);
398 # XXX: should be generalized
405 IncludeSystemRights => undef,
406 IncludeSuperusers => undef,
407 IncludeSubgroupMembers => 1,
412 my $groups = $self->_JoinGroups( %args );
413 my $acl = $self->_JoinACL( %args );
415 $self->Limit( ALIAS => $acl,
416 FIELD => 'PrincipalType',
417 VALUE => "$groups.Type",
422 $self->Limit( ALIAS => $self->PrincipalsAlias,
425 VALUE => $RT::SystemUser->id
428 my @objects = $self->_GetEquivObjects( %args );
429 unless ( @objects ) {
430 unless ( $args{'IncludeSystemRights'} ) {
431 $self->_AddSubClause( WhichObjects => "($acl.ObjectType != 'RT::System')" );
436 my ($groups_clauses, $acl_clauses) = $self->_RoleClauses( $groups, $acl, @objects );
437 $self->_AddSubClause( "WhichObject", "(". join( ' OR ', @$groups_clauses ) .")" );
438 $self->_AddSubClause( "WhichRole", "(". join( ' OR ', @$acl_clauses ) .")" );
443 sub _WhoHaveRoleRightSplitted {
448 IncludeSystemRights => undef,
449 IncludeSuperusers => undef,
450 IncludeSubgroupMembers => 1,
455 my $groups = $self->_JoinGroups( %args );
456 my $acl = $self->_JoinACL( %args );
458 $self->Limit( ALIAS => $acl,
459 FIELD => 'PrincipalType',
460 VALUE => "$groups.Type",
465 $self->Limit( ALIAS => $self->PrincipalsAlias,
468 VALUE => $RT::SystemUser->id
471 my @objects = $self->_GetEquivObjects( %args );
472 unless ( @objects ) {
473 unless ( $args{'IncludeSystemRights'} ) {
474 $self->_AddSubClause( WhichObjects => "($acl.ObjectType != 'RT::System')" );
479 my ($groups_clauses, $acl_clauses) = $self->_RoleClauses( $groups, $acl, @objects );
480 $self->_AddSubClause( "WhichRole", "(". join( ' OR ', @$acl_clauses ) .")" );
483 foreach ( @$groups_clauses ) {
484 my $tmp = $self->Clone;
485 $tmp->_AddSubClause( WhichObject => $_ );
500 foreach my $obj ( @objects ) {
501 my $type = ref($obj)? ref($obj): $obj;
503 $id = $obj->id if ref($obj) && UNIVERSAL::can($obj, 'id') && $obj->id;
505 my $role_clause = "$groups.Domain = '$type-Role'";
506 # XXX: Groups.Instance is VARCHAR in DB, we should quote value
507 # if we want mysql 4.0 use indexes here. we MUST convert that
508 # field to integer and drop this quotes.
509 $role_clause .= " AND $groups.Instance = '$id'" if $id;
510 push @groups_clauses, "($role_clause)";
512 my $object_clause = "$acl.ObjectType = '$type'";
513 $object_clause .= " AND $acl.ObjectId = $id" if $id;
514 push @acl_clauses, "($object_clause)";
516 return (\@groups_clauses, \@acl_clauses);
519 # XXX: should be generalized
520 sub _JoinGroupMembersForGroupRights
524 my $group_members = $self->_JoinGroupMembers( %args );
525 $self->Limit( ALIAS => $args{'ACLAlias'},
526 FIELD => 'PrincipalId',
527 VALUE => "$group_members.GroupId",
532 # XXX: should be generalized
533 sub WhoHaveGroupRight
539 IncludeSystemRights => undef,
540 IncludeSuperusers => undef,
541 IncludeSubgroupMembers => 1,
546 # Find only rows where the right granted is
547 # the one we're looking up or _possibly_ superuser
548 my $acl = $self->_JoinACL( %args );
550 my ($check_objects) = ('');
551 my @objects = $self->_GetEquivObjects( %args );
555 foreach my $obj ( @objects ) {
556 my $type = ref($obj)? ref($obj): $obj;
558 $id = $obj->id if ref($obj) && UNIVERSAL::can($obj, 'id') && $obj->id;
560 my $object_clause = "$acl.ObjectType = '$type'";
561 $object_clause .= " AND $acl.ObjectId = $id" if $id;
562 push @object_clauses, "($object_clause)";
565 $check_objects = join ' OR ', @object_clauses;
567 if( !$args{'IncludeSystemRights'} ) {
568 $check_objects = "($acl.ObjectType != 'RT::System')";
571 $self->_AddSubClause( "WhichObject", "($check_objects)" );
573 $self->_JoinGroupMembersForGroupRights( %args, ACLAlias => $acl );
574 # Find only members of groups that have the right.
575 $self->Limit( ALIAS => $acl,
576 FIELD => 'PrincipalType',
581 $self->Limit( ALIAS => $self->PrincipalsAlias,
584 VALUE => $RT::SystemUser->id
589 # {{{ WhoBelongToGroups
591 =head2 WhoBelongToGroups { Groups => ARRAYREF, IncludeSubgroupMembers => 1 }
595 # XXX: should be generalized
596 sub WhoBelongToGroups {
598 my %args = ( Groups => undef,
599 IncludeSubgroupMembers => 1,
602 # Unprivileged users can't be granted real system rights.
603 # is this really the right thing to be saying?
604 $self->LimitToPrivileged();
606 my $group_members = $self->_JoinGroupMembers( %args );
608 foreach my $groupid (@{$args{'Groups'}}) {
609 $self->Limit( ALIAS => $group_members,
613 ENTRYAGGREGATOR => 'OR',