Make fetching of users from LDAP or MySQL faster, by only reading DB entries

that we actually need.
This commit is contained in:
Jamie Cameron
2010-09-20 15:07:42 -07:00
parent ba7b3a8da6
commit cb5cbcd970
5 changed files with 75 additions and 59 deletions

View File

@@ -11,8 +11,6 @@ Library for editing webmin users, passwords and access rights.
=cut
# XXX make read_acl faster when we only care about one user
BEGIN { push(@INC, ".."); };
use WebminCore;
&init_config();

View File

@@ -74,7 +74,8 @@ push(@ldapgrid,
&ui_textbox("ldap_groupclass", $proto eq "ldap" && $args->{'groupclass'} ?
$args->{'groupclass'} : "webminGroup",30));
push(@ldapgrid,
&ui_submit($text{'sql_schema'}, 'schema'), "");
&ui_button($text{'sql_schema'}, undef, 0,
"onClick='window.location=\"schema.cgi\"'"), "");
$ldapgrid = &ui_grid_table(\@ldapgrid, 2, 100);
print &ui_table_row(undef,

View File

@@ -8,12 +8,6 @@ $access{'pass'} || &error($text{'sql_ecannot'});
&error_setup($text{'sql_err'});
$p = $in{'proto'};
if ($in{'schema'}) {
# Redirect to schema download page
&redirect("schema.cgi");
return;
}
# Parse inputs
if ($p eq 'mysql' || $p eq 'postgresql' || $p eq 'ldap') {
gethostbyname($in{$p."_host"}) ||

View File

@@ -3023,6 +3023,7 @@ elsif ($canmode == 1) {
if ($uinfo && &password_crypt($pass, $uinfo->{'pass'})) {
# Password is valid .. but check for expiry
local $lc = $uinfo->{'lastchanges'};
print DEBUG "validate_user: Password is valid lc=$lc pass_maxdays=$config{'pass_maxdays'}\n";
if ($config{'pass_maxdays'} && $lc && !$uinfo->{'nochange'}) {
local $daysold = (time() - $lc)/(24*60*60);
print DEBUG "maxdays=$config{'pass_maxdays'} daysold=$daysold temppass=$uinfo->{'temppass'}\n";
@@ -3042,7 +3043,12 @@ elsif ($canmode == 1) {
}
return ( $user, 0, 0 );
}
elsif (!$uinfo) {
print DEBUG "validate_user: User $webminuser not found\n";
return ( undef, 0, 0 );
}
else {
print DEBUG "validate_user: User $webminuser password mismatch $pass != $uinfo->{'pass'}\n";
return ( undef, 0, 0 );
}
}
@@ -3536,10 +3542,12 @@ if ($header{'cookie'} !~ /testing=1/ && $vu &&
# check with main process for delay
if ($config{'passdelay'} && $vu) {
print DEBUG "handle_login: requesting delay vu=$vu acptip=$acptip ok=$ok\n";
print $PASSINw "delay $vu $acptip $ok\n";
<$PASSOUTr> =~ /(\d+) (\d+)/;
$blocked = $2;
sleep($1);
print DEBUG "handle_login: delay=$1 blocked=$2\n";
}
if ($ok && (!$expired ||
@@ -3547,6 +3555,7 @@ if ($ok && (!$expired ||
# Logged in OK! Tell the main process about
# the new SID
local $sid = &generate_random_id($pass);
print DEBUG "handle_login: sid=$sid\n";
print $PASSINw "new $sid $authuser $acptip\n";
# Run the post-login script, if any
@@ -3555,6 +3564,7 @@ if ($ok && (!$expired ||
# Check for a redirect URL for the user
local $rurl = &login_redirect($authuser, $pass, $host);
print DEBUG "handle_login: redirect URL rurl=$rurl\n";
if ($rurl) {
# Got one .. go to it
&write_data("HTTP/1.0 302 Moved Temporarily\r\n");
@@ -4167,7 +4177,7 @@ if ($config{'userdb'}) {
}
# Extract attributes
my $pass = $u->get_value('pass');
my $pass = $u->get_value('webminPass');
$user = { 'name' => $username,
'id' => $u->dn(),
'pass' => $pass,

View File

@@ -1689,7 +1689,7 @@ $rv .= "' value=\"...\">";
return $rv;
}
=head2 read_acl(&user-module-hash, &user-list-hash)
=head2 read_acl(&user-module-hash, &user-list-hash, [&only-users])
Reads the Webmin acl file into the given hash references. The first is indexed
by a combined key of username,module , with the value being set to 1 when
@@ -1699,9 +1699,13 @@ the value being an array ref of allowed modules.
This function is deprecated in favour of foreign_available, which performs a
more comprehensive check of module availability.
If the only-users array ref parameter is given, the results may be limited to
users in that list of names.
=cut
sub read_acl
{
my ($usermod, $userlist, $only) = @_;
if (!%main::acl_hash_cache) {
# Read from local files
local $_;
@@ -1717,52 +1721,61 @@ if (!%main::acl_hash_cache) {
}
}
close(ACL);
# Read from user DB
my $userdb = &get_userdb_string();
my ($dbh, $proto, $prefix, $args) =
$userdb ? &connect_userdb($userdb) : ( );
if (ref($dbh)) {
if ($proto eq "mysql" || $proto eq "postgresql") {
# Select usernames and modules from SQL DB
my $cmd = $dbh->prepare("select webmin_user.name,webmin_user_attr.value from webmin_user,webmin_user_attr where webmin_user.id = webmin_user_attr.id and webmin_user_attr.attr = 'modules'");
if ($cmd && $cmd->execute()) {
while(my ($user, $mods) = $cmd->fetchrow()) {
my @mods = split(/\s+/, $mods);
foreach my $m (@mods) {
$main::acl_hash_cache{$user,
$m}++;
}
$main::acl_array_cache{$user} = \@mods;
}
}
$cmd->finish() if ($cmd);
}
elsif ($proto eq "ldap") {
# Find users in LDAP
my $rv = $dbh->search(
base => $prefix,
filter => '(objectClass='.
$args->{'userclass'}.')',
scope => 'sub',
attrs => [ 'cn', 'webminModule' ]);
if ($rv && !$rv->code) {
foreach my $u ($rv->all_entries) {
my $user = $u->get_value('cn');
my @mods =$u->get_value('webminModule');
foreach my $m (@mods) {
$main::acl_hash_cache{$user,
$m}++;
}
$main::acl_array_cache{$user} = \@mods;
}
}
}
&disconnect_userdb($userdb, $dbh);
}
}
if ($_[0]) { %{$_[0]} = %main::acl_hash_cache; }
if ($_[1]) { %{$_[1]} = %main::acl_array_cache; }
%$usermod = %main::acl_hash_cache if ($usermod);
%$userlist = %main::acl_array_cache if ($userlist);
# Read from user DB
my $userdb = &get_userdb_string();
my ($dbh, $proto, $prefix, $args) =
$userdb ? &connect_userdb($userdb) : ( );
if (ref($dbh)) {
if ($proto eq "mysql" || $proto eq "postgresql") {
# Select usernames and modules from SQL DB
my $cmd = $dbh->prepare(
"select webmin_user.name,webmin_user_attr.value ".
"from webmin_user,webmin_user_attr ".
"where webmin_user.id = webmin_user_attr.id ".
"and webmin_user_attr.attr = 'modules' ".
($only ? " and webmin_user.name in (".
join(",", map { "'$_'" } @$only).")" : ""));
if ($cmd && $cmd->execute()) {
while(my ($user, $mods) = $cmd->fetchrow()) {
my @mods = split(/\s+/, $mods);
foreach my $m (@mods) {
$usermod->{$user,$m}++ if ($usermod);
}
$userlist->{$user} = \@mods if ($userlist);
}
}
$cmd->finish() if ($cmd);
}
elsif ($proto eq "ldap") {
# Find users in LDAP
my $filter = '(objectClass='.$args->{'userclass'}.')';
if ($only) {
my $ufilter =
"(|".join("", map { "(cn=$_)" } @$only).")";
$filter = "(&".$filter.$ufilter.")";
}
my $rv = $dbh->search(
base => $prefix,
filter => $filter,
scope => 'sub',
attrs => [ 'cn', 'webminModule' ]);
if ($rv && !$rv->code) {
foreach my $u ($rv->all_entries) {
my $user = $u->get_value('cn');
my @mods =$u->get_value('webminModule');
foreach my $m (@mods) {
$usermod->{$user,$m}++ if ($usermod);
}
$userlist->{$user} = \@mods if ($userlist);
}
}
}
&disconnect_userdb($userdb, $dbh);
}
}
=head2 acl_filename
@@ -2952,7 +2965,7 @@ my %foreign_module_info = &get_module_info($_[0]);
# Check list of allowed modules
my %acl;
&read_acl(\%acl, undef);
&read_acl(\%acl, undef, [ $base_remote_user ]);
return 0 if (!$acl{$base_remote_user,$_[0]} &&
!$acl{$base_remote_user,'*'});
@@ -6013,7 +6026,7 @@ if ($serv->{'fast'} || !$sn) {
if ($base_remote_user ne 'root' &&
$base_remote_user ne 'admin') {
# Need to fake up a login for the CGI!
&read_acl(undef, \%acl);
&read_acl(undef, \%acl, [ 'root' ]);
$ENV{'BASE_REMOTE_USER'} =
$ENV{'REMOTE_USER'} =
$acl{'root'} ? 'root' : 'admin';
@@ -7152,7 +7165,7 @@ returned by get_module_info.
sub get_available_module_infos
{
my (%acl, %uacl);
&read_acl(\%acl, \%uacl);
&read_acl(\%acl, \%uacl, [ $base_remote_user ]);
my $risk = $gconfig{'risk_'.$base_remote_user};
my @rv;
foreach my $minfo (&get_all_module_infos($_[0])) {