Re: [webmin-devel] using modify_user to change user details

"Todd Kennedy" <[email protected]> Tue, 3 Jun 2008 09:28:50 -0400
Newsgroups gmane.comp.web.webmin.devel
Message-ID <[email protected]>
Hi Jamie,

Thanks for replying to my post. I still can't figure out whats going
on. I've defined %user hash but to no avail I cannot seem to locate
the issue. I've attached my recycle_users.cgi. If you could briefly
review it that would be great. I also welcome any pointers and
constructive criticism.

Thanks

-Todd

On Mon, Jun 2, 2008 at 5:04 PM, Jamie Cameron <[email protected]> wrote:
> Hi Todd,
>
> Make sure that in your code, you aren't calling modify_user with an
> empty user hash. It looks like that is the case, as this would cause
> an 'empty' line like that to be inserted into /etc/passwd.
>
> I'd have to see all your code to comment further though..
>
>  - Jamie
>
> On 02/Jun/2008 12:11 Todd Kennedy wrote ..
>> Hello,
>>
>> I am new to webmin module development and am trying to extend the "Users
>> & Groups" module for internal purposes. What I have done is create a
>> "Recycle User" button on the selected users "Edit Users" page that
>> basically mimics the functionality of the delete_user.cgi (without
>> actually deleting the user) but also extends it by showing me a list of
>> currently selected user processes running. Once I press the "Recycle
>> user and home directory button" it goes off and first terminates those
>> running processes then removes the users home directory then I need to
>> reset the users password back to some default. I am about 90% of the way
>> there but seem to be caught up on the saving of the modified password. I
>> am using the modify_user function. Whats actually happening is that it's
>> leaving an incomplete line in the /etc/password and shadow file looking
>> something like this:
>>
>> svctag:*LK*:6445::::::
>> nobody:*LK*:6445::::::
>> noaccess:*LK*:6445::::::
>> nobody4:*LK*:6445::::::
>> ::::::::
>>
>> This last line should be the entry that I recycled but it's just a bunch
>> of empty GECOS fields. So it seems to me that it's rewriting the line
>> but is doing it with empty values.
>>
>> So I think I am missing something somewhere but am unsure what other
>> functions possibly I need to call in order for it to write out the
>> password file. I'm assuming the modify_user function takes care of that, no?
>>
>>
>> This does seem like a strange thing to do but would fit nicely into
>> managing test accounts for may lab machines. When the lab reservation
>> expires the admin can recycle the user with a press of the button and
>> set it back to some default settings.
>>
>> Here's the snippet of code that is suppose to be resetting the password
>> and saving it.
>>
>> %recycleuser = %{$ulist[$in{'num'}]};
>> $pass = &encrypt_password($config{'recycle_passwd'});
>> $recycleuser{'pass'} = $pass;
>>
>> # Update user details
>>     &modify_user(\%recycleuser, \%user);
>>
>> # Run the pre-change command
>>     $merr = &making_changes();
>>     &error(&text('usave_emaking', "<tt>$merr</tt>")) if (defined($merr));
>>     &unlock_user_files();
>>     &made_changes();
>>
>> So as the code runs the following is displayed in the borwser, The has
>> info is there for debugging purposes.
>>
>> Deleting from other modules ..
>> .. done
>> Killing user processes
>> .. done
>>
>> Deleting home directory ..
>> .. done
>>
>> Creating Home Directory
>> .. done
>>
>> new encrypted passwd=TN4xb4vuWbxc6
>>
>> Contents of recycleuser Hash prior to change
>>
>> num => 20
>>
>> inactive =>
>>
>> max =>
>>
>> warn =>
>>
>> user => testuser
>>
>> shell => /bin/sh
>>
>> home => /export/home/testuser
>>
>> real =>
>>
>> line => 20
>>
>> pass => NNwEdL/U3qYwU
>>
>> sline => 20
>>
>> uid => 1000
>>
>> change => 14032
>>
>> min =>
>>
>> expire =>
>>
>> gid => 10
>>
>>
>> Contents of User hash after replacing $pass but before saving
>>
>> num => 20
>>
>> inactive =>
>>
>> max =>
>>
>> warn =>
>>
>> user => testuser
>>
>> shell => /bin/sh
>>
>> home => /export/home/testuser
>>
>> real =>
>>
>> line => 20
>>
>> pass => TN4xb4vuWbxc6
>>
>> sline => 20
>>
>> uid => 1000
>>
>> change => 14032
>>
>> min =>
>>
>> expire =>
>>
>> gid => 10
>>
>>
>> Reread the %ulist has for the user "Still has the old password"
>>
>> num => 20
>>
>> inactive =>
>>
>> max =>
>>
>> warn =>
>>
>> user => testuser
>>
>> shell => /bin/sh
>>
>> home => /export/home/testuser
>>
>> real =>
>>
>> line => 20
>>
>> pass => NNwEdL/U3qYwU
>>
>> sline => 20
>>
>> uid => 1000
>>
>> change => 14032
>>
>> min =>
>>
>> expire =>
>>
>> gid => 10
>>
>>
>> Any help on this would be much appriciated.
>
> -------------------------------------------------------------------------
> This SF.net email is sponsored by: Microsoft
> Defy all challenges. Microsoft(R) Visual Studio 2008.
> http://clk.atdmt.com/MRT/go/vse0120000070mrt/direct/01/
> -
> Forwarded by the Webmin development list at [email protected]
> To remove yourself from this list, go to
> http://lists.sourceforge.net/lists/listinfo/webadmin-devel
>

