Use faster functions to get specific users and groups
authorJamie Cameron <jcameron@webmin.com>
Mon, 20 Sep 2010 01:06:54 +0000 (18:06 -0700)
committerJamie Cameron <jcameron@webmin.com>
Mon, 20 Sep 2010 01:06:54 +0000 (18:06 -0700)
12 files changed:
acl/acl-lib.pl
acl/acl_security.pl
acl/backup_config.pl
acl/cgi_args.pl
acl/edit_acl.cgi
acl/edit_user.cgi
acl/lang/en
acl/log_parser.pl
acl/postinstall.pl
acl/save_acl.cgi
acl/save_user.cgi
acl/useradmin_update.pl

index 9020ca5..c000b68 100755 (executable)
@@ -11,6 +11,9 @@ Library for editing webmin users, passwords and access rights.
 
 =cut
 
+# XXX use get_user / get_group
+# XXX make read_acl faster when we only care about one user
+
 BEGIN { push(@INC, ".."); };
 use WebminCore;
 &init_config();
@@ -18,7 +21,7 @@ do 'md5-lib.pl';
 %access = &get_module_acl();
 $access{'switch'} = 0 if (&is_readonly_mode());
 
-=head2 list_users
+=head2 list_users([&only-users])
 
 Returns a list of hashes containing Webmin user details. Useful keys include :
 
@@ -30,9 +33,13 @@ Returns a list of hashes containing Webmin user details. Useful keys include :
 
 =item theme - Custom theme, if any
 
