Fix a bug in how groups using IP filters cache the user Visitor. Also, significant speedups in group lookups. Fixes bug #11552.
This commit is contained in:
parent
0a05907c39
commit
d2b6a7fff1
5 changed files with 633 additions and 56 deletions
|
|
@ -16,6 +16,7 @@
|
|||
- fixed #11566: Group API: group membership cannot be checked without consideration of expiration dates.
|
||||
- fixed #11567: EMS: Build badge page, ticket tab, pagination
|
||||
- added: a new inbox setting which supresses friend rejection notices
|
||||
- fixed #11552: Visitors (and others) can bypass group-by-IP restrictions
|
||||
|
||||
7.9.4
|
||||
- We're shipping underscore.js now for its suite of extremely handy utility
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ my $session = start(); # this line required
|
|||
modifySortItems( $session );
|
||||
fixRequestForApprovalScratch($session);
|
||||
addRejectNoticeSetting($session);
|
||||
updateGroupGroupingsTable($session);
|
||||
|
||||
finish($session); # this line required
|
||||
|
||||
|
|
@ -56,6 +57,16 @@ sub addRejectNoticeSetting {
|
|||
print "DONE!\n" unless $quiet;
|
||||
}
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
# Add keys and indicies to groupGroupings to help speed up group queries
|
||||
sub updateGroupGroupingsTable {
|
||||
my $session = shift;
|
||||
print "\tAdding primary key and indicies to groupGroupings table... " unless $quiet;
|
||||
$session->db->write("alter table groupGroupings add primary key (groupId,inGroup)");
|
||||
$session->db->write("alter table groupGroupings add index inGroup (inGroup)");
|
||||
print "DONE!\n" unless $quiet;
|
||||
}
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
# Describe what our function does
|
||||
sub fixRequestForApprovalScratch {
|
||||
|
|
|
|||
|
|
@ -219,6 +219,47 @@ sub autoDelete {
|
|||
return $self->get("autoDelete");
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 cacheGroupings ( user, is_member )
|
||||
|
||||
Adds a record to the grouping for this group into the cache.
|
||||
|
||||
=head3 user
|
||||
|
||||
User object to set cache for
|
||||
|
||||
=head3 is_member
|
||||
|
||||
Boolean which indicates whether or not the user passed in is a member of this group
|
||||
|
||||
=cut
|
||||
|
||||
sub cacheGroupings {
|
||||
my $self = shift;
|
||||
my $session = $self->session;
|
||||
my $groupId = $self->getId;
|
||||
my $user = shift;
|
||||
my $isInGroup = shift || 0;
|
||||
my $userId = $user->userId;
|
||||
my $sessionId = $session->getId;
|
||||
|
||||
### Undocumented - cache and groupMembers can be passed in if it they are already built.
|
||||
#These exist specifically for WebGUI::User::isInGroup to use and should not be used elsewhere
|
||||
#unless you know what you are doing
|
||||
my $cache = shift || WebGUI::Cache->new($session,["groupMembers",$groupId]) || {};
|
||||
my $groupMembers = shift || $cache->get;
|
||||
|
||||
#Build cache in a special way for visitors
|
||||
if($userId eq '1') {
|
||||
$groupMembers->{$userId}->{$sessionId} = { isMember => $isInGroup };
|
||||
}
|
||||
else {
|
||||
$groupMembers->{$userId} = { isMember => $isInGroup };
|
||||
}
|
||||
|
||||
$cache->set($groupMembers, $self->groupCacheTimeout);
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
|
|
@ -229,15 +270,17 @@ Clears all caches for this group and any ancestor groups of the group.
|
|||
=cut
|
||||
|
||||
sub clearCaches {
|
||||
my $self = shift;
|
||||
my $self = shift;
|
||||
my $session = $self->session;
|
||||
##Clear my cache and the cache of all groups above me.
|
||||
my $groups = $self->getAllGroupsFor();
|
||||
foreach my $group ( $self->getId, @{ $groups } ) {
|
||||
WebGUI::Cache->new($self->session, $group)->delete;
|
||||
foreach my $groupId ( $self->getId, @{ $groups } ) {
|
||||
WebGUI::Cache->new($session, $groupId)->delete;
|
||||
WebGUI::Cache->new($session, ["groupMembers", $groupId])->delete;
|
||||
}
|
||||
$self->session->stow->delete("groupObj");
|
||||
$self->session->stow->delete("isInGroup");
|
||||
$self->session->stow->delete("gotGroupsInGroup");
|
||||
$session->stow->delete("groupObj");
|
||||
$session->stow->delete("isInGroup");
|
||||
$session->stow->delete("gotGroupsInGroup");
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
|
@ -523,7 +566,7 @@ sub get {
|
|||
|
||||
=head2 getAllGroupsFor ( )
|
||||
|
||||
Returns an array reference containing a list of all groups this group is in, recursively.
|
||||
Returns an array reference containing a list of all groupIds this group is in, recursively.
|
||||
|
||||
=cut
|
||||
|
||||
|
|
@ -637,7 +680,7 @@ sub getDatabaseUsers {
|
|||
|
||||
=head2 getGroupsFor ( )
|
||||
|
||||
Returns an array reference containing a list of groups this group is in. This method
|
||||
Returns an array reference containing a list of groupIds this group is in. This method
|
||||
does not check recursively backwards up the list of groups.
|
||||
|
||||
=cut
|
||||
|
|
@ -711,7 +754,7 @@ Returns the groupId for this group.
|
|||
|
||||
sub getId {
|
||||
my $self = shift;
|
||||
return $self->{_groupId};
|
||||
return $self->{_groupId};
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -956,6 +999,373 @@ sub getUsersNotIn {
|
|||
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 hasDatabaseUser ( userId )
|
||||
|
||||
Determine if the user passed in is a member of this group via a database query.
|
||||
|
||||
Membership will always be false if no query or database link has been defined
|
||||
for this group.
|
||||
|
||||
=head3 userId
|
||||
|
||||
id of the user to check for membership
|
||||
|
||||
=cut
|
||||
|
||||
sub hasDatabaseUser {
|
||||
my $self = shift;
|
||||
my $userId = shift;
|
||||
my $session = $self->session;
|
||||
my $gid = $self->getId;
|
||||
|
||||
my $query = $self->get("dbQuery");
|
||||
my $dbLinkId = $self->get("databaseLinkId");
|
||||
return 0 unless ($userId && $query && defined $dbLinkId);
|
||||
|
||||
my $dbLink = WebGUI::DatabaseLink->new($session,$dbLinkId);
|
||||
unless (defined $dbLink) {
|
||||
$session->log->error("The database link ".$dbLinkId." no longer exists even though group ".$gid." references it. Group $gid may not be working correctly");
|
||||
return 0;
|
||||
}
|
||||
|
||||
my $dbh = $dbLink->db;
|
||||
unless (defined $dbh) {
|
||||
$session->log->error("Link to database established by could not get database handler for group $gid. This group may not be working correctly");
|
||||
$dbLink->disconnect;
|
||||
return 0;
|
||||
}
|
||||
|
||||
WebGUI::Macro::process($self->session,\$query);
|
||||
#Try to speed up the query by adding a userId filter to the where clause
|
||||
if ($query =~ m/^\s*SELECT\s*(.*)\s*FROM/i) {
|
||||
my $uid_ident = $1;
|
||||
$query =~ s/where/where $uid_ident = '$userId' and/i;
|
||||
}
|
||||
my $sth = $dbh->unconditionalRead($query);
|
||||
|
||||
unless(defined $sth) {
|
||||
$session->log->error("Couldn't process unconditional read for database group with group id $gid. This group may not be working correctly");
|
||||
return 0;
|
||||
}
|
||||
|
||||
unless ($sth->errorCode < 1) {
|
||||
$session->log->warn("There was a problem with the database query for group ID $gid.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
while (my ($uid) = $sth->array) {
|
||||
if ($uid eq $userId) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 hasIpUser ( userId )
|
||||
|
||||
Determine if the user passed in is a member of this group via the lastIP recorded
|
||||
in the user's session and this group's IpFilter.
|
||||
|
||||
Membership will always be false if no IpFilter has been set
|
||||
|
||||
=head3 userId
|
||||
|
||||
id of the user to check for membership
|
||||
|
||||
=cut
|
||||
|
||||
sub hasIpUser {
|
||||
my $self = shift;
|
||||
my $userId = shift;
|
||||
my $session = $self->session;
|
||||
|
||||
my $IpFilter = $self->ipFilter();
|
||||
return 0 unless ($IpFilter && $userId);
|
||||
|
||||
$IpFilter =~ s/\s//g;
|
||||
my @filters = split /;/, $IpFilter;
|
||||
|
||||
my @ips = $session->db->buildArray(
|
||||
q{ select lastIP from userSession where expires > ? and userId = ? }
|
||||
,[ time(), $userId ]
|
||||
);
|
||||
|
||||
foreach my $ip (@ips) {
|
||||
return 1 if (isInSubnet($ip,\@filters));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 hasKarmaUser ( userId )
|
||||
|
||||
Determine if the user passed in is a member of this group via the their current
|
||||
karma setting and this group's karmaThreshold.
|
||||
|
||||
If karma is not enabled for this site, membership will always be false.
|
||||
|
||||
=head3 userId
|
||||
|
||||
id of the user to check for membership
|
||||
|
||||
=cut
|
||||
|
||||
sub hasKarmaUser {
|
||||
my $self = shift;
|
||||
my $userId = shift;
|
||||
my $session = $self->session;
|
||||
|
||||
return 0 unless ($session->setting->get('useKarma') && $userId);
|
||||
|
||||
return $session->db->quickScalar(
|
||||
q{ select count(*) from users where karma >= ? and userId = ? }
|
||||
,[$self->karmaThreshold,$userId]
|
||||
);
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 hasLDAPUser ( userId )
|
||||
|
||||
Determine if the user passed in is a member of this group via an LDAP
|
||||
connection
|
||||
|
||||
If ldapLink, ldapGroup, and ldapGroupProperty are not configured for this group
|
||||
membership will always be false.
|
||||
|
||||
#TODO - change the way this works to search LDAP for the dn associated with the
|
||||
userId. That should speed this up a bunch for people using LDAP groups.
|
||||
|
||||
=head3 userId
|
||||
|
||||
id of the user to check for membership
|
||||
|
||||
=cut
|
||||
|
||||
sub hasLDAPUser {
|
||||
my $self = shift;
|
||||
my $userId = shift;
|
||||
my $session = $self->session;
|
||||
my @ldapUsers = ();
|
||||
my $gid = $self->getId;
|
||||
|
||||
### Check LDAP
|
||||
my $ldapLinkId = $self->get("ldapLinkId");
|
||||
my $ldapGroup = $self->get("ldapGroup");
|
||||
my $ldapGroupProperty = $self->get("ldapGroupProperty");
|
||||
my $ldapRecursiveProperty = $self->get("ldapRecursiveProperty");
|
||||
my $ldapRecurseFilter = $self->get("ldapRecursiveFilter");
|
||||
|
||||
return 0 unless ($ldapLinkId && $ldapGroup && $ldapGroupProperty && $userId);
|
||||
|
||||
my $ldapLink = WebGUI::LDAPLink->new($session,$ldapLinkId);
|
||||
unless ($ldapLink && $ldapLink->bind) {
|
||||
$self->session->errorHandler->warn("There was a problem connecting to LDAP link $ldapLinkId for group ID $gid.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
my $people = [];
|
||||
if($ldapRecursiveProperty) {
|
||||
$ldapLink->recurseProperty($ldapGroup,$people,$ldapGroupProperty,$ldapRecursiveProperty,$ldapRecurseFilter);
|
||||
} else {
|
||||
$people = $ldapLink->getProperty($ldapGroup,$ldapGroupProperty);
|
||||
}
|
||||
$ldapLink->unbind;
|
||||
|
||||
foreach my $person (@{$people}) {
|
||||
$person =~ s/\s*,\s*/,/g;
|
||||
$person = lc($person);
|
||||
my $personRegExp = "^uid=$person,";
|
||||
my $uid = $session->db->quickScalar("select userId from authentication where authMethod='LDAP' and fieldName='connectDN' and lower(fieldData) = ? OR lower(fieldData) REGEXP ?",[$person,$personRegExp]);
|
||||
return 1 if ($uid eq $userId);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 hasScratchUser ( userId )
|
||||
|
||||
Determine if the user passed in is a member of this group via session scratch
|
||||
variable settings and this group's scratchFilter.
|
||||
|
||||
If no scratchFilter has been set for this group, membership will always be false.
|
||||
|
||||
=head3 userId
|
||||
|
||||
id of the user to check for membership
|
||||
|
||||
=cut
|
||||
|
||||
sub hasScratchUser {
|
||||
my $self = shift;
|
||||
my $userId = shift;
|
||||
my $session = $self->session;
|
||||
|
||||
my $scratchFilter = $self->scratchFilter();
|
||||
return 0 unless ($scratchFilter && $userId);
|
||||
|
||||
$scratchFilter =~ s/\s//g;
|
||||
my @filters = split /;/, $scratchFilter;
|
||||
|
||||
my @scratchClauses = ();
|
||||
my @scratchPlaceholders = ( $userId, time() );
|
||||
foreach my $filter (@filters) {
|
||||
my ($name, $value) = split /=/, $filter;
|
||||
push @scratchClauses, "(s.name=? AND s.value=?)";
|
||||
push @scratchPlaceholders, $name, $value;
|
||||
}
|
||||
my $scratchClause = join ' OR ', @scratchClauses;
|
||||
|
||||
my $query = qq{
|
||||
select
|
||||
count(*)
|
||||
from
|
||||
userSession u, userSessionScratch s
|
||||
where
|
||||
u.sessionId=s.sessionId AND
|
||||
u.userId = ? AND
|
||||
u.expires > ? AND
|
||||
( $scratchClause )
|
||||
};
|
||||
|
||||
return $session->db->quickScalar($query, [ @scratchPlaceholders ]);
|
||||
}
|
||||
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 hasUser ( user )
|
||||
|
||||
Determine if the user passed in is a member of one of the special groups
|
||||
for this group
|
||||
|
||||
=head3 user
|
||||
|
||||
user object to check groups for
|
||||
|
||||
=cut
|
||||
|
||||
sub hasUser {
|
||||
my $self = shift;
|
||||
my $session = $self->session;
|
||||
my $user = shift || WebGUI::User->new($session,3); #Check the admin account if no user is passed in
|
||||
my $gid = $self->getId;
|
||||
my $db = $session->db;
|
||||
|
||||
my $uid = $user->userId;
|
||||
### Get what's in session cache for this group
|
||||
my $isInGroup = $session->stow->get("isInGroup", { noclone => 1 }) || {};
|
||||
### Check to see that we have a cache built for this user
|
||||
my $hasCache = (exists $isInGroup->{$uid}->{cached});
|
||||
|
||||
### Return what is in the cache if we've already cached this group in the session.
|
||||
return $isInGroup->{$uid}->{$gid} if ( exists $isInGroup->{$uid}->{$gid} );
|
||||
|
||||
### If we dont' have a cache yet, cache all of the groups this user is directly a member of
|
||||
### this will only happen if there is no cache built for this user and it saves us from running one query per group
|
||||
unless ($hasCache) {
|
||||
### Get the list of groups this user is directly a member of
|
||||
my @groups = $db->buildArray(
|
||||
q{ select groupId from groupings where userId=? and expireDate > ? }
|
||||
, [$uid,time()]
|
||||
);
|
||||
### Cache the groupings we find
|
||||
map { $isInGroup->{$uid}->{$_} = 1 } @groups;
|
||||
### Set a cached flag so someone else doesn't accidentally call stow before us and screw our quick caching method
|
||||
$isInGroup->{$uid}->{cached} = 1;
|
||||
### Stow the cache here because we have set the cache for other groups besides this one.
|
||||
$session->stow->set("isInGroup",$isInGroup);
|
||||
### Return if we found the user in this group
|
||||
return 1 if ( $isInGroup->{$uid}->{$gid} );
|
||||
}
|
||||
|
||||
### User was not found directly in this group. Create a list of groups to check deeply and add this group to that list
|
||||
my $groupsToCheckDeeply = { $gid => 1 };
|
||||
|
||||
#Made it here because user is not in the group itself. Now check for direct existance in the sub groups.
|
||||
#Now build a list of the subgroups for this group that the user is part of
|
||||
|
||||
### Check all of the sub groups for direct existance, caching all of the subgroups that we do not find the user
|
||||
### in our list of groups that need to be checked more deeply
|
||||
my @groups = ($gid); #Start checking sub groups of this group only
|
||||
my $loopLimit = 100; #Set a loop limit just to be safe
|
||||
while (scalar(@groups) && $loopLimit--) {
|
||||
### Check all of the groups of groups for all of the current @groups array. The query below
|
||||
### returns the group that was in the group along with whether or not the user is directly a member
|
||||
my $sqlInStr = $db->quoteAndJoin(\@groups);
|
||||
my $sth = $db->read(
|
||||
qq{ select
|
||||
groupGroupings.groupId, userId
|
||||
from
|
||||
groupGroupings
|
||||
left join groupings on groupGroupings.groupId=groupings.groupId and userId=?
|
||||
where
|
||||
inGroup in ($sqlInStr)
|
||||
}
|
||||
,[$uid]
|
||||
);
|
||||
### Create a subgroup cache for this pass of the loop so we know what groups to check next
|
||||
my $subgroupCache = {};
|
||||
while (my ($groupId,$userId) = $sth->array){
|
||||
next if ($subgroupCache->{$groupId}); #Skip subgroups we've already checked - nothing has changed
|
||||
### Return true if we find that the user is in the sub group from the session cache - no need to stow any caches here
|
||||
return 1 if ($isInGroup->{$uid}->{$groupId});
|
||||
### If the userId field is not null, that means that this user is directly a member of this sub group
|
||||
if($userId) {
|
||||
### Stow the result and return true;
|
||||
$isInGroup->{$uid}->{$groupId} = 1; #Cache the sub group results
|
||||
$isInGroup->{$uid}->{$gid} = 1; #Cache the results for the group we are checking
|
||||
$session->stow->set("isInGroup",$isInGroup); #Stow the Cache
|
||||
return 1;
|
||||
}
|
||||
### We made it here because the user is not directly in the subgroup.
|
||||
$subgroupCache->{$groupId} = 1; #Update the subgroup cache for the next outer loop pass
|
||||
$groupsToCheckDeeply->{$groupId} = 1; #We need to check this group more deeply
|
||||
}
|
||||
### Get the next level of sub groups to check from the subgroupCache keys.
|
||||
@groups = keys %{$subgroupCache};
|
||||
}
|
||||
|
||||
### Made it here because the user is not directly in the group itself or directly in any of it's subgroups
|
||||
### We should have a flattened list of groups in this group that we should now check one by one to see if the
|
||||
### user is is a member via one of the other methods available for groups
|
||||
|
||||
foreach my $groupIdInGroup (keys %{$groupsToCheckDeeply}) {
|
||||
### Instantiate the group
|
||||
my $groupToCheck = __PACKAGE__->new($session,$groupIdInGroup);
|
||||
### Check the 'has' method for each of the 'other' group methods available for this user
|
||||
### perform checks in a least -> most expensive manner. If we find the user, stow the cache and return true
|
||||
if( $groupToCheck->hasIpUser($uid)
|
||||
|| $groupToCheck->hasKarmaUser($uid)
|
||||
|| $groupToCheck->hasScratchUser($uid)
|
||||
|| $groupToCheck->hasDatabaseUser($uid)
|
||||
|| $groupToCheck->hasLDAPUser($uid)
|
||||
) {
|
||||
#Found the user in one of the 'other' group methods
|
||||
$isInGroup->{$uid}->{$groupIdInGroup} = 1; #Cache the results for this group so we don't have to check it again
|
||||
$isInGroup->{$uid}->{$gid} = 1; #Cache the results for the main group because we found the user in one of the subgroups
|
||||
$session->stow->set("isInGroup",$isInGroup); #Stow the cache
|
||||
return 1;
|
||||
}
|
||||
#Made it here because we did not find the user at all in this subgroup. Cache the result so we don't have to check this subgroup again.
|
||||
$isInGroup->{$uid}->{$groupIdInGroup} = 0;
|
||||
}
|
||||
|
||||
#If we made it here, that means the user is not in the group or any of it's sub groups
|
||||
#Cache the result, stow the cache, and return false as this group does not contain the user.
|
||||
$isInGroup->{$uid}->{$gid} = 0;
|
||||
$session->stow->set("isInGroup",$isInGroup);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -900,7 +900,7 @@ sub isEnabled {
|
|||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 isInGroup ( [ groupId ] )
|
||||
=head2 isInGroup ( [groupId ] )
|
||||
|
||||
Returns a boolean (0|1) value signifying that the user has the required privileges. Always returns true for Admins.
|
||||
|
||||
|
|
@ -911,38 +911,48 @@ The group that you wish to verify against the user. Defaults to group with Id 3
|
|||
=cut
|
||||
|
||||
sub isInGroup {
|
||||
my ($self, $gid) = @_;
|
||||
$gid = 3 unless $gid;
|
||||
my $uid = $self->userId;
|
||||
### The following several checks are to increase performance. If this section were removed, everything would continue to work as normal.
|
||||
#my $eh = $self->session->errorHandler;
|
||||
#$eh->warn("Group Id is: $gid for ".$tgroup->name);
|
||||
return 1 if ($gid eq '7'); # everyone is in the everyone group
|
||||
return 1 if ($gid eq '1' && $uid eq '1'); # visitors are in the visitors group
|
||||
return 1 if ($gid eq '2' && $uid ne '1'); # if you're not a visitor, then you're a registered user
|
||||
### Get data for auxillary checks.
|
||||
my $isInGroup = $self->session->stow->get("isInGroup", { noclone => 1 });
|
||||
### Look to see if we've already looked up this group.
|
||||
return $isInGroup->{$uid}{$gid} if exists $isInGroup->{$uid}{$gid};
|
||||
### Lookup the actual groupings.
|
||||
my $group = WebGUI::Group->new($self->session,$gid);
|
||||
if ( !$group ) {
|
||||
$group = WebGUI::Group->new($self->session,3);
|
||||
}
|
||||
### Check for groups of groups.
|
||||
my $users = $group->getAllUsers();
|
||||
foreach my $user (@{$users}) {
|
||||
$isInGroup->{$user}{$gid} = 1;
|
||||
if ($uid eq $user) {
|
||||
$self->session->stow->set("isInGroup",$isInGroup);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
$isInGroup->{$uid}{$gid} = 0;
|
||||
$self->session->stow->set("isInGroup",$isInGroup);
|
||||
return 0;
|
||||
}
|
||||
my ($self, $gid) = @_;
|
||||
my $session = $self->session;
|
||||
my $uid = $self->userId;
|
||||
$gid = 3 unless $gid;
|
||||
|
||||
### The following several checks are to increase performance. If this section were removed, everything would continue to work as normal.
|
||||
return 1 if ($gid eq '7'); # everyone is in the everyone group
|
||||
return 1 if ($gid eq '1' && $uid eq '1'); # visitors are in the visitors group
|
||||
return 1 if ($gid eq '2' && $uid ne '1'); # if you're not a visitor, then you're a registered user
|
||||
|
||||
### Check stow before we check the cache. Stow is in memory and much faster
|
||||
my $stow = $session->stow->get("isInGroup", { noclone => 1 }) || {};
|
||||
return $stow->{$uid}->{$gid} if (exists $stow->{$uid}->{$gid});
|
||||
|
||||
### Don't bother checking File Cache if we already have a stow for this group.
|
||||
### We can find what we need there and save ourselves a bunch of time
|
||||
my $cache = undef;
|
||||
my $groupMembers = undef;
|
||||
unless ($stow->{$uid}->{$gid}) {
|
||||
$cache = WebGUI::Cache->new($session,["groupMembers",$gid]);
|
||||
$groupMembers = $cache->get || {};
|
||||
#If we have this user's membership cached, return what we have stored
|
||||
if (exists $groupMembers->{$uid}) {
|
||||
return $groupMembers->{$uid}->{isMember} if (!$self->isVisitor);
|
||||
return $groupMembers->{$uid}->{$session->getId}->{isMember} #Include the session check for visitors
|
||||
}
|
||||
}
|
||||
|
||||
### Instantiate the group
|
||||
my $group = WebGUI::Group->new($session,$gid);
|
||||
if ( !$group ) {
|
||||
#Group is not valid, check the admin group
|
||||
$group = WebGUI::Group->new($session,3);
|
||||
}
|
||||
|
||||
#Check the group for membership
|
||||
my $isInGroup = $group->hasUser($self);
|
||||
|
||||
#Write what we found to file cache
|
||||
$group->cacheGroupings( $self, $isInGroup, $cache, $groupMembers );
|
||||
return $isInGroup;
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
|
|
|
|||
175
t/Group.t
175
t/Group.t
|
|
@ -74,8 +74,26 @@ my @ipTests = (
|
|||
},
|
||||
);
|
||||
|
||||
my @ldapTests = (
|
||||
{
|
||||
dn => 'uid=Byron Hadley,o=shawshank',
|
||||
comment => 'bad dn for group',
|
||||
expect => 0,
|
||||
},
|
||||
{
|
||||
dn => 'uid=Andy Dufresne,o=shawshank',
|
||||
comment => 'good dn for group',
|
||||
expect => 1,
|
||||
},
|
||||
{
|
||||
dn => 'uid=Bogs Diamond,o=shawshank',
|
||||
comment => 'another good dn for group',
|
||||
expect => 1,
|
||||
},
|
||||
);
|
||||
|
||||
plan tests => (151 + scalar(@scratchTests) + scalar(@ipTests)); # increment this value for each test you create
|
||||
|
||||
plan tests => (164 + (scalar(@scratchTests) * 2) + scalar(@ipTests)); # increment this value for each test you create
|
||||
|
||||
my $session = WebGUI::Test->session;
|
||||
my $testCache = WebGUI::Cache->new($session, 'myTestKey');
|
||||
|
|
@ -167,16 +185,44 @@ $optionGroup->delete;
|
|||
#
|
||||
################################################################
|
||||
|
||||
my $ldapProps = WebGUI::Test->getSmokeLDAPProps();
|
||||
$session->db->setRow('ldapLink', 'ldapLinkId', $ldapProps, $ldapProps->{ldapLinkId});
|
||||
my $ldap = WebGUI::LDAPLink->new($session, $ldapProps->{ldapLinkId});
|
||||
is($ldap->getValue("ldapLinkId"),$ldapProps->{ldapLinkId},'ldap link created properly');
|
||||
addToCleanup($ldap);
|
||||
|
||||
my @shawshank;
|
||||
|
||||
foreach my $idx (0..$#ldapTests) {
|
||||
$shawshank[$idx] = WebGUI::User->new($session, "new");
|
||||
$shawshank[$idx]->username("shawshank$idx");
|
||||
$shawshank[$idx]->authMethod("LDAP");
|
||||
my $auth = $shawshank[$idx]->authInstance;
|
||||
$auth->saveParams($shawshank[$idx]->getId,$shawshank[$idx]->authMethod,{
|
||||
connectDN => $ldapTests[$idx]->{dn},
|
||||
ldapConnection => $ldap->getValue("ldapLinkId"),
|
||||
ldapUrl => $ldap->getValue("ldapUrl"),
|
||||
});
|
||||
}
|
||||
|
||||
WebGUI::Test->usersToDelete(@shawshank);
|
||||
|
||||
my $lGroup = WebGUI::Group->new($session, 'new');
|
||||
|
||||
$lGroup->ldapGroup('LDAP group');
|
||||
is($lGroup->ldapGroup(), 'LDAP group', 'ldapGroup set and fetched correctly');
|
||||
$lGroup->ldapGroup('cn=Convicts,o=shawshank');
|
||||
is($lGroup->ldapGroup(), 'cn=Convicts,o=shawshank', 'ldapGroup set and fetched correctly');
|
||||
|
||||
$lGroup->ldapGroupProperty('LDAP group property');
|
||||
is($lGroup->ldapGroupProperty(), 'LDAP group property', 'ldapGroup set and fetched correctly');
|
||||
$lGroup->ldapGroupProperty('member');
|
||||
is($lGroup->ldapGroupProperty(), 'member', 'ldapGroup set and fetched correctly');
|
||||
|
||||
$lGroup->ldapLinkId('LDAP link id');
|
||||
is($lGroup->ldapLinkId(), 'LDAP link id', 'ldapLinkId set and fetched correctly');
|
||||
$lGroup->ldapLinkId($ldapProps->{ldapLinkId});
|
||||
is($lGroup->ldapLinkId(),$ldapProps->{ldapLinkId}, 'ldapLinkId set and fetched correctly');
|
||||
|
||||
is_deeply(
|
||||
[ (map { $lGroup->hasLDAPUser($_->getId) } @shawshank) ],
|
||||
[0, 1, 1],
|
||||
'shawshank user 2, and 3 found in lGroup users from LDAP'
|
||||
);
|
||||
|
||||
$lGroup->ldapRecursiveProperty('LDAP recursive property');
|
||||
is($lGroup->ldapRecursiveProperty(), 'LDAP recursive property', 'ldapRecursiveProperty set and fetched correctly');
|
||||
|
|
@ -242,6 +288,7 @@ cmp_bag($gB->getGroupsIn(), [$gA->getId, 3], 'Group A is in Group B');
|
|||
cmp_bag($gA->getGroupsFor(), [$gB->getId], 'Group B contains Group A');
|
||||
cmp_bag($gA->getGroupsIn(), [3], 'Admin added to group A automatically');
|
||||
|
||||
diag $gA->getId;
|
||||
$gA->addGroups([$gB->getId]);
|
||||
cmp_bag($gA->getGroupsIn(), [3], 'Not allowed to create recursive group loops');
|
||||
|
||||
|
|
@ -416,7 +463,7 @@ cmp_ok($expirationDate-time(), '>', 50, 'checking expire offset override on addU
|
|||
|
||||
################################################################
|
||||
#
|
||||
# getDatabaseUsers
|
||||
# getDatabaseUsers & hasDatabaseUsers
|
||||
#
|
||||
################################################################
|
||||
|
||||
|
|
@ -437,7 +484,17 @@ cmp_bag($mobUsers, [map {$_->userId} @mob], 'verify SQL table built correctly');
|
|||
is( $gY->databaseLinkId, 0, "Group Y's databaseLinkId is set to WebGUI");
|
||||
$gY->dbQuery(q!select userId from myUserTable!);
|
||||
is( $session->stow->get('isInGroup'), undef, 'setting dbQuery clears cached isInGroup');
|
||||
WebGUI::Cache->new($session, $gZ->getId)->delete(); ##Delete cached key for testing
|
||||
|
||||
is( $mob[0]->isInGroup($gY->getId), 1, 'mob[0] is in group Y after setting dbQuery');
|
||||
is( $mob[0]->isInGroup($gZ->getId), 1, 'mob[0] isInGroup Z');
|
||||
|
||||
ok( isIn($mob[0]->userId, @{ $gY->getAllUsers() }), 'mob[0] in list of group Y users');
|
||||
ok( !isIn($mob[0]->userId, @{ $gZ->getUsers() }), 'mob[0] not in list of group Z users');
|
||||
|
||||
ok( isIn($mob[0]->userId, @{ $gZ->getAllUsers() }), 'mob[0] in list of group Z users, recursively');
|
||||
|
||||
WebGUI::Cache->new($session, $gY->getId)->delete(); ##Delete cached key for testing
|
||||
$session->stow->delete("isInGroup");
|
||||
|
||||
my @mobIds = map { $_->userId } @mob;
|
||||
|
||||
|
|
@ -447,13 +504,17 @@ cmp_bag(
|
|||
'all mob users in list of group Y users from database'
|
||||
);
|
||||
|
||||
is( $mob[0]->isInGroup($gY->getId), 1, 'mob[0] is in group Y after setting dbQuery');
|
||||
is( $mob[0]->isInGroup($gZ->getId), 1, 'mob[0] isInGroup Z');
|
||||
$session->db->write('delete from myUserTable where userId=?',[$mob[0]->getId]);
|
||||
my $inDb = $session->db->quickScalar("select count(*) from myUserTable where userId=?",[$mob[0]->getId]);
|
||||
ok ( !$inDb, 'mob[0] no longer in myUserTable');
|
||||
WebGUI::Cache->new($session, ["groupMembers",$gY->getId])->delete; #Delete cache so we get a good test
|
||||
$session->stow->delete("isInGroup"); #Delete stow so we get a good test
|
||||
|
||||
ok( isIn($mob[0]->userId, @{ $gY->getAllUsers() }), 'mob[0] in list of group Y users');
|
||||
ok( !isIn($mob[0]->userId, @{ $gZ->getUsers() }), 'mob[0] not in list of group Z users');
|
||||
|
||||
ok( isIn($mob[0]->userId, @{ $gZ->getAllUsers() }), 'mob[0] in list of group Z users, recursively');
|
||||
is_deeply(
|
||||
[ (map { $gY->hasDatabaseUser($_->getId) } @mob) ],
|
||||
[0, 1, 1],
|
||||
'mob users 1,2 found in list of group Y users from database'
|
||||
);
|
||||
|
||||
##Karma tests
|
||||
|
||||
|
|
@ -499,6 +560,12 @@ is_deeply(
|
|||
'karma disabled in settings, no users in group'
|
||||
);
|
||||
|
||||
is_deeply(
|
||||
[ (map { $gK->hasKarmaUser($_->getId) } @chameleons) ],
|
||||
[0, 0, 0, 0],
|
||||
'karma disabled in settings, group K has no users via karma threshold'
|
||||
);
|
||||
|
||||
$session->setting->set('useKarma', 1);
|
||||
$gK->clearCaches; ##Clear cache since previous data is wrong
|
||||
|
||||
|
|
@ -508,6 +575,12 @@ is_deeply(
|
|||
'chameleons 1, 2 and 3 are in group K via karma threshold'
|
||||
);
|
||||
|
||||
is_deeply(
|
||||
[ (map { $gK->hasKarmaUser($_->getId) } @chameleons) ],
|
||||
[0, 1, 1, 1],
|
||||
'group K has chameleons 1, 2 and 3 via karma threshold'
|
||||
);
|
||||
|
||||
cmp_bag(
|
||||
$gK->getKarmaUsers,
|
||||
[ (map { $_->userId() } @chameleons[1..3]) ],
|
||||
|
|
@ -562,10 +635,20 @@ foreach my $idx (0..$#scratchTests) {
|
|||
WebGUI::Test->usersToDelete(@itchies);
|
||||
WebGUI::Test->sessionsToDelete(@sessionBank);
|
||||
|
||||
#isInGroup test
|
||||
foreach my $scratchTest (@scratchTests) {
|
||||
is($scratchTest->{user}->isInGroup($gS->getId), $scratchTest->{expect}, $scratchTest->{comment});
|
||||
}
|
||||
|
||||
WebGUI::Cache->new($session, $gS->getId)->delete(); ##Delete cached key for testing
|
||||
$session->stow->delete("isInGroup");
|
||||
|
||||
#hasScratchUser test
|
||||
foreach my $scratchTest (@scratchTests) {
|
||||
is($gS->hasScratchUser($scratchTest->{user}->getId), $scratchTest->{expect}, $scratchTest->{comment}." - hasScratchUser");
|
||||
}
|
||||
|
||||
|
||||
cmp_bag(
|
||||
$gS->getScratchUsers,
|
||||
[ (map { $_->{user}->userId() } grep { $_->{expect} } @scratchTests) ],
|
||||
|
|
@ -578,6 +661,33 @@ cmp_bag(
|
|||
'getAllUsers for group with scratch'
|
||||
);
|
||||
|
||||
{ ##Add scope to force cleanup
|
||||
|
||||
note "Checking for user Visitor session leak";
|
||||
|
||||
my $remoteSession = WebGUI::Test->newSession;
|
||||
$remoteSession->user({userId => 1});
|
||||
$remoteSession->scratch->set('remote','nok');
|
||||
|
||||
my $localScratchGroup = WebGUI::Group->new($session, 'new');
|
||||
$localScratchGroup->name("Local IP Group");
|
||||
$localScratchGroup->scratchFilter('local=ok');
|
||||
|
||||
ok !$remoteSession->user->isInGroup($localScratchGroup->getId), 'Remote Visitor fails to be in the scratch group';
|
||||
|
||||
my $localSession = WebGUI::Test->newSession;
|
||||
WebGUI::Test->addToCleanup($localScratchGroup, $remoteSession, $localSession);
|
||||
$localSession->user({userId => 1});
|
||||
$remoteSession->scratch->set('local','ok');
|
||||
$localScratchGroup->clearCaches;
|
||||
|
||||
ok $localSession->user->isInGroup($localScratchGroup->getId), 'Local Visitor is in the scratch group';
|
||||
|
||||
$remoteSession->stow->delete('isInGroup');
|
||||
ok !$remoteSession->user->isInGroup($localScratchGroup->getId), 'Remove Visitor is not in the scratch group, even though a different Visitor passed';
|
||||
|
||||
}
|
||||
|
||||
@sessionBank = ();
|
||||
my @tcps = ();
|
||||
|
||||
|
|
@ -619,10 +729,45 @@ cmp_bag(
|
|||
'getUsers for group with IP filter'
|
||||
);
|
||||
|
||||
is_deeply(
|
||||
[ (map { $gI->hasIpUser($_->{user}->getId) } @ipTests) ],
|
||||
[ (map { $_->{expect} } @ipTests) ],
|
||||
'hasIpUsers for group with IP filter'
|
||||
);
|
||||
|
||||
foreach my $ipTest (@ipTests) {
|
||||
is($ipTest->{user}->isInGroup($gI->getId), $ipTest->{expect}, $ipTest->{comment});
|
||||
}
|
||||
|
||||
{ ##Add scope to force cleanup
|
||||
|
||||
note "Checking for user Visitor session leak";
|
||||
|
||||
$ENV{REMOTE_ADDR} = '191.168.1.1';
|
||||
my $remoteSession = WebGUI::Test->newSession;
|
||||
$remoteSession->user({userId => 1});
|
||||
|
||||
my $localIpGroup = WebGUI::Group->new($session, 'new');
|
||||
$localIpGroup->name("Local IP Group");
|
||||
$localIpGroup->ipFilter('192.168.33.0/24');
|
||||
|
||||
ok !$remoteSession->user->isInGroup($localIpGroup->getId), 'Remote Visitor fails to be in the group';
|
||||
|
||||
$ENV{REMOTE_ADDR} = '192.168.33.1';
|
||||
my $localSession = WebGUI::Test->newSession;
|
||||
WebGUI::Test->addToCleanup($localIpGroup, $remoteSession, $localSession);
|
||||
$localSession->user({userId => 1});
|
||||
$localIpGroup->clearCaches;
|
||||
|
||||
ok $localSession->user->isInGroup($localIpGroup->getId), 'Local Visitor is in the group';
|
||||
|
||||
$remoteSession->stow->delete('isInGroup');
|
||||
ok !$remoteSession->user->isInGroup($localIpGroup->getId), 'Remove Visitor is not in the group, even though a different Visitor passed';
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
##Cache check.
|
||||
|
||||
my $cacheDude = WebGUI::User->new($session, "new");
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue