X-Git-Url: http://git.freeside.biz/gitweb/?a=blobdiff_plain;f=rt%2Flib%2FRT%2FGroup_Overlay.pm;fp=rt%2Flib%2FRT%2FGroup_Overlay.pm;h=5bf5f7c23f505fd053fc11a122d3293a4862b332;hb=b4b0c7e72d7eaee2fbfc7022022c9698323203dd;hp=eabeab0e029c9b28db61eec553fa51568106efe5;hpb=2dfda73eeb3eae2d4f894099754794ef07d060dd;p=freeside.git diff --git a/rt/lib/RT/Group_Overlay.pm b/rt/lib/RT/Group_Overlay.pm index eabeab0e0..5bf5f7c23 100644 --- a/rt/lib/RT/Group_Overlay.pm +++ b/rt/lib/RT/Group_Overlay.pm @@ -2,8 +2,8 @@ # BEGIN BPS TAGGED BLOCK {{{ # # COPYRIGHT: -# -# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC +# +# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC # # # (Except where explicitly superseded by other copyright notices) @@ -46,6 +46,7 @@ # those contributions and any derivatives thereof. # # END BPS TAGGED BLOCK }}} + # Released under the terms of version 2 of the GNU Public License =head1 NAME @@ -64,85 +65,6 @@ An RT group object. =head1 METHODS -=begin testing - -# {{{ Tests -ok (require RT::Group); - -ok (my $group = RT::Group->new($RT::SystemUser), "instantiated a group object"); -ok (my ($id, $msg) = $group->CreateUserDefinedGroup( Name => 'TestGroup', Description => 'A test group', - ), 'Created a new group'); -ok ($id != 0, "Group id is $id"); -ok ($group->Name eq 'TestGroup', "The group's name is 'TestGroup'"); -my $ng = RT::Group->new($RT::SystemUser); - -ok($ng->LoadUserDefinedGroup('TestGroup'), "Loaded testgroup"); -ok(($ng->id == $group->id), "Loaded the right group"); - - -ok (($id,$msg) = $ng->AddMember('1'), "Added a member to the group"); -ok($id, $msg); -ok (($id,$msg) = $ng->AddMember('2' ), "Added a member to the group"); -ok($id, $msg); -ok (($id,$msg) = $ng->AddMember('3' ), "Added a member to the group"); -ok($id, $msg); - -# Group 1 now has members 1, 2 ,3 - -my $group_2 = RT::Group->new($RT::SystemUser); -ok (my ($id_2, $msg_2) = $group_2->CreateUserDefinedGroup( Name => 'TestGroup2', Description => 'A second test group'), , 'Created a new group'); -ok ($id_2 != 0, "Created group 2 ok- $msg_2 "); -ok (($id,$msg) = $group_2->AddMember($ng->PrincipalId), "Made TestGroup a member of testgroup2"); -ok($id, $msg); -ok (($id,$msg) = $group_2->AddMember('1' ), "Added member RT_System to the group TestGroup2"); -ok($id, $msg); - -# Group 2 how has 1, g1->{1, 2,3} - -my $group_3 = RT::Group->new($RT::SystemUser); -ok (($id_3, $msg) = $group_3->CreateUserDefinedGroup( Name => 'TestGroup3', Description => 'A second test group'), 'Created a new group'); -ok ($id_3 != 0, "Created group 3 ok - $msg"); -ok (($id,$msg) =$group_3->AddMember($group_2->PrincipalId), "Made TestGroup a member of testgroup2"); -ok($id, $msg); - -# g3 now has g2->{1, g1->{1,2,3}} - -my $principal_1 = RT::Principal->new($RT::SystemUser); -$principal_1->Load('1'); - -my $principal_2 = RT::Principal->new($RT::SystemUser); -$principal_2->Load('2'); - -ok (($id,$msg) = $group_3->AddMember('1' ), "Added member RT_System to the group TestGroup2"); -ok($id, $msg); - -# g3 now has 1, g2->{1, g1->{1,2,3}} - -ok($group_3->HasMember($principal_2) == undef, "group 3 doesn't have member 2"); -ok($group_3->HasMemberRecursively($principal_2), "group 3 has member 2 recursively"); -ok($ng->HasMember($principal_2) , "group ".$ng->Id." has member 2"); -my ($delid , $delmsg) =$ng->DeleteMember($principal_2->Id); -ok ($delid !=0, "Sucessfully deleted it-".$delid."-".$delmsg); - -#Gotta reload the group objects, since we've been messing with various internals. -# we shouldn't need to do this. -#$ng->LoadUserDefinedGroup('TestGroup'); -#$group_2->LoadUserDefinedGroup('TestGroup2'); -#$group_3->LoadUserDefinedGroup('TestGroup'); - -# G1 now has 1, 3 -# Group 2 how has 1, g1->{1, 3} -# g3 now has 1, g2->{1, g1->{1, 3}} - -ok(!$ng->HasMember($principal_2) , "group ".$ng->Id." no longer has member 2"); -ok($group_3->HasMemberRecursively($principal_2) == undef, "group 3 doesn't have member 2"); -ok($group_2->HasMemberRecursively($principal_2) == undef, "group 2 doesn't have member 2"); -ok($ng->HasMember($principal_2) == undef, "group 1 doesn't have member 2");; -ok($group_3->HasMemberRecursively($principal_2) == undef, "group 3 has member 2 recursively"); - -# }}} - -=end testing @@ -165,10 +87,17 @@ $RIGHTS = { AdminGroup => 'Modify group metadata or delete group', # loc_pair AdminGroupMembership => 'Modify membership roster for this group', # loc_pair + DelegateRights => + "Delegate specific rights which have been granted to you.", # loc_pair ModifyOwnMembership => 'Join or leave this group', # loc_pair EditSavedSearches => 'Edit saved searches for this group', # loc_pair ShowSavedSearches => 'Display saved searches for this group', # loc_pair SeeGroup => 'Make this group visible to user', # loc_pair + + SeeGroupDashboard => 'View dashboards for this group', #loc_pair + CreateGroupDashboard => 'Create dashboards for this group', #loc_pair + ModifyGroupDashboard => 'Modify dashboards for this group', #loc_pair + DeleteGroupDashboard => 'Delete dashboards for this group', #loc_pair }; # Tell RT::ACE that this sort of object can get acls granted @@ -184,6 +113,20 @@ foreach my $right ( keys %{$RIGHTS} ) { $RT::ACE::LOWERCASERIGHTNAMES{ lc $right } = $right; } +=head2 AddRights C, C [, ...] + +Adds the given rights to the list of possible rights. This method +should be called during server startup, not at runtime. + +=cut + +sub AddRights { + my $self = shift; + my %new = @_; + $RIGHTS = { %$RIGHTS, %new }; + %RT::ACE::LOWERCASERIGHTNAMES = ( %RT::ACE::LOWERCASERIGHTNAMES, + map { lc($_) => $_ } keys %new); +} =head2 AvailableRights @@ -255,7 +198,6 @@ sub Load { my $self = shift; my $identifier = shift || return undef; - #if it's an int, load by id. otherwise, load by name. if ( $identifier !~ /\D/ ) { $self->SUPER::LoadById($identifier); } @@ -281,34 +223,44 @@ sub LoadUserDefinedGroup { my $self = shift; my $identifier = shift; - $self->LoadByCols( "Domain" => 'UserDefined', - "Name" => $identifier ); + if ( $identifier =~ /^\d+$/ ) { + return $self->LoadByCols( + Domain => 'UserDefined', + id => $identifier, + ); + } else { + return $self->LoadByCols( + Domain => 'UserDefined', + Name => $identifier, + ); + } } # }}} # {{{ sub LoadACLEquivalenceGroup -=head2 LoadACLEquivalenceGroup PRINCIPAL +=head2 LoadACLEquivalenceGroup PRINCIPAL -Loads a user's acl equivalence group. Takes a principal object. +Loads a user's acl equivalence group. Takes a principal object or its ID. ACL equivalnce groups are used to simplify the acl system. Each user has one group that only he is a member of. Rights granted to the user are actually granted to that group. This greatly simplifies ACL checks. While this results in a somewhat more complex setup when creating users and granting ACLs, it _greatly_ simplifies acl checks. - - =cut sub LoadACLEquivalenceGroup { - my $self = shift; - my $princ = shift; + my $self = shift; + my $principal = shift; + $principal = $principal->id if ref $principal; - $self->LoadByCols( "Domain" => 'ACLEquivalence', - "Type" => 'UserEquiv', - "Instance" => $princ->Id); + return $self->LoadByCols( + Domain => 'ACLEquivalence', + Type => 'UserEquiv', + Instance => $principal, + ); } # }}} @@ -349,8 +301,10 @@ sub LoadSystemInternalGroup { my $self = shift; my $identifier = shift; - $self->LoadByCols( "Domain" => 'SystemInternal', - "Type" => $identifier ); + return $self->LoadByCols( + Domain => 'SystemInternal', + Type => $identifier, + ); } # }}} @@ -493,6 +447,7 @@ sub _Create { ); my $id = $self->Id; unless ($id) { + $RT::Handle->Rollback() unless ($args{'InsideTransaction'}); return ( 0, $self->loc('Could not create group') ); } @@ -515,7 +470,7 @@ sub _Create { if ( $args{'_RecordTransaction'} ) { - $self->_NewTransaction( Type => "Create" ); + $self->_NewTransaction( Type => "Create" ); } $RT::Handle->Commit() unless ($args{'InsideTransaction'}); @@ -772,8 +727,14 @@ This routine finds all the cached group members that are members of this group } } + $self->_NewTransaction( Type => ($val == 1) ? "Disabled" : "Enabled" ); + $RT::Handle->Commit(); - return (1, $self->loc("Succeeded")); + if ( $val == 1 ) { + return (1, $self->loc("Group disabled")); + } else { + return (1, $self->loc("Group enabled")); + } } @@ -810,55 +771,108 @@ sub DeepMembersObj { # }}} -# {{{ UserMembersObj +# {{{ MembersObj -=head2 UserMembersObj +=head2 MembersObj -Returns an RT::Users object of this group's members, including -all members of subgroups +Returns an RT::GroupMembers object of this group's direct members. =cut -sub UserMembersObj { +sub MembersObj { my $self = shift; - - my $users = RT::Users->new($self->CurrentUser); + my $members_obj = RT::GroupMembers->new( $self->CurrentUser ); #If we don't have rights, don't include any results # TODO XXX WHY IS THERE NO ACL CHECK HERE? + $members_obj->LimitToMembersOfGroup( $self->PrincipalId ); - my $cached_members = $users->NewAlias('CachedGroupMembers'); - $users->Join(ALIAS1 => $cached_members, FIELD1 => 'MemberId', - ALIAS2 => $users->PrincipalsAlias, FIELD2 => 'id'); - $users->Limit(ALIAS => $cached_members, - FIELD => 'GroupId', - OPERATOR => '=', - VALUE => $self->PrincipalId); + return ( $members_obj ); - return ( $users); +} + +# }}} + +# {{{ GroupMembersObj + +=head2 GroupMembersObj [Recursively => 1] + +Returns an L object of this group's members. +By default returns groups including all subgroups, but +could be changed with C named argument. +B that groups are not filtered by type and result +may contain as well system groups, personal and other. + +=cut + +sub GroupMembersObj { + my $self = shift; + my %args = ( Recursively => 1, @_ ); + + my $groups = RT::Groups->new( $self->CurrentUser ); + my $members_table = $args{'Recursively'}? + 'CachedGroupMembers': 'GroupMembers'; + + my $members_alias = $groups->NewAlias( $members_table ); + $groups->Join( + ALIAS1 => $members_alias, FIELD1 => 'MemberId', + ALIAS2 => $groups->PrincipalsAlias, FIELD2 => 'id', + ); + $groups->Limit( + ALIAS => $members_alias, + FIELD => 'GroupId', + VALUE => $self->PrincipalId, + ); + $groups->Limit( + ALIAS => $members_alias, + FIELD => 'Disabled', + VALUE => 0, + ) if $args{'Recursively'}; + + return $groups; } # }}} -# {{{ MembersObj +# {{{ UserMembersObj -=head2 MembersObj +=head2 UserMembersObj -Returns an RT::GroupMembers object of this group's direct members. +Returns an L object of this group's members, by default +returns users including all members of subgroups, but could be +changed with C named argument. =cut -sub MembersObj { +sub UserMembersObj { my $self = shift; - my $members_obj = RT::GroupMembers->new( $self->CurrentUser ); + my %args = ( Recursively => 1, @_ ); #If we don't have rights, don't include any results # TODO XXX WHY IS THERE NO ACL CHECK HERE? - $members_obj->LimitToMembersOfGroup( $self->PrincipalId ); - return ( $members_obj ); + my $members_table = $args{'Recursively'}? + 'CachedGroupMembers': 'GroupMembers'; + my $users = RT::Users->new($self->CurrentUser); + my $members_alias = $users->NewAlias( $members_table ); + $users->Join( + ALIAS1 => $members_alias, FIELD1 => 'MemberId', + ALIAS2 => $users->PrincipalsAlias, FIELD2 => 'id', + ); + $users->Limit( + ALIAS => $members_alias, + FIELD => 'GroupId', + VALUE => $self->PrincipalId, + ); + $users->Limit( + ALIAS => $members_alias, + FIELD => 'Disabled', + VALUE => 0, + ) if $args{'Recursively'}; + + return ( $users); } # }}} @@ -982,7 +996,7 @@ sub _AddMember { if ( $self->HasMember( $new_member_obj ) ) { #User is already a member of this group. no need to add it - return ( 0, $self->loc("Group already has member") ); + return ( 0, $self->loc("Group already has member: [_1]", $new_member_obj->Object->Name) ); } if ( $new_member_obj->IsGroup && $new_member_obj->Object->HasMemberRecursively($self->PrincipalObj) ) { @@ -999,7 +1013,7 @@ sub _AddMember { InsideTransaction => $args{'InsideTransaction'} ); if ($id) { - return ( 1, $self->loc("Member added") ); + return ( 1, $self->loc("Member added: [_1]", $new_member_obj->Object->Name) ); } else { return(0, $self->loc("Couldn't add member to group")); @@ -1009,9 +1023,9 @@ sub _AddMember { # {{{ HasMember -=head2 HasMember RT::Principal +=head2 HasMember RT::Principal|id -Takes an RT::Principal object returns a GroupMember Id if that user is a +Takes an L object or its id returns a GroupMember Id if that user is a member of this group. Returns undef if the user isn't a member of the group or if the current user doesn't have permission to find out. Arguably, it should differentiate @@ -1023,29 +1037,28 @@ sub HasMember { my $self = shift; my $principal = shift; - - unless (UNIVERSAL::isa($principal,'RT::Principal')) { - $RT::Logger->crit("Group::HasMember was called with an argument that". - "isn't an RT::Principal. It's $principal"); - return(undef); - } - - unless ($principal->Id) { + my $id; + if ( UNIVERSAL::isa($principal,'RT::Principal') ) { + $id = $principal->id; + } elsif ( $principal =~ /^\d+$/ ) { + $id = $principal; + } else { + $RT::Logger->error("Group::HasMember was called with an argument that". + " isn't an RT::Principal or id. It's ".($principal||'(undefined)')); return(undef); } + return undef unless $id; my $member_obj = RT::GroupMember->new( $self->CurrentUser ); - $member_obj->LoadByCols( MemberId => $principal->id, - GroupId => $self->PrincipalId ); + $member_obj->LoadByCols( + MemberId => $id, + GroupId => $self->PrincipalId + ); - #If we have a member object - if ( defined $member_obj->id ) { - return ( $member_obj->id ); + if ( my $member_id = $member_obj->id ) { + return $member_id; } - - #If Load returns no objects, we have an undef id. else { - #$RT::Logger->debug($self." does not contain principal ".$principal->id); return (undef); } } @@ -1054,9 +1067,9 @@ sub HasMember { # {{{ HasMemberRecursively -=head2 HasMemberRecursively RT::Principal +=head2 HasMemberRecursively RT::Principal|id -Takes an RT::Principal object and returns true if that user is a member of +Takes an L object or its id and returns true if that user is a member of this group. Returns undef if the user isn't a member of the group or if the current user doesn't have permission to find out. Arguably, it should differentiate @@ -1068,23 +1081,27 @@ sub HasMemberRecursively { my $self = shift; my $principal = shift; - unless (UNIVERSAL::isa($principal,'RT::Principal')) { - $RT::Logger->crit("Group::HasMemberRecursively was called with an argument that". - "isn't an RT::Principal. It's $principal"); + my $id; + if ( UNIVERSAL::isa($principal,'RT::Principal') ) { + $id = $principal->id; + } elsif ( $principal =~ /^\d+$/ ) { + $id = $principal; + } else { + $RT::Logger->error("Group::HasMemberRecursively was called with an argument that". + " isn't an RT::Principal or id. It's $principal"); return(undef); } + return undef unless $id; + my $member_obj = RT::CachedGroupMember->new( $self->CurrentUser ); - $member_obj->LoadByCols( MemberId => $principal->Id, - GroupId => $self->PrincipalId , - Disabled => 0 - ); - - #If we have a member object - if ( defined $member_obj->id ) { - return ( 1); - } + $member_obj->LoadByCols( + MemberId => $id, + GroupId => $self->PrincipalId + ); - #If Load returns no objects, we have an undef id. + if ( my $member_id = $member_obj->id ) { + return $member_id; + } else { return (undef); } @@ -1325,23 +1342,17 @@ Returns the principal object for this user. returns an empty RT::Principal if there's no principal object matching this user. The response is cached. PrincipalObj should never ever change. -=begin testing - -ok(my $u = RT::Group->new($RT::SystemUser)); -ok($u->Load(4), "Loaded the first user"); -ok($u->PrincipalObj->ObjectId == 4, "user 4 is the fourth principal"); -ok($u->PrincipalObj->PrincipalType eq 'Group' , "Principal 4 is a group"); - -=end testing =cut sub PrincipalObj { my $self = shift; - unless ($self->{'PrincipalObj'} && + unless ( defined $self->{'PrincipalObj'} && + defined $self->{'PrincipalObj'}->ObjectId && ($self->{'PrincipalObj'}->ObjectId == $self->Id) && - ($self->{'PrincipalObj'}->PrincipalType eq 'Group')) { + (defined $self->{'PrincipalObj'}->PrincipalType && + $self->{'PrincipalObj'}->PrincipalType eq 'Group')) { $self->{'PrincipalObj'} = RT::Principal->new($self->CurrentUser); $self->{'PrincipalObj'}->LoadByCols('ObjectId' => $self->Id,