1 # BEGIN BPS TAGGED BLOCK {{{
5 # This software is Copyright (c) 1996-2005 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., 675 Mass Ave, Cambridge, MA 02139, USA.
28 # CONTRIBUTION SUBMISSION POLICY:
30 # (The following paragraph is not intended to limit the rights granted
31 # to you to modify and distribute this software under the terms of
32 # the GNU General Public License and is only of importance to you if
33 # you choose to contribute your changes and enhancements to the
34 # community by submitting them to Best Practical Solutions, LLC.)
36 # By intentionally submitting any modifications, corrections or
37 # derivatives to this work, or any other work intended for use with
38 # Request Tracker, to Best Practical Solutions, LLC, you confirm that
39 # you are the copyright holder for those contributions and you grant
40 # Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
41 # royalty-free, perpetual, license to use, copy, create derivative
42 # works based on those contributions, and sublicense and distribute
43 # those contributions and any derivatives thereof.
45 # END BPS TAGGED BLOCK }}}
49 RT::Users - Collection of RT::User objects
63 ok(require RT::Users);
73 no warnings qw(redefine);
78 $self->{'table'} = 'Users';
79 $self->{'primary_key'} = 'id';
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',
106 =head2 PrincipalsAlias
108 Returns the string that represents this Users object's primary "Principals" alias.
112 # XXX: should be generalized
113 sub PrincipalsAlias {
115 return($self->{'princalias'});
124 A subclass of DBIx::SearchBuilder::_DoSearch that makes sure that _Disabled rows never get seen unless
125 we're explicitly trying to see them.
132 #unless we really want to find disabled rows, make sure we\'re only finding enabled ones.
133 unless ( $self->{'find_disabled_rows'} ) {
134 $self->LimitToEnabled();
136 return ( $self->SUPER::_DoSearch(@_) );
141 # {{{ sub LimitToEnabled
143 =head2 LimitToEnabled
145 Only find items that haven\'t been disabled
149 # XXX: should be generalized
153 $self->Limit( ALIAS => $self->PrincipalsAlias,
165 Takes one argument. an email address. limits the returned set to
173 $self->Limit( FIELD => 'EmailAddress', VALUE => "$addr" );
180 =head2 MemberOfGroup PRINCIPAL_ID
182 takes one argument, a group's principal id. Limits the returned set
183 to members of a given group
191 return $self->loc("No group specified") if ( !defined $group );
193 my $groupalias = $self->NewAlias('CachedGroupMembers');
195 # Join the principal to the groups table
196 $self->Join( ALIAS1 => $self->PrincipalsAlias,
198 ALIAS2 => $groupalias,
199 FIELD2 => 'MemberId' );
201 $self->Limit( ALIAS => "$groupalias",
209 # {{{ LimitToPrivileged
211 =head2 LimitToPrivileged
213 Limits to users who can be made members of ACLs and groups
217 sub LimitToPrivileged {
220 my $priv = RT::Group->new( $self->CurrentUser );
221 $priv->LoadSystemInternalGroup('Privileged');
222 unless ( $priv->Id ) {
223 $RT::Logger->crit("Couldn't find a privileged users group");
225 $self->MemberOfGroup( $priv->PrincipalId );
232 =head2 WhoHaveRight { Right => 'name', Object => $rt_object , IncludeSuperusers => undef, IncludeSubgroupMembers => undef, IncludeSystemRights => undef, EquivObjects => [ ] }
236 ok(my $users = RT::Users->new($RT::SystemUser));
237 $users->WhoHaveRight(Object =>$RT::System, Right =>'SuperUser');
238 ok($users->Count == 1, "There is one privileged superuser - Found ". $users->Count );
239 # TODO: this wants more testing
241 my $RTxUser = RT::User->new($RT::SystemUser);
242 ($id, $msg) = $RTxUser->Create( Name => 'RTxUser', Comments => "RTx extension user", Privileged => 1);
245 my $group = RT::Group->new($RT::SystemUser);
246 $group->LoadACLEquivalenceGroup($RTxUser->PrincipalObj);
249 bless $RTxSysObj, 'RTx::System';
250 *RTx::System::Id = sub { 1; };
251 *RTx::System::id = *RTx::System::Id;
252 my $ace = RT::Record->new($RT::SystemUser);
254 $ace->_BuildTableAttributes unless ($_TABLE_ATTR->{ref($self)});
255 ($id, $msg) = $ace->Create( PrincipalId => $group->id, PrincipalType => 'Group', RightName => 'RTxUserRight', ObjectType => 'RTx::System', ObjectId => 1 );
256 ok ($id, "ACL for RTxSysObj created");
259 bless $RTxObj, 'RTx::System::Record';
260 *RTx::System::Record::Id = sub { 4; };
261 *RTx::System::Record::id = *RTx::System::Record::Id;
263 $users = RT::Users->new($RT::SystemUser);
264 $users->WhoHaveRight(Right => 'RTxUserRight', Object => $RTxSysObj);
265 is($users->Count, 1, "RTxUserRight found for RTxSysObj");
267 $users = RT::Users->new($RT::SystemUser);
268 $users->WhoHaveRight(Right => 'RTxUserRight', Object => $RTxObj);
269 is($users->Count, 0, "RTxUserRight not found for RTxObj");
271 $users = RT::Users->new($RT::SystemUser);
272 $users->WhoHaveRight(Right => 'RTxUserRight', Object => $RTxObj, EquivObjects => [ $RTxSysObj ]);
273 is($users->Count, 1, "RTxUserRight found for RTxObj using EquivObjects");
275 $ace = RT::Record->new($RT::SystemUser);
277 $ace->_BuildTableAttributes unless ($_TABLE_ATTR->{ref($self)});
278 ($id, $msg) = $ace->Create( PrincipalId => $group->id, PrincipalType => 'Group', RightName => 'RTxUserRight', ObjectType => 'RTx::System::Record', ObjectId => 5 );
279 ok ($id, "ACL for RTxObj created");
282 bless $RTxObj2, 'RTx::System::Record';
283 *RTx::System::Record::Id = sub { 5; };
284 *RTx::System::Record::id = sub { 5; };
286 $users = RT::Users->new($RT::SystemUser);
287 $users->WhoHaveRight(Right => 'RTxUserRight', Object => $RTxObj2);
288 is($users->Count, 1, "RTxUserRight found for RTxObj2");
290 $users = RT::Users->new($RT::SystemUser);
291 $users->WhoHaveRight(Right => 'RTxUserRight', Object => $RTxObj2, EquivObjects => [ $RTxSysObj ]);
292 is($users->Count, 1, "RTxUserRight found for RTxObj2");
297 find all users who the right Right for this group, either individually
298 or as members of groups
300 If passed a queue object, with no id, it will find users who have that right for _any_ queue
304 # XXX: should be generalized
305 sub _JoinGroupMembers
309 IncludeSubgroupMembers => 1,
313 my $principals = $self->PrincipalsAlias;
315 # The cachedgroupmembers table is used for unrolling group memberships
316 # to allow fast lookups. if we bind to CachedGroupMembers, we'll find
317 # all members of groups recursively. if we don't we'll find only 'direct'
318 # members of the group in question
320 if ( $args{'IncludeSubgroupMembers'} ) {
321 $group_members = $self->NewAlias('CachedGroupMembers');
324 $group_members = $self->NewAlias('GroupMembers');
328 ALIAS1 => $group_members,
329 FIELD1 => 'MemberId',
330 ALIAS2 => $principals,
334 return $group_members;
337 # XXX: should be generalized
343 my $group_members = $self->_JoinGroupMembers( %args );
344 my $groups = $self->NewAlias('Groups');
348 ALIAS2 => $group_members,
355 # XXX: should be generalized
361 IncludeSuperusers => undef,
365 my $acl = $self->NewAlias('ACL');
368 FIELD => 'RightName',
369 OPERATOR => ( $args{Right} ? '=' : 'IS NOT' ),
370 VALUE => $args{Right} || 'NULL',
371 ENTRYAGGREGATOR => 'OR'
373 if ( $args{'IncludeSuperusers'} and $args{'Right'} ) {
376 FIELD => 'RightName',
378 VALUE => 'SuperUser',
379 ENTRYAGGREGATOR => 'OR'
385 # XXX: should be generalized
391 IncludeSystemRights => undef,
395 return () unless $args{'Object'};
397 my @objects = ($args{'Object'});
398 if ( UNIVERSAL::isa( $args{'Object'}, 'RT::Ticket' ) ) {
399 # If we're looking at ticket rights, we also want to look at the associated queue rights.
400 # this is a little bit hacky, but basically, now that we've done the ticket roles magic,
401 # we load the queue object and ask all the rest of our questions about the queue.
403 # XXX: This should be abstracted into object itself
404 if( $args{'Object'}->id ) {
405 push @objects, $args{'Object'}->QueueObj;
407 push @objects, 'RT::Queue';
411 if( $args{'IncludeSystemRights'} ) {
412 push @objects, 'RT::System';
414 push @objects, @{ $args{'EquivObjects'} };
415 return grep $_, @objects;
418 # XXX: should be generalized
424 IncludeSystemRights => undef,
425 IncludeSuperusers => undef,
426 IncludeSubgroupMembers => 1,
431 if ( defined $args{'ObjectType'} || defined $args{'ObjectId'} ) {
432 $RT::Logger->crit( "WhoHaveRight called with the Obsolete ObjectId/ObjectType API");
436 my $from_role = $self->Clone;
437 $from_role->WhoHaveRoleRight( %args );
439 my $from_group = $self->Clone;
440 $from_group->WhoHaveGroupRight( %args );
443 use DBIx::SearchBuilder::Union;
444 my $union = new DBIx::SearchBuilder::Union;
445 $union->add($from_role);
446 $union->add($from_group);
448 bless $self, ref($union);
454 # XXX: should be generalized
461 IncludeSystemRights => undef,
462 IncludeSuperusers => undef,
463 IncludeSubgroupMembers => 1,
468 my $groups = $self->_JoinGroups( %args );
469 my $acl = $self->_JoinACL( %args );
471 my ($check_roles, $check_objects) = ('','');
473 my @objects = $self->_GetEquivObjects( %args );
477 foreach my $obj ( @objects ) {
478 my $type = ref($obj)? ref($obj): $obj;
480 $id = $obj->id if ref($obj) && UNIVERSAL::can($obj, 'id') && $obj->id;
482 my $role_clause = "$groups.Domain = '$type-Role'";
483 # XXX: Groups.Instance is VARCHAR in DB, we should quote value
484 # if we want mysql 4.0 use indexes here. we MUST convert that
485 # field to integer and drop this quotes.
486 $role_clause .= " AND $groups.Instance = '$id'" if $id;
487 push @role_clauses, "($role_clause)";
489 my $object_clause = "$acl.ObjectType = '$type'";
490 $object_clause .= " AND $acl.ObjectId = $id" if $id;
491 push @object_clauses, "($object_clause)";
494 $check_roles .= join ' OR ', @role_clauses;
495 $check_objects = join ' OR ', @object_clauses;
497 if( !$args{'IncludeSystemRights'} ) {
498 $check_objects = "($acl.ObjectType != 'RT::System')";
502 $self->_AddSubClause( "WhichObject", "($check_objects)" );
503 $self->_AddSubClause( "WhichRole", "($check_roles)" );
505 $self->Limit( ALIAS => $acl,
506 FIELD => 'PrincipalType',
507 VALUE => "$groups.Type",
512 $self->Limit( ALIAS => $self->PrincipalsAlias,
515 VALUE => $RT::SystemUser->id
520 # XXX: should be generalized
521 sub _JoinGroupMembersForGroupRights
525 my $group_members = $self->_JoinGroupMembers( %args );
526 $self->Limit( ALIAS => $args{'ACLAlias'},
527 FIELD => 'PrincipalId',
528 VALUE => "$group_members.GroupId",
533 # XXX: should be generalized
534 sub WhoHaveGroupRight
540 IncludeSystemRights => undef,
541 IncludeSuperusers => undef,
542 IncludeSubgroupMembers => 1,
547 # Find only rows where the right granted is
548 # the one we're looking up or _possibly_ superuser
549 my $acl = $self->_JoinACL( %args );
551 my ($check_objects) = ('');
552 my @objects = $self->_GetEquivObjects( %args );
556 foreach my $obj ( @objects ) {
557 my $type = ref($obj)? ref($obj): $obj;
559 $id = $obj->id if ref($obj) && UNIVERSAL::can($obj, 'id') && $obj->id;
561 my $object_clause = "$acl.ObjectType = '$type'";
562 $object_clause .= " AND $acl.ObjectId = $id" if $id;
563 push @object_clauses, "($object_clause)";
566 $check_objects = join ' OR ', @object_clauses;
568 if( !$args{'IncludeSystemRights'} ) {
569 $check_objects = "($acl.ObjectType != 'RT::System')";
572 $self->_AddSubClause( "WhichObject", "($check_objects)" );
574 $self->_JoinGroupMembersForGroupRights( %args, ACLAlias => $acl );
575 # Find only members of groups that have the right.
576 $self->Limit( ALIAS => $acl,
577 FIELD => 'PrincipalType',
582 $self->Limit( ALIAS => $self->PrincipalsAlias,
585 VALUE => $RT::SystemUser->id
590 # {{{ WhoBelongToGroups
592 =head2 WhoBelongToGroups { Groups => ARRAYREF, IncludeSubgroupMembers => 1 }
596 # XXX: should be generalized
597 sub WhoBelongToGroups {
599 my %args = ( Groups => undef,
600 IncludeSubgroupMembers => 1,
603 # Unprivileged users can't be granted real system rights.
604 # is this really the right thing to be saying?
605 $self->LimitToPrivileged();
607 my $group_members = $self->_JoinGroupMembers( %args );
609 foreach my $groupid (@{$args{'Groups'}}) {
610 $self->Limit( ALIAS => $group_members,
614 ENTRYAGGREGATOR => 'OR',