-------------------------------------------------------------------------
This SF.net email is sponsored by: Microsoft
Defy all challenges. Microsoft(R) Visual Studio 2008.
http://clk.atdmt.com/MRT/go/vse0120000070mrt/direct/01/

-
Forwarded by the Webmin development list at [email protected]
To remove yourself from this list, go to
http://lists.sourceforge.net/lists/listinfo/webadmin-devel
recycle_user.cgi.txt (text/plain, 6.5 KB)
#!/usr/local/bin/perl
# Recycle the current users login account
# Kill all users processes
# remove / recreate users home directory
# reset the users password to a common password


require './user-lib.pl';
&foreign_require("proc", "proc-lib.pl");

&ReadParse();
&lock_user_files();
@ulist = &list_users();
%recycleuser = %{$ulist[$in{'num'}]};
%user = %recycleuser;
$user = $ulist[$in{'num'}];
$user || &error($text{'recycle_enum'});
&error_setup($text{'recycle_err'});
%access = &get_module_acl();
&can_edit_user(\%access, $user) || &error($text{'recycle_euser'});
$access{'udelete'} || &error($text{'recycle_euser'});
@procs = sort { $b->{'cpu'} <=> $a->{'cpu'} } &proc::list_processes();
@procs = grep { &proc::can_view_process($_->{'user'}) } @procs;
$valid = 0;

$| = 1;

&ui_print_header(undef, $text{'recycle_title'}, "");

############# Start of User Del section ##############

if (!$config{'delete_root'} && $user->{'uid'} <= 10) {
	print "<p> <b>$text{'recycle_eroot'}</b> <p>\n";
	print "<hr>\n";
	&footer("", $text{'index_return'});
	exit;
	}

# Check for repeat click
if ($user->{'user'} ne $in{'user'} || $in{'user'} eq '') {
	print "<p> <b>$text{'recycle_ealready'}</b> <p>\n";
	print "<hr>\n";
	&footer("", $text{'index_return'});
	exit;
	}

if ($in{'confirmed'}) {
	# Run the before command
	local @secs;
	foreach $g (&list_groups()) {
		@mems = split(/,/, $g->{'members'});
		if (&indexof($user->{'user'}, @mems) >= 0) {
			push(@secs, $g->{'gid'});
			}
		}
	&set_user_envs($user, 'MODIFY_USER', undef, \@secs);
	$merr = &making_changes();
	&error(&text('usave_emaking', "<tt>$merr</tt>")) if (defined($merr));

	# Go ahead and do it!
	$in{'others'} = !$access{'dothers'} if ($access{'dothers'} != 1);
	if ($in{'others'}) {
		print "$text{'recycle_other'}<br>\n";
		local $error_must_die = 1;
		eval { &other_modules("useradmin_delete_user", $user); };
		if ($@) {
			print &text('recycle_failed', $@),"<p>\n";
			}
		else {
			print "$text{'recycle_done'}<p>\n";
			$pass = &encrypt_password($config{'recycle_passwd'});
			$recycleuser{'pass'} = $pass;
			&modify_user(\%recycleuser, \%user);
			}
		}
		
	# Kill Running Processes
	print "$text{'recycle_procs'}<br>\n";
	@pidlist = split(/\s+/, $in{pidlist});
	@pinfo = &proc::list_processes(@pidlist);
	for($i=0; $i<@pidlist; $i++) {
		$in{"args$i"} = $pinfo[$i]->{'args'};
		print "$text{'pid'} <tt>$pidlist[$i]</tt> ... \n";
		if (&proc::can_edit_process($pinfo[$i]->{'user'})) {
			if (&proc::kill_logged($in{signal}, $pidlist[$i])) {
				print "SIG$in{signal} $text{'recycle_kill_sent'}<br>\n";
				}
			else {
				print "$!<br>\n";
				}
			}
		else {
			print "$text{'recyle_kill_ecannot'}<br>\n";
			}
		}
		print "$text{'recycle_done'}<p>\n";
	&webmin_log("kill", undef, undef, \%in);
	print "<p>\n";

	&unlock_user_files();

	if ($in{'delhome'} && $user->{'home'} !~ /^\/+$/ && $access{'delhome'} != 0) {
		# Delete home directory
		print "$text{'recycle_home'}<br>\n";
		&lock_file($user->{'home'});
		&delete_home_directory($user);
		&unlock_file($user->{'home'});
		print "$text{'recycle_done'}<p>\n";
		# Now lets recreate the user directory
		print "$text{'recycle_ucreate_home'}<br>\n";
		&create_home_directory(\%user, $user->{'home'});
		print "$text{'recycle_done'}<p>\n";
		}
	&made_changes();

	%p = ( %in, %$user );
	delete($p{'pass'});
	&webmin_log("recycle", "user", $user->{'user'}, \%p);

done:
	&ui_print_footer("", $text{'index_return'});
	}