+If the only-users parameter is given, limit the list to just users with
+those usernames.
+
 =cut
 sub list_users
 {
+my ($only) = @_;
 my (%miniserv, @rv, %acl, %logout);
 local %_;
 &read_acl(undef, \%acl);
@@ -47,7 +54,8 @@ while(<PWFILE>) {
        s/\r|\n//g;
        local @user = split(/:/, $_);
        if (@user) {
-               local(%user);
+               my %user;
+               next if ($only && &indexof($user[0], @$only) < 0);
                $user{'name'} = $user[0];
                $user{'pass'} = $user[1];
                $user{'sync'} = $user[2];
@@ -97,7 +105,9 @@ if ($miniserv{'userdb'}) {
        if ($proto eq "mysql" || $proto eq "postgresql") {
                # Fetch users with SQL
                my %userid;
-               my $cmd = $dbh->prepare("select id,name,pass from webmin_user");
+               my $cmd = $dbh->prepare("select id,name,pass from webmin_user".
+                       ($only ? " where name in (".
+                                join(",", map { "'$_'" } @$only).")" : ""));
                $cmd && $cmd->execute() ||
                        &error("Failed to query users : ".$dbh->errstr);
                while(my ($id, $name, $pass) = $cmd->fetchrow()) {
@@ -112,7 +122,9 @@ if ($miniserv{'userdb'}) {
 
                # Add user attributes
                my $cmd = $dbh->prepare(
-                       "select id,attr,value from webmin_user_attr");
+                       "select id,attr,value from webmin_user_attr ".
+                       ($only && %userid ?
+                        " where id in (".join(",", keys %userid).")" : ""));
                $cmd && $cmd->execute() ||
                        &error("Failed to query user attrs : ".$dbh->errstr);
                while(my ($id, $attr, $value) = $cmd->fetchrow()) {
@@ -126,9 +138,15 @@ if ($miniserv{'userdb'}) {
                }
        elsif ($proto eq "ldap") {
                # Find users with LDAP query
+               my $filter = '(objectClass='.$args->{'userclass'}.')';
+               if ($only) {
+                       my $ufilter =
+                               "(|".join("", map { "(cn=$_)" } @$only).")";
+                       $filter = "(&".$filter.$ufilter.")";
+                       }
                my $rv = $dbh->search(
                        base => $prefix,
-                       filter => '(objectClass='.$args->{'userclass'}.')',
+                       filter => $filter,
                        scope => 'sub');
                if (!$rv || $rv->code) {
                        &error("Failed to search users : ".
@@ -156,6 +174,20 @@ if ($miniserv{'userdb'}) {
 return @rv;
 }
 
+=head2 get_user(username)
+
+Returns a hash ref of details of the user with some name, in the same format
+as list_users. Returns undef if not found
+
+=cut
+sub get_user
+{
+my ($username) = @_;
+my @rv = &list_users([ $username ]);
+my ($user) = grep { $_->{'name'} eq $username } @rv;
+return $user;
+}
+
 =head2 list_groups
 
 Returns a list of hashes, one per Webmin group. Group membership is stored in
@@ -256,6 +288,20 @@ if ($miniserv{'userdb'}) {
 return @rv;
 }
 
+=head2 get_group(groupname)
+
+Returns a hash ref of details of the group with some name, in the same format
+as list_groups. Returns undef if not found
+
+=cut
+sub get_group
+{
+my ($groupname) = @_;
+my @rv = &list_groups([ $groupname ]);
+my ($group) = grep { $_->{'name'} eq $groupname } @rv;
+return $group;
+}
+
 =head2 list_modules
 
 Returns a list of the dirs of all modules available on this system.
index 6d8f785..115a8b4 100755 (executable)
@@ -5,7 +5,7 @@ do 'acl-lib.pl';
 # Output HTML for editing security options for the acl module
 sub acl_security_form
 {
-local $o = $_[0];
+my ($o) = @_;
 print "<tr> <td valign=top><b>$text{'acl_users'}</b></td>\n";
 print "<td valign=top>\n";
 printf "<input type=radio name=users_def value=1 %s> %s\n",
index 5edd98a..062b596 100755 (executable)
@@ -5,19 +5,20 @@ do 'acl-lib.pl';
 # Returns files and directories that can be backed up
 sub backup_config_files
 {
-local @rv;
+my @rv;
 
 # Add primary user and group files
-local %miniserv;
+my %miniserv;
 &get_miniserv_config(\%miniserv);
 push(@rv, $miniserv{'userfile'});
 push(@rv, &acl_filename());
 
 # Add all .acl files for users and groups
-local $u;
-foreach $u (&list_users(), &list_groups()) {
-       push(@rv, "$config_directory/$u->{'name'}.acl",
-                 glob("$config_directory/*/$u->{'name'}.acl"));
+foreach my $u (&list_users(), &list_groups()) {
+       if (!$u->{'proto'}) {
+               push(@rv, "$config_directory/$u->{'name'}.acl",
+                         glob("$config_directory/*/$u->{'name'}.acl"));
+               }
        }
 
 # Add /etc/webmin/config
@@ -52,10 +53,11 @@ return undef;
 sub pre_restore
 {
 # Remove user and group .acl files
-local $u;
-foreach $u (&list_users(), &list_groups()) {
-       unlink("$config_directory/$u->{'name'}.acl",
-              glob("$config_directory/*/$u->{'name'}.acl"));
+foreach my $u (&list_users(), &list_groups()) {
+       if (!$u->{'proto'}) {
+               unlink("$config_directory/$u->{'name'}.acl",
+                      glob("$config_directory/*/$u->{'name'}.acl"));
+               }
        }
 return undef;
 }
@@ -65,14 +67,13 @@ return undef;
 sub post_restore
 {
 # Splice global config entries for users into real config
-local %aclbackup;
+my %aclbackup;
 &read_file("$config_directory/config.aclbackup", \%aclbackup);
 unlink("$config_directory/config.aclbackup");
-local $k;
-foreach $k (keys %gconfig) {
+foreach my $k (keys %gconfig) {
        delete($gconfig{$k}) if ($k =~ /^(lang_|notabs_|skill_|risk_|theme_|ownmods_)/);
        }
-foreach $k (keys %aclbackup) {
+foreach my $k (keys %aclbackup) {
        $gconfig{$k} = $aclbackup{$k} if ($k =~ /^(lang_|notabs_|skill_|risk_|theme_|ownmods_)/);
        }
 &write_file("$config_directory/config", \%gconfig);
@@ -81,7 +82,7 @@ foreach $k (keys %aclbackup) {
 %aclbackup = ( );
 &read_file("$config_directory/miniserv.conf.aclbackup", \%aclbackup);
 unlink("$config_directory/miniserv.conf.aclbackup");
-local %miniserv;
+my %miniserv;
 &get_miniserv_config(\%miniserv);
 foreach $k (keys %miniserv) {
        delete($miniserv{$k}) if ($k =~ /^(preroot_)/);
index dd989b8..c9f1836 100755 (executable)
@@ -5,20 +5,17 @@ sub cgi_args
 {
 my ($cgi) = @_;
 if ($cgi eq 'edit_user.cgi') {
-       local ($u) = grep { &can_edit_user($u->{'name'}) }
-                         &list_users();
+       my ($u) = grep { &can_edit_user($u->{'name'}) } &list_users();
        return $u ? 'user='.&urlize($u->{'name'}) :
               $access{'create'} ? '' : 'none';
        }
 elsif ($cgi eq 'edit_group.cgi') {
-       local ($u) = grep { &can_edit_group($u->{'name'}) }
-                         &list_groups();
+       my ($u) = grep { &can_edit_group($u->{'name'}) } &list_groups();
        return $u ? 'group='.&urlize($u->{'name'}) :
               $access{'groups'} ? '' : 'none';
        }
 elsif ($cgi eq 'edit_acl.cgi') {
-       local ($u) = grep { &can_edit_user($u->{'name'}) }
-                         &list_users();
+       my ($u) = grep { &can_edit_user($u->{'name'}) } &list_users();
        if ($u && @{$u->{'modules'}}) {
                return 'user='.&urlize($u->{'name'}).
                       '&mod='.$u->{'modules'}->[0];
index 867cb88..cf355ee 100755 (executable)
@@ -10,9 +10,7 @@ if ($in{'group'}) {
        $who = $in{'group'};
        }
 else {
-       foreach $u (&list_users()) {
-               $me = $u if ($u->{'name'} eq $base_remote_user);
-               }
+       $me = &get_user($base_remote_user);
        @mcan = $access{'mode'} == 1 ? @{$me->{'modules'}} :
                $access{'mode'} == 2 ? split(/\s+/, $access{'mods'}) :
                                       ( &list_modules() , "" );
index 3818bf9..4671a72 100755 (executable)
@@ -10,31 +10,23 @@ if ($in{'user'}) {
        # Editing an existing user
        &can_edit_user($in{'user'}) || &error($text{'edit_euser'});
        &ui_print_header(undef, $text{'edit_title'}, "");
-       foreach $u (&list_users()) {
-               if ($u->{'name'} eq $in{'user'}) {
-                       %user = %$u;
-                       }
-               if ($u->{'name'} eq $base_remote_user) {
-                       $me = $u;
-                       }
-               }
+       $u = &get_user($in{'user'});
+       $u || &error($text{'edit_egone'});
+       %user = %$u;
        }
 else {
        # Creating a new user
        $access{'create'} || &error($text{'edit_ecreate'});
        &ui_print_header(undef, $text{'edit_title2'}, "");
-       foreach $u (&list_users()) {
-               if ($u->{'name'} eq $in{'clone'}) {
-                       # Initial settings come from clone
-                       %user = %$u;
-                       delete($user{'name'});
-                       }
-               if ($u->{'name'} eq $base_remote_user) {
-                       $me = $u;
-                       }
+       if ($in{'clone'}) {
+               # Initial settings come from clone
+               $u = &get_user($in{'clone'});
+               %user = %$u;
+               delete($user{'name'});
                }
        $user{'skill'} = $user{'risk'} = 'high' if ($in{'risk'});
        }
+$me = &get_user($base_remote_user);
 
 # Give up if readonly
 if ($user{'readonly'} && !$in{'readwrite'}) {
index d819615..3906ccc 100644 (file)
@@ -69,6 +69,7 @@ edit_risk_medium=Admin user
 edit_risk_low=Normal user
 edit_groupmods=(In addition to modules from group)
 edit_euser=You are not allowed to edit this user
+edit_egone=Selected user no longer exists!
 edit_ecreate=You are not allowed to create users
 edit_theme=Personal UI theme
 edit_themeglobal=From Webmin Configuration
index 43180e5..ae2870e 100755 (executable)
@@ -7,8 +7,8 @@ do 'acl-lib.pl';
 # Converts logged information from this module into human-readable form
 sub parse_webmin_log
 {
-local ($user, $script, $action, $type, $object, $p) = @_;
-local $g = $type eq 'group' ? "_g" : "";
+my ($user, $script, $action, $type, $object, $p) = @_;
+my $g = $type eq 'group' ? "_g" : "";
 if ($action eq 'modify') {
        if ($p->{'old'} ne $p->{'name'}) {
                return &text('log_rename'.$g, "<tt>$p->{'old'}</tt>",
index f0c4b16..431a0e3 100755 (executable)
@@ -5,25 +5,23 @@ require 'acl-lib.pl';
 sub module_install
 {
 # Fix up .acl files
-local @mods = &get_all_module_infos();
-local %isuser = map { $_->{'name'}, 1 } &list_users();
-local $g;
-foreach $g (&list_groups()) {
+my @mods = &get_all_module_infos();
+my %isuser = map { $_->{'name'}, 1 } &list_users();
+foreach my $g (&list_groups()) {
        next if ($isuser{$g->{'name'}});
-       local $m;
-       foreach $m (@mods) {
+       next if ($g->{'proto'});
+       foreach my $m (@mods) {
                if (-r "$config_directory/$m->{'dir'}/$g->{'name'}.acl") {
-                       print STDERR "renaming $config_directory/$m->{'dir'}/$g->{'name'}.acl $config_directory/$m->{'dir'}/$g->{'name'}.gacl\n";
                        rename("$config_directory/$m->{'dir'}/$g->{'name'}.acl",
-                              "$config_directory/$m->{'dir'}/$g->{'name'}.gacl");
+                            "$config_directory/$m->{'dir'}/$g->{'name'}.gacl");
                        }
                }
        }
 
 # Update sub-groups in webmin.groups file to use @ names
-foreach $g (&list_groups()) {
-       local ($u, @newmembers, $any);
-       foreach $u (@{$g->{'members'}}) {
+foreach my $g (&list_groups()) {
+       my (@newmembers, $any);
+       foreach my $u (@{$g->{'members'}}) {
                if ($u !~ /^\@/ && !$isuser{$u}) {
                        push(@newmembers, '@'.$u);
                        $any = 1;
@@ -34,7 +32,6 @@ foreach $g (&list_groups()) {
                }
        $g->{'members'} = \@newmembers;
        if ($any) {
-               print STDERR "Changing members of $g->{'name'} to ",join(" ", @newmembers),"\n";
                &modify_group($g->{'name'}, $g);
                }
        }
index bc0b29d..0965ad1 100755 (executable)
@@ -9,9 +9,7 @@ if ($in{'_acl_group'}) {
        $who = $in{'_acl_group'};
        }
 else {
-       foreach $u (&list_users()) {
-               $me = $u if ($u->{'name'} eq $base_remote_user);
-               }
+       $me = &get_user($base_remote_user);
        @mcan = $access{'mode'} == 1 ? @{$me->{'modules'}} :
                $access{'mode'} == 2 ? split(/\s+/, $access{'mods'}) :
                                       ( &list_modules(), "" );
index c4c8315..60cf1d5 100755 (executable)
@@ -26,14 +26,12 @@ elsif ($in{'but_delete'}) {
        }
 
 # Get the user object
-@ulist = &list_users();
 if ($in{'old'}) {
        %user = ( );
        $in{'name'} = $in{'old'} if (!$access{'rename'});
        &can_edit_user($in{'old'}) || &error($text{'save_euser'});
-       foreach $u (@ulist) {
-               $old = $u if ($u->{'name'} eq $in{'old'});
-               }
+       $old = &get_user($in{'old'});
+       $old || &error($text{'edit_egone'});
        $user{'proto'} = $old->{'proto'};
        $user{'id'} = $old->{'id'};
        }
@@ -47,11 +45,8 @@ $in{'name'} =~ /^[A-z0-9\-\_\.\@]+$/ && $in{'name'} !~ /^\@/ ||
        &error(&text('save_ename', $in{'name'}));
 $in{'name'} eq 'webmin' && &error($text{'save_enamewebmin'});
 if (!$in{'old'} || $in{'old'} ne $in{'name'}) {
-       foreach $u (@ulist) {
-               if ($u->{'name'} eq $in{'name'}) {
-                       &error(&text('save_edup', $in{'name'}));
-                       }
-               }
+       $clash = &get_user($in{'name'});
+       $clash && &error(&text('save_edup', $in{'name'}));
        }
 !$access{'logouttime'} || $in{'logouttime_def'} ||
        $in{'logouttime'} =~ /^\d+$/ || &error($text{'save_elogouttime'});
@@ -274,7 +269,7 @@ else {
        # Password synchronization (deprecated)
        &foreign_check("useradmin") || &error($text{'save_eos'});
        &foreign_require("useradmin", "user-lib.pl");
-       foreach $uu (&foreign_call("useradmin", "list_users")) {
+       foreach $uu (&useradmin::list_users()) {
                $user{'pass'} = $uu->{'pass'}
                        if ($uu->{'user'} eq $in{'name'});
                }
@@ -330,14 +325,17 @@ if ($in{'temp'}) {
 if ($in{'old'}) {
        # update user and all ACLs
        &modify_user($in{'old'}, \%user);
-       foreach $u (&list_users()) {
-               %uaccess = &get_module_acl($u->{'name'});
-               local @au = split(/\s+/, $uaccess{'users'});
-               local $idx = &indexof($in{'old'}, @au);
-               if ($idx != -1) {
-                       $au[$idx] = $in{'name'};
-                       $uaccess{'users'} = join(" ", @au);
-                       &save_module_acl(\%uaccess, $u->{'name'});
+       if ($in{'old'} ne $user{'name'}) {
+               # Change username in other user's ACLs
+               foreach $u (&list_users()) {
+                       %uaccess = &get_module_acl($u->{'name'});
+                       local @au = split(/\s+/, $uaccess{'users'});
+                       local $idx = &indexof($in{'old'}, @au);
+                       if ($idx != -1) {
+                               $au[$idx] = $in{'name'};
+                               $uaccess{'users'} = join(" ", @au);
+                               &save_module_acl(\%uaccess, $u->{'name'});
+                               }
                        }
                }
        }
index 1c36ee9..c505a9e 100755 (executable)
@@ -6,23 +6,21 @@ do "acl-lib.pl";
 sub useradmin_create_user
 {
 return if (!$config{'sync_create'});
-local ($group) = grep { $_->{'name'} eq $config{'sync_group'} }
-                     &list_groups();
+my $group = &get_group($config{'sync_group'});
 return if (!$group);
-local ($clash) = grep { $_->{'name'} eq $_[0]->{'user'} }
-                       (&list_users(), &list_groups());
+my $clash = &get_user($_[0]->{'user'}) || &get_group($_[0]->{'user'});
 return if ($clash);
 return if ($_[0]->{'user'} !~ /^[A-z0-9\-\_\.]+$/);
-local $user = { 'name' => $_[0]->{'user'},
-               'pass' => $config{'sync_unix'} ? 'x' : $_[0]->{'pass'},
-               'sync' => 1,
-               'modules' => $group->{'modules'} };
+my $user = { 'name' => $_[0]->{'user'},
+            'pass' => $config{'sync_unix'} ? 'x' : $_[0]->{'pass'},
+            'sync' => 1,
+            'modules' => $group->{'modules'} };
 &create_user($user);
 push(@{$group->{'members'}}, $user->{'name'});
 &modify_group($group->{'name'}, $group);
 
-foreach $m (@{$group->{'modules'}}, "") {
-       local %groupacl;
+foreach my $m (@{$group->{'modules'}}, "") {
+       my %groupacl;
        unlink("$config_directory/$m/$user->{'name'}.acl");
        if (&read_file("$config_directory/$m/$group->{'name'}.gacl",
                       \%groupacl)) {
@@ -38,16 +36,14 @@ foreach $m (@{$group->{'modules'}}, "") {
 sub useradmin_delete_user
 {
 return if (!$config{'sync_delete'});
-local @list = &list_users();
-foreach $u (@list) {
-       if ($u->{'name'} eq $_[0]->{'user'}) {
-               &delete_user($u->{'name'});
-               &reload_miniserv();
-               }
+my $u = &get_user($_[0]->{'user'});
+if ($u) {
+       &delete_user($u->{'name'});
+       &reload_miniserv();
        }
 foreach $g (&list_groups()) {
-       local @mems = @{$g->{'members'}};
-       local $i = &indexof($_[0]->{'user'}, @mems);
+       my @mems = @{$g->{'members'}};
+       my $i = &indexof($_[0]->{'user'}, @mems);
        if ($i >= 0) {
                splice(@mems, $i, 1);
                $g->{'members'} = \@mems;
@@ -61,41 +57,43 @@ foreach $g (&list_groups()) {
 sub useradmin_modify_user
 {
 return if ($_[0]->{'passmode'} == 4 && $_[0]->{'olduser'} eq $_[0]->{'user'});
-foreach $u (&list_users()) {
-       if ($u->{'name'} eq $_[0]->{'olduser'} && $u->{'sync'}) {
-               if ($_[0]->{'user'} ne $_[0]->{'olduser'}) {
-                       # New name might clash (or be invalid)
-                       local ($clash) =grep { $_->{'name'} eq $_[0]->{'user'} }
-                                               (&list_users(), &list_groups());
-                       return if ($clash);
-                       return if ($_[0]->{'user'} !~ /^[A-z0-9\-\_\.]+$/);
-                       }
-               $u->{'name'} = $_[0]->{'user'};
-               if ($u->{'pass'} ne 'x') {
-                       $u->{'pass'} = $_[0]->{'passmode'} == 3 ?
-                          &encrypt_password($_[0]->{'plainpass'}) :
-                          $_[0]->{'pass'};
-                       }
-               &modify_user($_[0]->{'olduser'}, $u);
-               &reload_miniserv();
+my $u = &get_user($_[0]->{'olduser'});
+if ($u && $u->{'sync'}) {
+       if ($_[0]->{'user'} ne $_[0]->{'olduser'}) {
+               # New name might clash (or be invalid)
+               my $clash = &get_user($_[0]->{'user'}) ||
+                              &get_group($_[0]->{'user'});
+               return if ($clash);
+               return if ($_[0]->{'user'} !~ /^[A-z0-9\-\_\.]+$/);
                }
-
-       # Check other users' acl module acls
-       local %uaccess = &get_module_acl($u->{'name'});
-       local @au = split(/\s+/, $uaccess{'users'});
-       local $idx = &indexof($_[0]->{'olduser'}, @au);
-       if ($idx != -1) {
-               $au[$idx] = $_[0]->{'user'};
-               $uaccess{'users'} = join(" ", @au);
-               &save_module_acl(\%uaccess, $u->{'name'});
+       $u->{'name'} = $_[0]->{'user'};
+       if ($u->{'pass'} ne 'x') {
+               $u->{'pass'} = $_[0]->{'passmode'} == 3 ?
+                  &encrypt_password($_[0]->{'plainpass'}) :
+                  $_[0]->{'pass'};
                }
+       &modify_user($_[0]->{'olduser'}, $u);
+       &reload_miniserv();
        }
 
-# Rename the user in his group
+
 if ($_[0]->{'user'} ne $_[0]->{'olduser'}) {
-       foreach $g (&list_groups()) {
-               local @mems = @{$g->{'members'}};
-               local $i = &indexof($_[0]->{'olduser'}, @mems);
+       # Check other users' acl module acls
+       foreach my $u (&list_users()) {
+               my %uaccess = &get_module_acl($u->{'name'});
+               my @au = split(/\s+/, $uaccess{'users'});
+               my $idx = &indexof($_[0]->{'olduser'}, @au);
+               if ($idx != -1) {
+                       $au[$idx] = $_[0]->{'user'};
+                       $uaccess{'users'} = join(" ", @au);
+                       &save_module_acl(\%uaccess, $u->{'name'});
+                       }
+               }
+
+       # Rename the user in his group
+       foreach my $g (&list_groups()) {
+               my @mems = @{$g->{'members'}};
+               my $i = &indexof($_[0]->{'olduser'}, @mems);
                if ($i >= 0) {
                        $mems[$i] = $_[0]->{'user'};
                        $g->{'members'} = \@mems;