1 # BEGIN BPS TAGGED BLOCK {{{
5 # This software is Copyright (c) 1996-2007 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/copyleft/gpl.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 }}}
50 RT::Users - Collection of RT::User objects
64 ok(require RT::Users);
74 no warnings qw(redefine);
79 $self->{'table'} = 'Users';
80 $self->{'primary_key'} = 'id';
84 my @result = $self->SUPER::_Init(@_);
85 # By default, order by name
86 $self->OrderBy( ALIAS => 'main',
90 $self->{'princalias'} = $self->NewAlias('Principals');
92 # XXX: should be generalized
93 $self->Join( ALIAS1 => 'main',
95 ALIAS2 => $self->{'princalias'},
97 $self->Limit( ALIAS => $self->{'princalias'},
98 FIELD => 'PrincipalType',
107 =head2 PrincipalsAlias
109 Returns the string that represents this Users object's primary "Principals" alias.
113 # XXX: should be generalized
114 sub PrincipalsAlias {
116 return($self->{'princalias'});
125 A subclass of DBIx::SearchBuilder::_DoSearch that makes sure that _Disabled rows never get seen unless
126 we're explicitly trying to see them.
133 #unless we really want to find disabled rows, make sure we\'re only finding enabled ones.
134 unless ( $self->{'find_disabled_rows'} ) {
135 $self->LimitToEnabled();
137 return ( $self->SUPER::_DoSearch(@_) );
142 # {{{ sub LimitToEnabled
144 =head2 LimitToEnabled
146 Only find items that haven\'t been disabled
150 # XXX: should be generalized
154 $self->Limit( ALIAS => $self->PrincipalsAlias,
166 Takes one argument. an email address. limits the returned set to
174 $self->Limit( FIELD => 'EmailAddress', VALUE => "$addr" );
181 =head2 MemberOfGroup PRINCIPAL_ID
183 takes one argument, a group's principal id. Limits the returned set
184 to members of a given group
192 return $self->loc("No group specified") if ( !defined $group );
194 my $groupalias = $self->NewAlias('CachedGroupMembers');
196 # Join the principal to the groups table
197 $self->Join( ALIAS1 => $self->PrincipalsAlias,
199 ALIAS2 => $groupalias,
200 FIELD2 => 'MemberId' );
202 $self->Limit( ALIAS => "$groupalias",
210 # {{{ LimitToPrivileged
212 =head2 LimitToPrivileged
214 Limits to users who can be made members of ACLs and groups
218 sub LimitToPrivileged {
221 my $priv = RT::Group->new( $self->CurrentUser );
222 $priv->LoadSystemInternalGroup('Privileged');
223 unless ( $priv->Id ) {
224 $RT::Logger->crit("Couldn't find a privileged users group");
226 $self->MemberOfGroup( $priv->PrincipalId );
233 =head2 WhoHaveRight { Right => 'name', Object => $rt_object , IncludeSuperusers => undef, IncludeSubgroupMembers => undef, IncludeSystemRights => undef, EquivObjects => [ ] }
237 ok(my $users = RT::Users->new($RT::SystemUser));
238 $users->WhoHaveRight(Object =>$RT::System, Right =>'SuperUser');
239 ok($users->Count == 1, "There is one privileged superuser - Found ". $users->Count );
240 # TODO: this wants more testing
242 my $RTxUser = RT::User->new($RT::SystemUser);
243 ($id, $msg) = $RTxUser->Create( Name => 'RTxUser', Comments => "RTx extension user", Privileged => 1);
246 my $group = RT::Group->new($RT::SystemUser);
247 $group->LoadACLEquivalenceGroup($RTxUser->PrincipalObj);
250 bless $RTxSysObj, 'RTx::System';
251 *RTx::System::Id = sub { 1; };
252 *RTx::System::id = *RTx::System::Id;
253 my $ace = RT::Record->new($RT::SystemUser);
255 $ace->_BuildTableAttributes unless ($_TABLE_ATTR->{ref($self)});
256 ($id, $msg) = $ace->Create( PrincipalId => $group->id, PrincipalType => 'Group', RightName => 'RTxUserRight', ObjectType => 'RTx::System', ObjectId => 1 );
257 ok ($id, "ACL for RTxSysObj created");
260 bless $RTxObj, 'RTx::System::Record';
261 *RTx::System::Record::Id = sub { 4; };
262 *RTx::System::Record::id = *RTx::System::Record::Id;
264 $users = RT::Users->new($RT::SystemUser);
265 $users->WhoHaveRight(Right => 'RTxUserRight', Object => $RTxSysObj);
266 is($users->Count, 1, "RTxUserRight found for RTxSysObj");
268 $users = RT::Users->new($RT::SystemUser);
269 $users->WhoHaveRight(Right => 'RTxUserRight', Object => $RTxObj);
270 is($users->Count, 0, "RTxUserRight not found for RTxObj");
272 $users = RT::Users->new($RT::SystemUser);
273 $users->WhoHaveRight(Right => 'RTxUserRight', Object => $RTxObj, EquivObjects => [ $RTxSysObj ]);
274 is($users->Count, 1, "RTxUserRight found for RTxObj using EquivObjects");
276 $ace = RT::Record->new($RT::SystemUser);
278 $ace->_BuildTableAttributes unless ($_TABLE_ATTR->{ref($self)});
279 ($id, $msg) = $ace->Create( PrincipalId => $group->id, PrincipalType => 'Group', RightName => 'RTxUserRight', ObjectType => 'RTx::System::Record', ObjectId => 5 );
280 ok ($id, "ACL for RTxObj created");
283 bless $RTxObj2, 'RTx::System::Record';
284 *RTx::System::Record::Id = sub { 5; };
285 *RTx::System::Record::id = sub { 5; };
287 $users = RT::Users->new($RT::SystemUser);
288 $users->WhoHaveRight(Right => 'RTxUserRight', Object => $RTxObj2);
289 is($users->Count, 1, "RTxUserRight found for RTxObj2");
291 $users = RT::Users->new($RT::SystemUser);
292 $users->WhoHaveRight(Right => 'RTxUserRight', Object => $RTxObj2, EquivObjects => [ $RTxSysObj ]);
293 is($users->Count, 1, "RTxUserRight found for RTxObj2");
298 find all users who the right Right for this group, either individually
299 or as members of groups
301 If passed a queue object, with no id, it will find users who have that right for _any_ queue
305 # XXX: should be generalized
306 sub _JoinGroupMembers
310 IncludeSubgroupMembers => 1,
314 my $principals = $self->PrincipalsAlias;
316 # The cachedgroupmembers table is used for unrolling group memberships
317 # to allow fast lookups. if we bind to CachedGroupMembers, we'll find
318 # all members of groups recursively. if we don't we'll find only 'direct'
319 # members of the group in question
321 if ( $args{'IncludeSubgroupMembers'} ) {
322 $group_members = $self->NewAlias('CachedGroupMembers');
325 $group_members = $self->NewAlias('GroupMembers');
329 ALIAS1 => $group_members,
330 FIELD1 => 'MemberId',
331 ALIAS2 => $principals,
335 return $group_members;
338 # XXX: should be generalized
344 my $group_members = $self->_JoinGroupMembers( %args );
345 my $groups = $self->NewAlias('Groups');
349 ALIAS2 => $group_members,
356 # XXX: should be generalized
362 IncludeSuperusers => undef,
366 my $acl = $self->NewAlias('ACL');
369 FIELD => 'RightName',
370 OPERATOR => ( $args{Right} ? '=' : 'IS NOT' ),
371 VALUE => $args{Right} || 'NULL',
372 ENTRYAGGREGATOR => 'OR'
374 if ( $args{'IncludeSuperusers'} and $args{'Right'} ) {
377 FIELD => 'RightName',
379 VALUE => 'SuperUser',
380 ENTRYAGGREGATOR => 'OR'
386 # XXX: should be generalized
392 IncludeSystemRights => undef,
396 return () unless $args{'Object'};
398 my @objects = ($args{'Object'});
399 if ( UNIVERSAL::isa( $args{'Object'}, 'RT::Ticket' ) ) {
400 # If we're looking at ticket rights, we also want to look at the associated queue rights.
401 # this is a little bit hacky, but basically, now that we've done the ticket roles magic,
402 # we load the queue object and ask all the rest of our questions about the queue.
404 # XXX: This should be abstracted into object itself
405 if( $args{'Object'}->id ) {
406 push @objects, $args{'Object'}->QueueObj;
408 push @objects, 'RT::Queue';
412 if( $args{'IncludeSystemRights'} ) {
413 push @objects, 'RT::System';
415 push @objects, @{ $args{'EquivObjects'} };
416 return grep $_, @objects;
419 # XXX: should be generalized
425 IncludeSystemRights => undef,
426 IncludeSuperusers => undef,
427 IncludeSubgroupMembers => 1,
432 if ( defined $args{'ObjectType'} || defined $args{'ObjectId'} ) {
433 $RT::Logger->crit( "WhoHaveRight called with the Obsolete ObjectId/ObjectType API");
437 my $from_role = $self->Clone;
438 $from_role->WhoHaveRoleRight( %args );
440 my $from_group = $self->Clone;
441 $from_group->WhoHaveGroupRight( %args );
444 use DBIx::SearchBuilder::Union;
445 my $union = new DBIx::SearchBuilder::Union;
446 $union->add($from_role);
447 $union->add($from_group);
449 bless $self, ref($union);
455 # XXX: should be generalized
462 IncludeSystemRights => undef,
463 IncludeSuperusers => undef,
464 IncludeSubgroupMembers => 1,
469 my $groups = $self->_JoinGroups( %args );
470 my $acl = $self->_JoinACL( %args );
472 my ($check_roles, $check_objects) = ('','');
474 my @objects = $self->_GetEquivObjects( %args );
478 foreach my $obj ( @objects ) {
479 my $type = ref($obj)? ref($obj): $obj;
481 $id = $obj->id if ref($obj) && UNIVERSAL::can($obj, 'id') && $obj->id;
483 my $role_clause = "$groups.Domain = '$type-Role'";
484 # XXX: Groups.Instance is VARCHAR in DB, we should quote value
485 # if we want mysql 4.0 use indexes here. we MUST convert that
486 # field to integer and drop this quotes.
487 $role_clause .= " AND $groups.Instance = '$id'" if $id;
488 push @role_clauses, "($role_clause)";
490 my $object_clause = "$acl.ObjectType = '$type'";
491 $object_clause .= " AND $acl.ObjectId = $id" if $id;
492 push @object_clauses, "($object_clause)";
495 $check_roles .= join ' OR ', @role_clauses;
496 $check_objects = join ' OR ', @object_clauses;
498 if( !$args{'IncludeSystemRights'} ) {
499 $check_objects = "($acl.ObjectType != 'RT::System')";
503 $self->_AddSubClause( "WhichObject", "($check_objects)" );
504 $self->_AddSubClause( "WhichRole", "($check_roles)" );
506 $self->Limit( ALIAS => $acl,
507 FIELD => 'PrincipalType',
508 VALUE => "$groups.Type",
513 $self->Limit( ALIAS => $self->PrincipalsAlias,
516 VALUE => $RT::SystemUser->id
521 # XXX: should be generalized
522 sub _JoinGroupMembersForGroupRights
526 my $group_members = $self->_JoinGroupMembers( %args );
527 $self->Limit( ALIAS => $args{'ACLAlias'},
528 FIELD => 'PrincipalId',
529 VALUE => "$group_members.GroupId",
534 # XXX: should be generalized
535 sub WhoHaveGroupRight
541 IncludeSystemRights => undef,
542 IncludeSuperusers => undef,
543 IncludeSubgroupMembers => 1,
548 # Find only rows where the right granted is
549 # the one we're looking up or _possibly_ superuser
550 my $acl = $self->_JoinACL( %args );
552 my ($check_objects) = ('');
553 my @objects = $self->_GetEquivObjects( %args );
557 foreach my $obj ( @objects ) {
558 my $type = ref($obj)? ref($obj): $obj;
560 $id = $obj->id if ref($obj) && UNIVERSAL::can($obj, 'id') && $obj->id;
562 my $object_clause = "$acl.ObjectType = '$type'";
563 $object_clause .= " AND $acl.ObjectId = $id" if $id;
564 push @object_clauses, "($object_clause)";
567 $check_objects = join ' OR ', @object_clauses;
569 if( !$args{'IncludeSystemRights'} ) {
570 $check_objects = "($acl.ObjectType != 'RT::System')";
573 $self->_AddSubClause( "WhichObject", "($check_objects)" );
575 $self->_JoinGroupMembersForGroupRights( %args, ACLAlias => $acl );
576 # Find only members of groups that have the right.
577 $self->Limit( ALIAS => $acl,
578 FIELD => 'PrincipalType',
583 $self->Limit( ALIAS => $self->PrincipalsAlias,
586 VALUE => $RT::SystemUser->id
591 # {{{ WhoBelongToGroups
593 =head2 WhoBelongToGroups { Groups => ARRAYREF, IncludeSubgroupMembers => 1 }
597 # XXX: should be generalized
598 sub WhoBelongToGroups {
600 my %args = ( Groups => undef,
601 IncludeSubgroupMembers => 1,
604 # Unprivileged users can't be granted real system rights.
605 # is this really the right thing to be saying?
606 $self->LimitToPrivileged();
608 my $group_members = $self->_JoinGroupMembers( %args );
610 foreach my $groupid (@{$args{'Groups'}}) {
611 $self->Limit( ALIAS => $group_members,
615 ENTRYAGGREGATOR => 'OR',