Make fetching of users from LDAP or MySQL faster, by only reading DB entries
authorJamie Cameron <jcameron@webmin.com>
Mon, 20 Sep 2010 22:07:42 +0000 (15:07 -0700)
committerJamie Cameron <jcameron@webmin.com>
Mon, 20 Sep 2010 22:07:42 +0000 (15:07 -0700)
that we actually need.

acl/acl-lib.pl
acl/edit_sql.cgi
acl/save_sql.cgi
miniserv.pl
web-lib-funcs.pl

index a5781af..55bc99a 100755 (executable)
@@ -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();
index ef68ecd..9dd9c85 100755 (executable)
@@ -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,
index 4fe7bc5..c516133 100755 (executable)
@@ -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"}) ||
index 21f4dec..94e47f7 100755 (executable)
@@ -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,
index f84281f..d48d69e 100755 (executable)
@@ -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);
+       }
+%$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'");
-                       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;
+# 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 $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;
+               $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);
                }
+       &disconnect_userdb($userdb, $dbh);
        }
-if ($_[0]) { %{$_[0]} = %main::acl_hash_cache; }
-if ($_[1]) { %{$_[1]} = %main::acl_array_cache; }
 }
 
 =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])) {