=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();
%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 :
=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);
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];
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()) {
# 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()) {
}
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 : ".
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
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.
# 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",
# 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
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;
}
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);
%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_)/);
{
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];
$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() , "" );
# 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'}) {
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
# 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>",
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;
}
$g->{'members'} = \@newmembers;
if ($any) {
- print STDERR "Changing members of $g->{'name'} to ",join(" ", @newmembers),"\n";
&modify_group($g->{'name'}, $g);
}
}
$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(), "" );
}
# 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'};
}
&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'});
# 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'});
}
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'});
+ }
}
}
}
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)) {
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;
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;