581 lines
20 KiB
Perl
Executable file
581 lines
20 KiB
Perl
Executable file
package WebGUI::Grouping;
|
|
|
|
=head1 LEGAL
|
|
|
|
-------------------------------------------------------------------
|
|
WebGUI is Copyright 2001-2005 Plain Black Corporation.
|
|
-------------------------------------------------------------------
|
|
Please read the legal notices (docs/legal.txt) and the license
|
|
(docs/license.txt) that came with this distribution before using
|
|
this software.
|
|
-------------------------------------------------------------------
|
|
http://www.plainblack.com info@plainblack.com
|
|
-------------------------------------------------------------------
|
|
|
|
=cut
|
|
|
|
use strict;
|
|
use WebGUI::Auth;
|
|
use WebGUI::Cache;
|
|
use WebGUI::DateTime;
|
|
use WebGUI::ErrorHandler;
|
|
use WebGUI::LDAPLink;
|
|
use WebGUI::Macro;
|
|
use WebGUI::Session;
|
|
use WebGUI::SQL;
|
|
use WebGUI::Utility;
|
|
|
|
=head1 NAME
|
|
|
|
Package WebGUI::Grouping
|
|
|
|
=head1 DESCRIPTION
|
|
|
|
This package provides an interface for managing WebGUI user and group groupings.
|
|
|
|
=head1 SYNOPSIS
|
|
|
|
use WebGUI::Grouping;
|
|
WebGUI::Grouping::addGroupsToGroups(\@groups, \@toGroups);
|
|
WebGUI::Grouping::addUsersToGroups(\@users, \@toGroups);
|
|
WebGUI::Grouping::deleteGroupsFromGroups(\@groups, \@fromGroups);
|
|
WebGUI::Grouping::deleteUsersFromGroups(\@users, \@fromGroups);
|
|
$arrayRef = WebGUI::Grouping::getGroupsForGroup($groupId);
|
|
$arrayRef = WebGUI::Grouping::getGroupsForUser($userId);
|
|
$arrayRef = WebGUI::Grouping::getGroupsInGroup($groupId);
|
|
$arrayRef = WebGUI::Grouping::getUsersInGroup($groupId);
|
|
$boolean = WebGUI::Grouping::isInGroup($groupId, $userId);
|
|
$boolean = WebGUI::Grouping::userGroupAdmin($userId,$groupId);
|
|
$epoch = WebGUI::Grouping::userGroupExpireDate($userId,$groupId);
|
|
|
|
=head1 METHODS
|
|
|
|
These functions are available from this package:
|
|
|
|
=cut
|
|
|
|
|
|
|
|
#-------------------------------------------------------------------
|
|
|
|
=head2 addGroupsToGroups ( groups, toGroups )
|
|
|
|
Adds groups to a group.
|
|
|
|
=head3 groups
|
|
|
|
An array reference containing the list of group ids to add.
|
|
|
|
=head3 toGroups
|
|
|
|
An array reference containing the list of group ids to add the first list to.
|
|
|
|
=cut
|
|
|
|
sub addGroupsToGroups {
|
|
delete $session{isInGroup};
|
|
foreach my $gid (@{$_[0]}) {
|
|
next if ($gid eq '1');
|
|
foreach my $toGid (@{$_[1]}) {
|
|
my ($isIn) = WebGUI::SQL->quickArray("select count(*) from groupGroupings
|
|
where groupId=".quote($gid)." and inGroup=".quote($toGid));
|
|
my $recursive = isIn($toGid, @{getGroupsInGroup($gid,1)});
|
|
unless ($isIn || $recursive) {
|
|
WebGUI::SQL->write("insert into groupGroupings (groupId,inGroup) values (".quote($gid).",".quote($toGid).")");
|
|
my $cache = WebGUI::Cache->new("groups_in_group_".$gid);
|
|
$cache->delete if (defined $cache);
|
|
$cache = WebGUI::Cache->new("groups_in_group_".$toGid);
|
|
$cache->delete if (defined $cache);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
#-------------------------------------------------------------------
|
|
|
|
=head2 addUsersToGroups ( users, groups [, expireOffset ] )
|
|
|
|
Adds users to the specified groups.
|
|
|
|
=head3 users
|
|
|
|
An array reference containing a list of users.
|
|
|
|
=head3 groups
|
|
|
|
An array reference containing a list of groups.
|
|
|
|
=head3 expireOffset
|
|
|
|
An override for the default offset of the grouping. Specified in seconds.
|
|
|
|
=cut
|
|
|
|
sub addUsersToGroups {
|
|
delete $session{isInGroup};
|
|
foreach my $gid (@{$_[1]}) {
|
|
my $expireOffset;
|
|
if ($_[2]) {
|
|
$expireOffset = $_[2];
|
|
} else {
|
|
($expireOffset) = WebGUI::SQL->quickArray("select expireOffset from groups where groupId=".quote($gid));
|
|
}
|
|
foreach my $uid (@{$_[0]}) {
|
|
next if ($uid eq '1');
|
|
my ($isIn) = WebGUI::SQL->quickArray("select count(*) from groupings where groupId=".quote($gid)." and userId=".quote($uid));
|
|
unless ($isIn) {
|
|
WebGUI::SQL->write("insert into groupings (groupId,userId,expireDate)
|
|
values (".quote($gid).", ".quote($uid).", ".(WebGUI::DateTime::time()+$expireOffset).")");
|
|
} else {
|
|
if ($_[2]) {
|
|
userGroupExpireDate($uid,$gid,(WebGUI::DateTime::time()+$expireOffset));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#-------------------------------------------------------------------
|
|
|
|
=head2 deleteGroupsFromGroups ( groups, fromGroups )
|
|
|
|
Deletes groups from these groups.
|
|
|
|
=head3 groups
|
|
|
|
An array reference containing the list of group ids to delete.
|
|
|
|
=head3 fromGroups
|
|
|
|
An array reference containing the list of group ids to delete from.
|
|
|
|
=cut
|
|
|
|
sub deleteGroupsFromGroups {
|
|
delete $session{isInGroup};
|
|
foreach my $gid (@{$_[0]}) {
|
|
foreach my $fromGid (@{$_[1]}) {
|
|
WebGUI::Cache->new("groups_in_group_".$fromGid)->delete;
|
|
WebGUI::SQL->write("delete from groupGroupings where groupId=".quote($gid)." and inGroup=".quote($fromGid));
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
#-------------------------------------------------------------------
|
|
|
|
=head2 deleteUsersFromGroups ( users, groups )
|
|
|
|
Deletes a list of users from the specified groups.
|
|
|
|
=head3 users
|
|
|
|
An array reference containing a list of users.
|
|
|
|
=head3 groups
|
|
|
|
An array reference containing a list of groups.
|
|
|
|
=cut
|
|
|
|
sub deleteUsersFromGroups {
|
|
delete $session{isInGroup};
|
|
foreach my $gid (@{$_[1]}) {
|
|
foreach my $uid (@{$_[0]}) {
|
|
WebGUI::SQL->write("delete from groupings where groupId=".quote($gid)." and userId=".quote($uid));
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
#-------------------------------------------------------------------
|
|
|
|
=head2 getGroupsForGroup ( groupId )
|
|
|
|
Returns an array reference containing a list of groups the specified group is in.
|
|
|
|
=head3 groupId
|
|
|
|
A unique identifier for the group.
|
|
|
|
=cut
|
|
|
|
sub getGroupsForGroup {
|
|
return WebGUI::SQL->buildArrayRef("select inGroup from groupGroupings where groupId=".quote($_[0]));
|
|
}
|
|
|
|
|
|
#-------------------------------------------------------------------
|
|
|
|
=head2 getGroupsForUser ( userId [ , withoutExpired ] )
|
|
|
|
Returns an array reference containing a list of groups the specified user is in.
|
|
|
|
=head3 userId
|
|
|
|
A unique identifier for the user.
|
|
|
|
=head3 withoutExpired
|
|
|
|
If set to "1" then the listing will not include expired groupings. Defaults to "0".
|
|
|
|
=cut
|
|
|
|
sub getGroupsForUser {
|
|
my $userId = shift;
|
|
my $withoutExpired = shift;
|
|
my $clause = "and expireDate>".time() if ($withoutExpired);
|
|
if ($userId eq "") {
|
|
return [];
|
|
} elsif (exists $session{gotGroupsForUser}{$userId}) {
|
|
return $session{gotGroupsForUser}{$userId};
|
|
} else {
|
|
my @groups = WebGUI::SQL->buildArray("select groupId from groupings where userId=".quote($userId)." $clause");
|
|
foreach my $gid (@groups) {
|
|
$session{isInGroup}{$userId}{$gid} = 1;
|
|
}
|
|
$session{gotGroupsForUser}{$userId} = \@groups unless ($session{config}{disableCache});
|
|
return \@groups;
|
|
}
|
|
}
|
|
|
|
|
|
#-------------------------------------------------------------------
|
|
|
|
=head2 getGroupsInGroup ( groupId [, recursive ] )
|
|
|
|
Returns an array reference containing a list of groups that belong to the specified group.
|
|
|
|
=head3 groupId
|
|
|
|
A unique identifier for the group.
|
|
|
|
=head3 recursive
|
|
|
|
A boolean value to determine whether the method should return the groups directly in the group, or to follow the entire groups of groups hierarchy. Defaults to "0".
|
|
|
|
=cut
|
|
|
|
|
|
sub getGroupsInGroup {
|
|
my $groupId = shift;
|
|
my $isRecursive = shift;
|
|
my $loopCount = shift;
|
|
if ($isRecursive && exists $session{gotGroupsInGroup}{recursive}{$groupId}) {
|
|
return $session{gotGroupsInGroup}{recursive}{$groupId};
|
|
} elsif (exists $session{gotGroupsInGroup}{recursive}{$groupId}) {
|
|
return $session{gotGroupsInGroup}{direct}{$groupId};
|
|
}
|
|
my $groups = WebGUI::Cache->new("groups_in_group_".$groupId)->get;
|
|
unless (defined $groups) {
|
|
$groups = WebGUI::SQL->buildArrayRef("select groupId from groupGroupings where inGroup=".quote($groupId));
|
|
WebGUI::Cache->new("groups_in_group_".$groupId)->set($groups);
|
|
}
|
|
if ($isRecursive) {
|
|
$loopCount++;
|
|
if ($loopCount > 99) {
|
|
WebGUI::ErrorHandler::fatal("Endless recursive loop detected while determining".
|
|
" groups in group.\nRequested groupId: ".$groupId."\nGroups in that group: ".join(",",@$groups));
|
|
}
|
|
my @groupsOfGroups = @$groups;
|
|
foreach my $group (@$groups) {
|
|
my $gog = getGroupsInGroup($group,1,$loopCount);
|
|
push(@groupsOfGroups, @$gog);
|
|
}
|
|
$session{gotGroupsInGroup}{recursive}{$groupId} = \@groupsOfGroups unless ($session{config}{disableCache});
|
|
return \@groupsOfGroups;
|
|
}
|
|
$session{gotGroupsInGroup}{direct}{$groupId} = $groups;
|
|
return $groups;
|
|
}
|
|
|
|
|
|
#-------------------------------------------------------------------
|
|
|
|
=head2 getUsersInGroup ( groupId [, recursive, withoutExpired ] )
|
|
|
|
Returns an array reference containing a list of users that belong to the specified group.
|
|
|
|
=head3 groupId
|
|
|
|
A unique identifier for the group.
|
|
|
|
=head3 recursive
|
|
|
|
A boolean value to determine whether the method should return the users directly in the group or to follow the entire groups of groups hierarchy. Defaults to "0".
|
|
|
|
=head3 withoutExpired
|
|
|
|
A boolean that if set true will return the users list minus the expired groupings.
|
|
|
|
=cut
|
|
|
|
sub getUsersInGroup {
|
|
my $groupId = shift;
|
|
my $recursive = shift;
|
|
my $withoutExpired = shift;
|
|
my $clause;
|
|
if ($withoutExpired) {
|
|
$clause = "expireDate > ".time()." and ";
|
|
}
|
|
$clause .= "(groupId=".quote($groupId);
|
|
if ($recursive) {
|
|
my $groups = getGroupsInGroup($groupId,1);
|
|
if ($#$groups >= 0) {
|
|
if ($withoutExpired) {
|
|
foreach my $groupId (@$groups) {
|
|
$clause .= " OR (groupId = ".quote($groupId)." AND expireDate > ".time().") ";
|
|
}
|
|
} else {
|
|
$clause .= " OR groupId IN (".quoteAndJoin($groups).")";
|
|
}
|
|
}
|
|
}
|
|
$clause .= ")";
|
|
return WebGUI::SQL->buildArrayRef("select userId from groupings where $clause");
|
|
}
|
|
|
|
|
|
#-------------------------------------------------------------------
|
|
|
|
=head2 isInGroup ( [ groupId , userId ] )
|
|
|
|
Returns a boolean (0|1) value signifying that the user has the required privileges. Always returns true for Admins.
|
|
|
|
=head3 groupId
|
|
|
|
The group that you wish to verify against the user. Defaults to group with Id 3 (the Admin group).
|
|
|
|
=head3 userId
|
|
|
|
The user that you wish to verify against the group. Defaults to the currently logged in user.
|
|
|
|
=cut
|
|
|
|
sub isInGroup {
|
|
my (@data, %group, $groupId);
|
|
my ($gid, $uid, $secondRun) = @_;
|
|
$gid = 3 unless (defined $gid);
|
|
$uid = $session{user}{userId} if ($uid eq "");
|
|
### 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 0 if ($uid eq '1'); #Visitor is in no other groups
|
|
return 1 if ($uid eq '3'); #Admin is in every group
|
|
return 1 if ($gid eq '2' && $uid ne '1'); # if you're not a visitor, then you're a registered user
|
|
### Look to see if we've already looked up this group.
|
|
if ($session{isInGroup}{$uid}{$gid} eq '1') {
|
|
return 1;
|
|
} elsif ($session{isInGroup}{$uid}{$gid} eq "0") {
|
|
return 0;
|
|
}
|
|
### Lookup the actual groupings.
|
|
unless ($secondRun) { # don't look up user groups if we've already done it once.
|
|
my $groups = WebGUI::Grouping::getGroupsForUser($uid,1);
|
|
foreach (@{$groups}) {
|
|
$session{isInGroup}{$uid}{$_} = 1 unless ($session{config}{disableCache});
|
|
}
|
|
if ($session{isInGroup}{$uid}{$gid} eq '1') {
|
|
return 1;
|
|
}
|
|
}
|
|
### Get data for auxillary checks.
|
|
tie %group, 'Tie::CPHash';
|
|
%group = WebGUI::SQL->quickHash("select karmaThreshold,ipFilter,scratchFilter,databaseLinkId,dbQuery,dbCacheTimeout,ldapGroup,ldapGroupProperty,ldapRecursiveProperty from groups where groupId=".quote($gid));
|
|
### Check IP Address
|
|
if ($group{ipFilter} ne "") {
|
|
$group{ipFilter} =~ s/\t//g;
|
|
$group{ipFilter} =~ s/\r//g;
|
|
$group{ipFilter} =~ s/\n//g;
|
|
$group{ipFilter} =~ s/\s//g;
|
|
$group{ipFilter} =~ s/\./\\\./g;
|
|
my @ips = split(";",$group{ipFilter});
|
|
foreach my $ip (@ips) {
|
|
if ($session{env}{REMOTE_ADDR} =~ /^$ip/) {
|
|
$session{isInGroup}{$uid}{$gid} = 1 unless ($session{config}{disableCache});
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
### Check Scratch Variables
|
|
if ($group{scratchFilter} ne "") {
|
|
$group{scratchFilter} =~ s/\t//g;
|
|
$group{scratchFilter} =~ s/\r//g;
|
|
$group{scratchFilter} =~ s/\n//g;
|
|
$group{scratchFilter} =~ s/\s//g;
|
|
my @vars = split(";",$group{scratchFilter});
|
|
foreach my $var (@vars) {
|
|
my ($name, $value) = split(/\=/,$var);
|
|
if ($session{scratch}{$name} eq $value) {
|
|
$session{isInGroup}{$uid}{$gid} = 1 unless ($session{config}{disableCache});
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
### Check karma levels.
|
|
if ($session{setting}{useKarma}) {
|
|
my $karma;
|
|
if ($uid eq $session{user}{userId}) {
|
|
$karma = $session{user}{karma};
|
|
} else {
|
|
($karma) = WebGUI::SQL->quickHash("select karma from users where userId=".quote($uid));
|
|
}
|
|
if ($karma >= $group{karmaThreshold}) {
|
|
$session{isInGroup}{$uid}{$gid} = 1 unless ($session{config}{disableCache});
|
|
return 1;
|
|
}
|
|
}
|
|
### Check external database
|
|
if ($group{dbQuery} ne "" && $group{databaseLinkId}) {
|
|
# skip if not logged in and query contains a User macro
|
|
unless ($group{dbQuery} =~ /\^User/i && $uid eq '1') {
|
|
my $dbLink = WebGUI::DatabaseLink->new($group{databaseLinkId});
|
|
my $dbh = $dbLink->dbh;
|
|
if (defined $dbh) {
|
|
if ($group{dbQuery} =~ /select 1/i) {
|
|
my $query = $group{dbQuery};
|
|
WebGUI::Macro::process(\$query);
|
|
$group{dbQuery} = $query;
|
|
my $sth = WebGUI::SQL->unconditionalRead($group{dbQuery},$dbh);
|
|
unless ($sth->errorCode < 1) {
|
|
WebGUI::ErrorHandler::warn("There was a problem with the database query for group ID $gid.");
|
|
} else {
|
|
my ($result) = $sth->array;
|
|
if ($result == 1) {
|
|
$session{isInGroup}{$uid}{$gid} = 1 unless ($session{config}{disableCache});
|
|
if ($group{dbCacheTimeout} > 0) {
|
|
WebGUI::Grouping::deleteUsersFromGroups([$uid],[$gid]);
|
|
WebGUI::Grouping::addUsersToGroups([$uid],[$gid],$group{dbCacheTimeout});
|
|
}
|
|
} else {
|
|
$session{isInGroup}{$uid}{$gid} = 0 unless ($session{config}{disableCache});
|
|
WebGUI::Grouping::deleteUsersFromGroups([$uid],[$gid]) if ($group{dbCacheTimeout} > 0);
|
|
}
|
|
}
|
|
$sth->finish;
|
|
} else {
|
|
WebGUI::ErrorHandler::warn("Database query for group ID $gid must use 'select 1'");
|
|
}
|
|
$dbLink->disconnect;
|
|
return 1 if ($session{isInGroup}{$uid}{$gid});
|
|
}
|
|
}
|
|
}
|
|
|
|
### Check external database
|
|
if ($group{ldapGroup} ne "" && $group{ldapGroupProperty} ne "") {
|
|
# skip if not logged in
|
|
unless($uid eq '1') {
|
|
my $u = WebGUI::User->new($uid);
|
|
# skip if user is not set to LDAP
|
|
if($u->authMethod eq "LDAP") {
|
|
my $auth = WebGUI::Auth->new("LDAP",$uid);
|
|
my $params = $auth->getParams();
|
|
my $ldapLink = WebGUI::LDAPLink->new($params->{ldapConnection});
|
|
if($ldapLink ne "") {
|
|
my $people = [];
|
|
if($group{ldapRecursiveProperty}) {
|
|
$ldapLink->recurseProperty($group{ldapGroup},$people,$group{ldapGroupProperty},$group{ldapRecursiveProperty});
|
|
}else {
|
|
$people = $ldapLink->getProperty($group{ldapGroup},$group{ldapGroupProperty});
|
|
}
|
|
|
|
if(isIn($params->{connectDN},@{$people})) {
|
|
$session{isInGroup}{$uid}{$gid} = 1 unless ($session{config}{disableCache});
|
|
if ($group{dbCacheTimeout} > 10) {
|
|
WebGUI::Grouping::deleteUsersFromGroups([$uid],[$gid]);
|
|
WebGUI::Grouping::addUsersToGroups([$uid],[$gid],$group{dbCacheTimeout});
|
|
}
|
|
} else {
|
|
$session{isInGroup}{$uid}{$gid} = 0 unless ($session{config}{disableCache});
|
|
WebGUI::Grouping::deleteUsersFromGroups([$uid],[$gid]) if ($group{dbCacheTimeout} > 10);
|
|
}
|
|
$ldapLink->unbind;
|
|
return 1 if ($session{isInGroup}{$uid}{$gid});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
### Check for groups of groups.
|
|
my $groups = WebGUI::Grouping::getGroupsInGroup($gid,1);
|
|
foreach (@{$groups}) {
|
|
$session{isInGroup}{$uid}{$_} = isInGroup($_, $uid, 1);
|
|
if ($session{isInGroup}{$uid}{$_}) {
|
|
$session{isInGroup}{$uid}{$gid} = 1 unless ($session{config}{disableCache}); # cache current group also so we don't have to do the group in group check again
|
|
return 1;
|
|
}
|
|
}
|
|
$session{isInGroup}{$uid}{$gid} = 0 unless ($session{config}{disableCache});
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#-------------------------------------------------------------------
|
|
|
|
=head2 userGroupAdmin ( userId, groupId [, value ] )
|
|
|
|
Returns a 1 or 0 depending upon whether the user is a sub-admin for this group.
|
|
|
|
=head3 userId
|
|
|
|
An integer that is the unique identifier for a user.
|
|
|
|
=head3 groupId
|
|
|
|
An integer that is the unique identifier for a group.
|
|
|
|
=head3 value
|
|
|
|
If specified the admin flag will be set to this value.
|
|
|
|
=cut
|
|
|
|
sub userGroupAdmin {
|
|
if ($_[2] ne "") {
|
|
WebGUI::SQL->write("update groupings set groupAdmin=".quote($_[2])." where groupId=".quote($_[1])." and userId=".quote($_[0]));
|
|
return $_[2];
|
|
} else {
|
|
my ($admin) = WebGUI::SQL->quickArray("select groupAdmin from groupings where groupId=".quote($_[1])." and userId=".quote($_[0]));
|
|
return $admin;
|
|
}
|
|
}
|
|
|
|
#-------------------------------------------------------------------
|
|
|
|
=head2 userGroupExpireDate ( userId, groupId [, epoch ] )
|
|
|
|
Returns the epoch date that this grouping will expire.
|
|
|
|
=head3 userId
|
|
|
|
An integer that is the unique identifier for a user.
|
|
|
|
=head3 groupId
|
|
|
|
An integer that is the unique identifier for a group.
|
|
|
|
=head3 epoch
|
|
|
|
If specified the expire date will be set to this value.
|
|
|
|
=cut
|
|
|
|
sub userGroupExpireDate {
|
|
if ($_[2]) {
|
|
WebGUI::SQL->write("update groupings set expireDate=".quote($_[2])." where groupId=".quote($_[1])." and userId=".quote($_[0]));
|
|
return $_[2];
|
|
} else {
|
|
my ($expireDate) = WebGUI::SQL->quickArray("select expireDate from groupings where groupId=".quote($_[1])." and userId=".quote($_[0]));
|
|
return $expireDate;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
1;
|
|
|