else {
	# Check if something has changed
	if ($user->{'user'} ne $in{'user'}) {
		print "<p> <b>$text{'recycle_echanged'}</b> <p>\n";
		&ui_print_footer("", $text{'index_return'});
		exit;
		}

	############# List running user processes ###############

	if (&supports_users()) {
		@uinfo = getpwnam($user->{'user'});
		$uinfo[6] =~ s/,.*$//;
		}
	print &ui_subheading("$user->{'user'} ".($uinfo[6] ? "($uinfo[6])" : "")),"\n";
	print &ui_columns_start([
			  $text{'pid'},
			  $text{'cpu'},
			  $info_arg_map{'_stime'} ? ( $text{'stime'} ) : ( ),
			  $text{'command'} ], 100);
	foreach $pr (grep { $_->{'user'} eq $user->{'user'} } @procs) {
		local @cols;
		push(@pidlist, $pr->{'pid'});
		push(@cols, $pr->{'pid'});
		push(@cols, $pr->{'cpu'});
		if ($info_arg_map{'_stime'}) {
			push(@cols, $pr->{'_stime'});
			}
		push(@cols, &html_escape(&proc::cut_string($pr->{'args'})));
		print &ui_columns_row(\@cols);
		$valid++;
	}
	print &ui_columns_end();
	if ($valid eq 0) {
	print "<br><center><b>",&text('recycle_no_procs', $user->{'user'}),
	"</b><p><br>\n";
	}

	# warn if running processes
	if (@pidlist) {
		print "<br><center><b><font color=#ff0000>",
		      &text('recycle_kill_warn', "<tt>$user->{'user'}</tt>", scalar(@others)),
		      "</font></b><br>\n";
		}

	# Ask if the user is sure
	print "<form action=recycle_user.cgi>\n";
	print "<input type=hidden name=num value=\"$in{'num'}\">\n";
	print "<input type=hidden name=user value=\"$user->{'user'}\">\n";
	printf "<input type=hidden name=pidlist value=\"%s\">\n",
			join(" ", @pidlist);
	print "<input type=hidden name=signal value=\"KILL\">\n";
	print "<input type=hidden name=confirmed value=1>\n";

	if ($user->{'home'} ne "/" && -d $user->{'home'} && $access{'delhome'} != 0) {
		# Has a home directory, so check for files owned by others
		$size = &disk_usage_kb($user->{'home'});
		print "<center><b>",&text('recycle_sure', $user->{'user'},
			   "<tt>$user->{'home'}</tt>", &nice_size($size*1024)),
			   "</b><p>\n";
		if ($access{'delhome'} != 1) {
			print "<input type=submit value=\"$text{'recycle_del1'}\">\n";
			}
		print "<input name=delhome type=submit ",
		      "value=\"$text{'recycle_del2'}\">\n";

		# check for files owned by other users
		@others = &backquote_command("find ".quotemeta($user->{'home'})." ! -user $user->{'uid'} 2>/dev/null", 1);
		if (@others) {
			print "<br><b><font color=#ff0000>",
			      &text('recycle_dir_warn', "<tt>$user->{'home'}</tt>", scalar(@others)),
			      "</font></b><br>\n";
			}
		}
# We Don't want two buttons, we only want to recycle the user and home dir
#	else {
#		# No home directory
#		print "<center><b>",&text('recycle_sure2',
#					   $user->{'user'}),"</b><p>\n";
#		print "<input type=submit value=\"$text{'recycle_del1'}\">\n";
#		}
	print "<br>\n";
	if ($access{'dothers'} == 1) {
		printf "<input type=checkbox name=others value=1 %s> %s<br>\n",
			$config{'default_other'} ? "checked" : "",
	      		$text{'recycle_dothers'};
		}
	if ($user->{'user'} eq 'root') {
		print "<center><b><font color=#ff0000>$text{'recycle_root'}",
		      "</font></b><p></center>\n";
		}
	print "</form></center>\n";
	&ui_print_footer("", $text{'index_return'});
}