mirror of
https://github.com/webmin/webmin.git
synced 2026-08-21 14:30:41 +01:00
Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b8a0081fdc | ||
|
|
d38c7205eb | ||
|
|
76030a251c | ||
|
|
7e41f339a3 | ||
|
|
ac62ad5f94 | ||
|
|
cae7596303 | ||
|
|
8f44b698c9 | ||
|
|
b0bd9065f6 | ||
|
|
1dbdc60058 |
@@ -1,5 +1,12 @@
|
|||||||
## Changelog
|
## Changelog
|
||||||
|
|
||||||
|
#### 2.653 (July 26, 2026)
|
||||||
|
* Fix to include missing `xmlrpc-lib.pl` file in the package
|
||||||
|
* Fix listing partially installed Debian and derivative packages
|
||||||
|
* Update the Authentic theme to the latest version with various improvements:
|
||||||
|
- Add improved support for the upcoming Virtualmin Podman plugin
|
||||||
|
- Fix mail compose panel styling and sizing on initial load in Usermin
|
||||||
|
|
||||||
#### 2.652 (July 16, 2026)
|
#### 2.652 (July 16, 2026)
|
||||||
* Add a global per-user ACL control to block URL downloads from non-public IP addresses in File Manager, Mailboxes, and Upload and Download modules
|
* Add a global per-user ACL control to block URL downloads from non-public IP addresses in File Manager, Mailboxes, and Upload and Download modules
|
||||||
* Fix to recognize hex numeric HTML entities to work in various elements
|
* Fix to recognize hex numeric HTML entities to work in various elements
|
||||||
|
|||||||
172
bin/passwd
172
bin/passwd
@@ -19,6 +19,8 @@ sub main
|
|||||||
'config|c=s' => \$opt{'config'},
|
'config|c=s' => \$opt{'config'},
|
||||||
'user|u=s' => \$opt{'user'},
|
'user|u=s' => \$opt{'user'},
|
||||||
'password|p=s' => \$opt{'password'},
|
'password|p=s' => \$opt{'password'},
|
||||||
|
'unix' => \$opt{'unix'},
|
||||||
|
'webmin-only|webmin' => \$opt{'webmin'},
|
||||||
'stdout|o!' => \$opt{'stdout'});
|
'stdout|o!' => \$opt{'stdout'});
|
||||||
|
|
||||||
# If username passed as regular param
|
# If username passed as regular param
|
||||||
@@ -135,6 +137,29 @@ sub change_password
|
|||||||
RESET, "\n");
|
RESET, "\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# A Webmin user can either authenticate as a Unix user or have a separate
|
||||||
|
# password in miniserv.users. Make this distinction explicit whenever both
|
||||||
|
# accounts exist, as silently setting the latter overrides Unix
|
||||||
|
# authentication and leaves SSH and Webmin with different passwords.
|
||||||
|
my @unix_info = getpwnam($user);
|
||||||
|
my $target = choose_password_target(
|
||||||
|
$optref, $user, scalar(@unix_info));
|
||||||
|
if ($target eq 'unix') {
|
||||||
|
change_unix_password($user);
|
||||||
|
|
||||||
|
# Ensure Webmin uses the newly changed Unix password. No restart is
|
||||||
|
# needed when the account was already configured this way.
|
||||||
|
if (($uinfos{$user}->[0] // '') ne 'x') {
|
||||||
|
$uinfos{$user}->[0] = 'x';
|
||||||
|
map {$ulines{$_} = join(":", @{ $uinfos{$_} })} keys %uinfos;
|
||||||
|
store_webmin_users(
|
||||||
|
$confdif, $minserv_uconf_file, \%ulines);
|
||||||
|
}
|
||||||
|
say "Unix password for user ", BRIGHT_YELLOW, $user, RESET,
|
||||||
|
" updated successfully; Webmin will use Unix authentication";
|
||||||
|
exit 0;
|
||||||
|
}
|
||||||
|
|
||||||
# Ask for password on stdin
|
# Ask for password on stdin
|
||||||
my $suc_pre_msg = "";
|
my $suc_pre_msg = "";
|
||||||
my $suc_msg = 'updated successfully';
|
my $suc_msg = 'updated successfully';
|
||||||
@@ -171,13 +196,8 @@ sub change_password
|
|||||||
$uinfos{$user}->[5] = time() if ($uinfos{$user}->[5]);
|
$uinfos{$user}->[5] = time() if ($uinfos{$user}->[5]);
|
||||||
map {$ulines{$_} = join(":", @{ $uinfos{$_} })} keys %uinfos;
|
map {$ulines{$_} = join(":", @{ $uinfos{$_} })} keys %uinfos;
|
||||||
|
|
||||||
# Store original file first
|
# Write the new user config and restart Webmin
|
||||||
copy_source_dest($minserv_uconf_file, "$minserv_uconf_file-");
|
store_webmin_users($confdif, $minserv_uconf_file, \%ulines);
|
||||||
|
|
||||||
# Restart Webmin and write new user config file
|
|
||||||
system("$confdif/stop >/dev/null 2>&1");
|
|
||||||
write_file($minserv_uconf_file, \%ulines, ":");
|
|
||||||
system("$confdif/start >/dev/null 2>&1");
|
|
||||||
|
|
||||||
# Print user message
|
# Print user message
|
||||||
say "${suc_pre_msg}Password for Webmin user ", BRIGHT_YELLOW, $user, RESET, " $suc_msg";
|
say "${suc_pre_msg}Password for Webmin user ", BRIGHT_YELLOW, $user, RESET, " $suc_msg";
|
||||||
@@ -185,6 +205,119 @@ sub change_password
|
|||||||
exit 0;
|
exit 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub choose_password_target
|
||||||
|
{
|
||||||
|
my ($optref, $user, $unix_exists, $interactive) = @_;
|
||||||
|
|
||||||
|
die BRIGHT_RED, "Error: ", RESET,
|
||||||
|
"The --unix and --webmin-only options cannot be used together\n"
|
||||||
|
if ($optref->{'unix'} && $optref->{'webmin'});
|
||||||
|
die BRIGHT_RED, "Error: ", RESET,
|
||||||
|
"The --unix and --stdout options cannot be used together\n"
|
||||||
|
if ($optref->{'unix'} && $optref->{'stdout'});
|
||||||
|
die BRIGHT_RED, "Error: ", RESET,
|
||||||
|
"The --unix and --password options cannot be used together; ",
|
||||||
|
"the system passwd command reads the password securely\n"
|
||||||
|
if ($optref->{'unix'} && defined($optref->{'password'}));
|
||||||
|
die BRIGHT_RED, "Error: ", RESET, "Unix user ", BRIGHT_YELLOW,
|
||||||
|
$user, RESET, " doesn't exist\n"
|
||||||
|
if ($optref->{'unix'} && !$unix_exists);
|
||||||
|
|
||||||
|
return 'webmin' if ($optref->{'stdout'} || !$unix_exists);
|
||||||
|
return 'unix' if ($optref->{'unix'});
|
||||||
|
return 'webmin' if ($optref->{'webmin'});
|
||||||
|
if (defined($optref->{'password'})) {
|
||||||
|
print STDERR unix_password_warning($user),
|
||||||
|
" The --password option explicitly sets a separate Webmin-only ",
|
||||||
|
"password; use --unix without --password to change the Unix ",
|
||||||
|
"password instead.\n";
|
||||||
|
return 'webmin';
|
||||||
|
}
|
||||||
|
|
||||||
|
$interactive = -t STDIN if (!defined($interactive));
|
||||||
|
if ($interactive) {
|
||||||
|
return prompt_password_target($user);
|
||||||
|
}
|
||||||
|
|
||||||
|
print STDERR unix_password_warning($user),
|
||||||
|
" Non-interactive input prevents asking which password to change; ",
|
||||||
|
"continuing with a separate Webmin-only password. Pass --unix or ",
|
||||||
|
"--webmin-only to select explicitly.\n";
|
||||||
|
return 'webmin';
|
||||||
|
}
|
||||||
|
|
||||||
|
sub prompt_password_target
|
||||||
|
{
|
||||||
|
my ($user) = @_;
|
||||||
|
say unix_password_warning($user);
|
||||||
|
say " 1. Change the Unix password and use Unix authentication in Webmin",
|
||||||
|
" (recommended)";
|
||||||
|
say " 2. Set a separate Webmin-only password in miniserv.users";
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
print "Select password type [1]: ";
|
||||||
|
my $choice = <STDIN>;
|
||||||
|
die BRIGHT_RED, "\nError: ", RESET,
|
||||||
|
"No password type was selected\n" if (!defined($choice));
|
||||||
|
chomp $choice;
|
||||||
|
$choice = lc($choice);
|
||||||
|
return 'unix' if ($choice eq '' || $choice eq '1' ||
|
||||||
|
$choice eq 'u' || $choice eq 'unix');
|
||||||
|
return 'webmin' if ($choice eq '2' || $choice eq 'w' ||
|
||||||
|
$choice eq 'webmin');
|
||||||
|
say BRIGHT_RED, "Invalid selection.", RESET,
|
||||||
|
" Enter 1 for Unix or 2 for Webmin-only.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub unix_password_warning
|
||||||
|
{
|
||||||
|
my ($user) = @_;
|
||||||
|
return BRIGHT_YELLOW . "Warning: " . RESET . "Webmin user " .
|
||||||
|
BRIGHT_YELLOW . $user . RESET .
|
||||||
|
" is also a Unix user. A separate Webmin password overrides Unix " .
|
||||||
|
"authentication, so Webmin and SSH can have different passwords.";
|
||||||
|
}
|
||||||
|
|
||||||
|
sub change_unix_password
|
||||||
|
{
|
||||||
|
my ($user) = @_;
|
||||||
|
my $passwd = has_command('passwd');
|
||||||
|
die BRIGHT_RED, "Error: ", RESET,
|
||||||
|
"The system passwd command could not be found\n" if (!$passwd);
|
||||||
|
die BRIGHT_RED, "Error: ", RESET,
|
||||||
|
"Changing a Unix password requires an interactive terminal. ",
|
||||||
|
"Run ", BRIGHT_YELLOW, "$passwd $user", RESET, " directly instead.\n"
|
||||||
|
if (!-t STDIN);
|
||||||
|
|
||||||
|
my $status = system { $passwd } $passwd, $user;
|
||||||
|
if ($status == -1) {
|
||||||
|
die BRIGHT_RED, "Error: ", RESET,
|
||||||
|
"Failed to run $passwd: $!\n";
|
||||||
|
}
|
||||||
|
elsif ($status & 127) {
|
||||||
|
die BRIGHT_RED, "Error: ", RESET,
|
||||||
|
"The system passwd command was interrupted\n";
|
||||||
|
}
|
||||||
|
elsif ($status >> 8) {
|
||||||
|
die BRIGHT_RED, "Error: ", RESET,
|
||||||
|
"The system passwd command failed\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub store_webmin_users
|
||||||
|
{
|
||||||
|
my ($confdif, $minserv_uconf_file, $ulines) = @_;
|
||||||
|
|
||||||
|
# Store original file first
|
||||||
|
copy_source_dest($minserv_uconf_file, "$minserv_uconf_file-");
|
||||||
|
|
||||||
|
# Restart Webmin and write new user config file
|
||||||
|
system("$confdif/stop >/dev/null 2>&1");
|
||||||
|
write_file($minserv_uconf_file, $ulines, ":");
|
||||||
|
system("$confdif/start >/dev/null 2>&1");
|
||||||
|
}
|
||||||
|
|
||||||
sub root
|
sub root
|
||||||
{
|
{
|
||||||
my ($config, $conf_check) = @_;
|
my ($config, $conf_check) = @_;
|
||||||
@@ -221,7 +354,9 @@ sub root
|
|||||||
|
|
||||||
=head1 DESCRIPTION
|
=head1 DESCRIPTION
|
||||||
|
|
||||||
This program allows you to change the password of a user in the Webmin password file
|
This program allows you to change the password used by a Webmin user.
|
||||||
|
When a matching Unix user exists, it can change the Unix password or set a
|
||||||
|
separate password in the Webmin password file.
|
||||||
|
|
||||||
=head1 SYNOPSIS
|
=head1 SYNOPSIS
|
||||||
|
|
||||||
@@ -238,8 +373,10 @@ sub root
|
|||||||
Examples of usage:
|
Examples of usage:
|
||||||
- webmin passwd root
|
- webmin passwd root
|
||||||
- webmin passwd --user root
|
- webmin passwd --user root
|
||||||
- webmin passwd --user root --password ycwyMQRVAZY
|
- webmin passwd --user root --unix
|
||||||
- webmin passwd --config /usr/local/etc/webmin --user root --password ycwyMQRVAZY
|
- webmin passwd --user admin --webmin-only
|
||||||
|
- webmin passwd --user admin --webmin-only --password ycwyMQRVAZY
|
||||||
|
- webmin passwd --config /usr/local/etc/webmin --user admin --webmin-only --password ycwyMQRVAZY
|
||||||
- webmin passwd --config /usr/local/etc/webmin --user root --password ycwyMQRVAZY --stdout
|
- webmin passwd --config /usr/local/etc/webmin --user root --password ycwyMQRVAZY --stdout
|
||||||
|
|
||||||
=item --config, -c
|
=item --config, -c
|
||||||
@@ -252,7 +389,19 @@ sub root
|
|||||||
|
|
||||||
=item --password, -p
|
=item --password, -p
|
||||||
|
|
||||||
Set new user password. Using this option may be unsecure.
|
Set a new Webmin-only password. Using this option may be insecure because the
|
||||||
|
password can be exposed in the process list.
|
||||||
|
|
||||||
|
=item --unix
|
||||||
|
|
||||||
|
Change the matching Unix user's password with the system C<passwd> command,
|
||||||
|
and configure Webmin to use Unix authentication. This is the recommended mode
|
||||||
|
when the Webmin username is also a Unix username.
|
||||||
|
|
||||||
|
=item --webmin-only, --webmin
|
||||||
|
|
||||||
|
Set a separate password in C<miniserv.users>, even if a matching Unix user
|
||||||
|
exists. This password overrides Unix authentication for the Webmin user.
|
||||||
|
|
||||||
=back
|
=back
|
||||||
|
|
||||||
@@ -261,4 +410,3 @@ Set new user password. Using this option may be unsecure.
|
|||||||
Copyright 2018 Jamie Cameron <jcameron@webmin.com>
|
Copyright 2018 Jamie Cameron <jcameron@webmin.com>
|
||||||
Joe Cooper <joe@virtualmin.com>
|
Joe Cooper <joe@virtualmin.com>
|
||||||
Ilia Ross <ilia@virtualmin.com>
|
Ilia Ross <ilia@virtualmin.com>
|
||||||
|
|
||||||
|
|||||||
@@ -10,16 +10,10 @@ $cwd =~ s/(.*)\/.*/$1/;
|
|||||||
usage() if (@ARGV != 3);
|
usage() if (@ARGV != 3);
|
||||||
|
|
||||||
my ($config, $user, $pass) = @ARGV;
|
my ($config, $user, $pass) = @ARGV;
|
||||||
my $status = system("$cwd/bin/webmin passwd --config $config --user $user --pass $pass");
|
exec "$cwd/bin/webmin", "passwd", "--webmin-only",
|
||||||
if ($status != 0) {
|
"--config", $config, "--user", $user, "--pass", $pass;
|
||||||
if ($! =~ /no such file/i) {
|
print STDERR "Error: Failed to execute Webmin CLI command: $!\n";
|
||||||
print "Error: Webmin CLI command cannot be found\n";
|
exit 1;
|
||||||
}
|
|
||||||
else {
|
|
||||||
print "Error: $!\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
exit $status;
|
|
||||||
|
|
||||||
sub usage
|
sub usage
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -124,6 +124,7 @@ hr { border: 0;
|
|||||||
color: #D9D9D9;
|
color: #D9D9D9;
|
||||||
background-color: #D9D9D9;
|
background-color: #D9D9D9;
|
||||||
}
|
}
|
||||||
|
table.shrinkwrapper table.ui_table td.ui_form_value > table.ui_table,
|
||||||
table.wrapper, table.shrinkwrapper {
|
table.wrapper, table.shrinkwrapper {
|
||||||
background-color:#D9D9D9;
|
background-color:#D9D9D9;
|
||||||
border:1px solid #D9D9D9;
|
border:1px solid #D9D9D9;
|
||||||
@@ -148,6 +149,14 @@ div.wrapper {
|
|||||||
.tabUnselected {
|
.tabUnselected {
|
||||||
background-color:#D9D9D9;
|
background-color:#D9D9D9;
|
||||||
}
|
}
|
||||||
|
.tabUnselected sup,
|
||||||
|
.tabSelected sup {
|
||||||
|
vertical-align: text-top;
|
||||||
|
margin-top: -4px;
|
||||||
|
font-size: 70%;
|
||||||
|
display: inline-block;
|
||||||
|
margin-right: -3px;
|
||||||
|
}
|
||||||
.goArrow { margin-bottom: -4px; }
|
.goArrow { margin-bottom: -4px; }
|
||||||
.ui_checked_columns .ui_checked_checkbox {
|
.ui_checked_columns .ui_checked_checkbox {
|
||||||
min-width: 20px;
|
min-width: 20px;
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ $vers || usage();
|
|||||||
"webmin-openrc-init", "run-postinstalls.pl",
|
"webmin-openrc-init", "run-postinstalls.pl",
|
||||||
"config-lib.pl", "entities_map.txt", "ui-lib.pl",
|
"config-lib.pl", "entities_map.txt", "ui-lib.pl",
|
||||||
"password_form.cgi", "password_change.cgi", "pam_login.cgi",
|
"password_form.cgi", "password_change.cgi", "pam_login.cgi",
|
||||||
"module_chooser.cgi", "config-windows", "xmlrpc.cgi",
|
"module_chooser.cgi", "config-windows", "xmlrpc.cgi", "xmlrpc-lib.pl",
|
||||||
"uptracker.cgi", "create-module.pl", "webmin_search.cgi",
|
"uptracker.cgi", "create-module.pl", "webmin_search.cgi",
|
||||||
"webmin-search-lib.pl", "WebminCore.pm",
|
"webmin-search-lib.pl", "WebminCore.pm",
|
||||||
"record-login.pl", "record-logout.pl", "record-failed.pl",
|
"record-login.pl", "record-logout.pl", "record-failed.pl",
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ local $arg = @_ ? join(" ", map { quotemeta($_) } @_) : "";
|
|||||||
&open_execute_command(PKGINFO, "COLUMNS=1024 dpkg --list $arg", 1, 1);
|
&open_execute_command(PKGINFO, "COLUMNS=1024 dpkg --list $arg", 1, 1);
|
||||||
while(<PKGINFO>) {
|
while(<PKGINFO>) {
|
||||||
next if (/^\|/ || /^\+/);
|
next if (/^\|/ || /^\+/);
|
||||||
if (/^[uirph]i..(\S+)\s+(\S+)\s+(.*)/) {
|
if (/^[uirph][iHUFWt]..(\S+)\s+(\S+)\s+(.*)/) {
|
||||||
$packages{$i,'name'} = $1;
|
$packages{$i,'name'} = $1;
|
||||||
$packages{$i,'class'} = &alphabet_name($1);
|
$packages{$i,'class'} = &alphabet_name($1);
|
||||||
$packages{$i,'version'} = $2;
|
$packages{$i,'version'} = $2;
|
||||||
@@ -57,11 +57,11 @@ sub package_info
|
|||||||
local ($pkg, $ver) = @_;
|
local ($pkg, $ver) = @_;
|
||||||
local $qm = quotemeta($pkg);
|
local $qm = quotemeta($pkg);
|
||||||
|
|
||||||
# First check if it is really installed, and not just known to the package
|
# First check if it is installed or partially installed, and not just known
|
||||||
# system in some way
|
# to the package system in some way
|
||||||
local $out = &backquote_command("dpkg --list $qm 2>&1", 1);
|
local $out = &backquote_command("dpkg --list $qm 2>&1", 1);
|
||||||
local @lines = split(/\r?\n/, $out);
|
local @lines = split(/\r?\n/, $out);
|
||||||
if ($lines[$#lines] !~ /^.[ih]/) {
|
if ($lines[$#lines] !~ /^.[iHUFWt]/) {
|
||||||
return ( );
|
return ( );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
82
t/software-debian.t
Normal file
82
t/software-debian.t
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
#!/usr/local/bin/perl
|
||||||
|
|
||||||
|
use strict;
|
||||||
|
use warnings;
|
||||||
|
use Test::More;
|
||||||
|
use File::Basename qw(dirname);
|
||||||
|
use File::Spec;
|
||||||
|
use Cwd qw(abs_path);
|
||||||
|
|
||||||
|
our (%packages, %text);
|
||||||
|
|
||||||
|
my $root = abs_path(File::Spec->catdir(dirname(__FILE__), '..'));
|
||||||
|
chdir($root) or die "chdir($root): $!";
|
||||||
|
|
||||||
|
do './software/debian-lib.pl' or die $@ || $!;
|
||||||
|
|
||||||
|
my $dpkg_output = <<'EOF';
|
||||||
|
Desired=Unknown/Install/Remove/Purge/Hold
|
||||||
|
| Status=Not/Inst/Conf-files/Unpacked/halF-conf/Half-inst/trig-aWait/Trig-pend
|
||||||
|
|/ Err?=(none)/Reinst-required (Status,Err: uppercase=bad)
|
||||||
|
+++-==============-=======-============-=================================
|
||||||
|
ii installed 1.0 amd64 Installed package
|
||||||
|
iH half-installed 1.1 amd64 Half-installed package
|
||||||
|
iU unpacked 1.2 amd64 Unpacked package
|
||||||
|
iF half-configured 1.3 amd64 Half-configured package
|
||||||
|
iW triggers-await 1.4 amd64 Package awaiting triggers
|
||||||
|
it triggers-pend 1.5 amd64 Package with pending triggers
|
||||||
|
rc config-only 0.9 amd64 Removed package configuration
|
||||||
|
un not-installed <none> <none> Package not installed
|
||||||
|
EOF
|
||||||
|
|
||||||
|
{
|
||||||
|
no warnings qw(once redefine);
|
||||||
|
local *open_execute_command = sub {
|
||||||
|
my ($fh) = @_;
|
||||||
|
no strict 'refs';
|
||||||
|
open(ref($fh) ? $fh : \*{$fh}, '<', \$dpkg_output)
|
||||||
|
or die "open simulated dpkg output: $!";
|
||||||
|
};
|
||||||
|
|
||||||
|
my $count = list_packages();
|
||||||
|
is($count, 6, 'lists installed and partially installed packages');
|
||||||
|
is_deeply(
|
||||||
|
[ map { $packages{$_,'name'} } 0 .. $count-1 ],
|
||||||
|
[ qw(installed half-installed unpacked half-configured
|
||||||
|
triggers-await triggers-pend) ],
|
||||||
|
'keeps every present package state and excludes removed packages');
|
||||||
|
}
|
||||||
|
|
||||||
|
sub package_info_for_status
|
||||||
|
{
|
||||||
|
my ($status) = @_;
|
||||||
|
no warnings qw(once redefine);
|
||||||
|
local *has_command = sub {
|
||||||
|
return $_[0] eq 'apt-cache' ? '/usr/bin/apt-cache' : undef;
|
||||||
|
};
|
||||||
|
local *html_escape = sub { return $_[0]; };
|
||||||
|
local *make_date = sub { return $_[0]; };
|
||||||
|
local *backquote_command = sub {
|
||||||
|
my ($command) = @_;
|
||||||
|
$? = 0;
|
||||||
|
return "$status php8.2-fpm 8.2.1 amd64 PHP FPM\n"
|
||||||
|
if ($command =~ /^dpkg --list/);
|
||||||
|
return "Package: php8.2-fpm\n".
|
||||||
|
"Version: 8.2.1\n".
|
||||||
|
"Architecture: amd64\n".
|
||||||
|
"Maintainer: Debian PHP Maintainers\n".
|
||||||
|
"Description: PHP FPM\n";
|
||||||
|
};
|
||||||
|
return [ package_info('php8.2-fpm') ];
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach my $status (qw(ii iH iU iF iW it)) {
|
||||||
|
ok(@{package_info_for_status($status)},
|
||||||
|
"returns details for package status $status");
|
||||||
|
}
|
||||||
|
is_deeply(package_info_for_status('rc'), [],
|
||||||
|
'does not return details for a removed package');
|
||||||
|
is_deeply(package_info_for_status('un'), [],
|
||||||
|
'does not return details for a not-installed package');
|
||||||
|
|
||||||
|
done_testing();
|
||||||
112
t/webmin-passwd.t
Normal file
112
t/webmin-passwd.t
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
#!/usr/bin/perl
|
||||||
|
|
||||||
|
use strict;
|
||||||
|
use warnings;
|
||||||
|
use Test::More;
|
||||||
|
use File::Basename qw(dirname);
|
||||||
|
use File::Spec;
|
||||||
|
|
||||||
|
my $root = File::Spec->rel2abs(
|
||||||
|
File::Spec->catdir(dirname(__FILE__), '..'));
|
||||||
|
unshift(@INC, $root) if (!grep { $_ eq $root } @INC);
|
||||||
|
my $script = File::Spec->catfile($root, 'bin', 'passwd');
|
||||||
|
do $script or die "failed to load $script: $@ $!";
|
||||||
|
|
||||||
|
sub capture_stderr
|
||||||
|
{
|
||||||
|
my ($code) = @_;
|
||||||
|
my $output = '';
|
||||||
|
open(my $stderr, '>', \$output) or die "open captured stderr: $!";
|
||||||
|
{
|
||||||
|
local *STDERR = $stderr;
|
||||||
|
$code->();
|
||||||
|
}
|
||||||
|
return $output;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub prompt_with
|
||||||
|
{
|
||||||
|
my ($input) = @_;
|
||||||
|
my $output = '';
|
||||||
|
open(my $stdin, '<', \$input) or die "open simulated stdin: $!";
|
||||||
|
open(my $stdout, '>', \$output) or die "open captured stdout: $!";
|
||||||
|
my $target;
|
||||||
|
{
|
||||||
|
local *STDIN = $stdin;
|
||||||
|
local *STDOUT = $stdout;
|
||||||
|
$target = prompt_password_target('root');
|
||||||
|
}
|
||||||
|
return ($target, $output);
|
||||||
|
}
|
||||||
|
|
||||||
|
is(choose_password_target({}, 'web-only', 0, 0), 'webmin',
|
||||||
|
'Webmin-only account does not need a target choice');
|
||||||
|
is(choose_password_target({ webmin => 1 }, 'root', 1, 0), 'webmin',
|
||||||
|
'Webmin-only target can be selected explicitly');
|
||||||
|
is(choose_password_target({ unix => 1 }, 'root', 1, 1), 'unix',
|
||||||
|
'Unix target can be selected explicitly');
|
||||||
|
is(choose_password_target({ stdout => 1 }, 'root', 1, 1), 'webmin',
|
||||||
|
'hash-only output does not prompt for a password target');
|
||||||
|
|
||||||
|
my $explicit_webmin_warning = capture_stderr(sub {
|
||||||
|
is(choose_password_target(
|
||||||
|
{ webmin => 1, password => 'secret' }, 'root', 1, 1),
|
||||||
|
'webmin',
|
||||||
|
'explicit Webmin-only password does not prompt');
|
||||||
|
});
|
||||||
|
is($explicit_webmin_warning, '',
|
||||||
|
'explicit Webmin-only password does not emit a Unix-user warning');
|
||||||
|
|
||||||
|
my $password_warning = capture_stderr(sub {
|
||||||
|
is(choose_password_target({ password => 'secret' }, 'root', 1, 1),
|
||||||
|
'webmin',
|
||||||
|
'command-line password explicitly selects Webmin-only behavior');
|
||||||
|
});
|
||||||
|
like($password_warning, qr/--password option explicitly sets/,
|
||||||
|
'command-line password warns about separate Webmin-only authentication');
|
||||||
|
|
||||||
|
my $warning = capture_stderr(sub {
|
||||||
|
is(choose_password_target({}, 'root', 1, 0), 'webmin',
|
||||||
|
'non-interactive compatibility path keeps Webmin-only behavior');
|
||||||
|
});
|
||||||
|
like($warning, qr/is also a Unix user/,
|
||||||
|
'non-interactive compatibility path warns about the matching Unix user');
|
||||||
|
like($warning, qr/--unix or --webmin-only/,
|
||||||
|
'non-interactive warning explains how to select a target');
|
||||||
|
|
||||||
|
my ($default_target, $default_output) = prompt_with("\n");
|
||||||
|
is($default_target, 'unix', 'interactive prompt defaults to Unix password');
|
||||||
|
like($default_output, qr/Unix authentication in Webmin.*recommended/s,
|
||||||
|
'interactive prompt labels Unix authentication as recommended');
|
||||||
|
like($default_output, qr/separate Webmin-only password/,
|
||||||
|
'interactive prompt explains the separate password choice');
|
||||||
|
|
||||||
|
my ($webmin_target) = prompt_with("2\n");
|
||||||
|
is($webmin_target, 'webmin',
|
||||||
|
'interactive prompt accepts the Webmin-only password choice');
|
||||||
|
|
||||||
|
my ($retry_target, $retry_output) = prompt_with("invalid\nu\n");
|
||||||
|
is($retry_target, 'unix', 'interactive prompt accepts Unix shorthand');
|
||||||
|
like($retry_output, qr/Invalid selection/,
|
||||||
|
'interactive prompt retries invalid selections');
|
||||||
|
|
||||||
|
foreach my $case (
|
||||||
|
[ { unix => 1, webmin => 1 }, qr/cannot be used together/,
|
||||||
|
'conflicting targets are rejected' ],
|
||||||
|
[ { unix => 1, stdout => 1 }, qr/cannot be used together/,
|
||||||
|
'Unix target cannot be combined with hash-only output' ],
|
||||||
|
[ { unix => 1, password => 'secret' }, qr/system passwd command/,
|
||||||
|
'Unix target rejects a command-line password' ],
|
||||||
|
[ { unix => 1 }, qr/doesn't exist/,
|
||||||
|
'Unix target requires a matching Unix user' ],
|
||||||
|
) {
|
||||||
|
my ($options, $error, $name) = @$case;
|
||||||
|
my $ok = eval {
|
||||||
|
choose_password_target($options, 'missing-user', 0, 0);
|
||||||
|
1;
|
||||||
|
};
|
||||||
|
ok(!$ok, $name);
|
||||||
|
like($@, $error, "$name reports the reason");
|
||||||
|
}
|
||||||
|
|
||||||
|
done_testing();
|
||||||
@@ -14971,8 +14971,7 @@ if (&read_env_file($wconfig, \%wconfig) &&
|
|||||||
my ($whost) = split(/:/, $ENV{'HTTP_HOST'});
|
my ($whost) = split(/:/, $ENV{'HTTP_HOST'});
|
||||||
my $wurl = ($wminiserv{'ssl'} ? 'https' : 'http').'://'.$whost;
|
my $wurl = ($wminiserv{'ssl'} ? 'https' : 'http').'://'.$whost;
|
||||||
if ($wminiserv{'port'} &&
|
if ($wminiserv{'port'} &&
|
||||||
$wminiserv{'port'} != 80 &&
|
$wminiserv{'port'} != 80 && $wminiserv{'port'} != 443) {
|
||||||
$wminiserv{'port'} != 443) {
|
|
||||||
$wurl .= ":$wminiserv{'port'}";
|
$wurl .= ":$wminiserv{'port'}";
|
||||||
}
|
}
|
||||||
$wurl .= $wminiserv{'webprefix'} if ($wminiserv{'webprefix'});
|
$wurl .= $wminiserv{'webprefix'} if ($wminiserv{'webprefix'});
|
||||||
|
|||||||
Reference in New Issue
Block a user