mirror of
https://github.com/webmin/webmin.git
synced 2026-06-04 20:30:22 +01:00
Add raw config edit ACL and server shortcut
* Note: Gate manual Nginx config editing behind a dedicated ACL and add a per-server quick edit icon for the owning config file.
This commit is contained in:
@@ -38,6 +38,11 @@ print &ui_table_row($text{'acl_root'},
|
||||
print &ui_table_row($text{'acl_global'},
|
||||
&ui_yesno_radio("global", $o->{'global'}));
|
||||
|
||||
# Can manually edit configuration files?
|
||||
print &ui_table_row($text{'acl_manual'},
|
||||
&ui_yesno_radio("manual",
|
||||
defined($o->{'manual'}) ? $o->{'manual'} : $o->{'global'}));
|
||||
|
||||
# Can edit log files?
|
||||
print &ui_table_row($text{'acl_logs'},
|
||||
&ui_yesno_radio("logs", $o->{'logs'}));
|
||||
@@ -59,6 +64,7 @@ $o->{'edit'} = $in{'edit'};
|
||||
$o->{'create'} = $in{'create'};
|
||||
$o->{'root'} = $in{'root'};
|
||||
$o->{'global'} = $in{'global'};
|
||||
$o->{'manual'} = $in{'manual'};
|
||||
$o->{'logs'} = $in{'logs'};
|
||||
$o->{'user'} = $in{'user'};
|
||||
$o->{'stop'} = $in{'stop'};
|
||||
|
||||
@@ -6,11 +6,11 @@ use warnings;
|
||||
require './nginx-lib.pl';
|
||||
&ReadParse();
|
||||
our (%text, %in, %access);
|
||||
$access{'global'} || &error($text{'index_eglobal'});
|
||||
&can_edit_manual_config() || &error($text{'manual_ecannot'});
|
||||
|
||||
&ui_print_header(undef, $text{'manual_title'}, "");
|
||||
|
||||
my @files = &get_all_config_files();
|
||||
my @files = &get_manual_config_files();
|
||||
$in{'file'} ||= $files[0];
|
||||
&indexof($in{'file'}, @files) >= 0 || &error($text{'manual_efile'});
|
||||
|
||||
@@ -38,4 +38,3 @@ print &ui_table_end();
|
||||
print &ui_form_end([ [ undef, $text{'save'} ] ]);
|
||||
|
||||
&ui_print_footer("", $text{'index_return'});
|
||||
|
||||
|
||||
@@ -28,10 +28,18 @@ if ($in{'id'}) {
|
||||
my @spages = ( $access{'logs'} ? ( "slogs" ) : ( ),
|
||||
"sdocs", "ssl", "fcgi", "sssi", "sgzip", "sproxy",
|
||||
"saccess", "srewrite", );
|
||||
my @slinks = map { "edit_".$_.".cgi?id=".&urlize($in{'id'}) } @spages;
|
||||
my @stitles = map { $text{$_."_title"} } @spages;
|
||||
my @sicons = map { "images/".$_.".gif" } @spages;
|
||||
if (&can_edit_manual_config() && &can_edit_manual_file($server->{'file'})) {
|
||||
push(@slinks, "edit_manual.cgi?file=".&urlize($server->{'file'}));
|
||||
push(@stitles, $text{'manual_server'});
|
||||
push(@sicons, "images/manual.gif");
|
||||
}
|
||||
&icons_table(
|
||||
[ map { "edit_".$_.".cgi?id=".&urlize($in{'id'}) } @spages ],
|
||||
[ map { $text{$_."_title"} } @spages ],
|
||||
[ map { "images/".$_.".gif" } @spages ],
|
||||
\@slinks,
|
||||
\@stitles,
|
||||
\@sicons,
|
||||
);
|
||||
|
||||
# Show table for locations
|
||||
|
||||
@@ -48,7 +48,8 @@ print &ui_tabs_start(\@tabs, "mode", $mode, 1);
|
||||
if ($access{'global'}) {
|
||||
# Show icons for global config types
|
||||
print &ui_tabs_start_tab("mode", "global");
|
||||
my @gpages = ( "net", "mime", "logs", "docs", "ssi", "misc", "manual" );
|
||||
my @gpages = ( "net", "mime", "logs", "docs", "ssi", "misc",
|
||||
&can_edit_manual_config() ? ( "manual" ) : ( ) );
|
||||
&icons_table(
|
||||
[ map { "edit_".$_.".cgi" } @gpages ],
|
||||
[ map { $text{$_."_title"} } @gpages ],
|
||||
|
||||
@@ -151,12 +151,14 @@ opt_essi_types=$1 is not a valid MIME type
|
||||
opt_essi_value_length=Maximum parameter length must be a number
|
||||
|
||||
manual_title=Edit Configuration Files
|
||||
manual_server=Edit Configuration File
|
||||
manual_file=Editing configuration file:
|
||||
manual_ok=Switch
|
||||
manual_efile=Selected file is not part of the Nginx configuration!
|
||||
manual_test=Test new configuration before saving?
|
||||
manual_elink=Dangling symbolic link!
|
||||
manual_err=Failed to save configuration file
|
||||
manual_ecannot=You are not allowed to manually edit configuration files!
|
||||
|
||||
server_create=Create a New Server Block
|
||||
server_edit=Edit Server Block
|
||||
@@ -419,6 +421,7 @@ acl_create=Can create server blocks?
|
||||
acl_stop=Can stop and start Nginx?
|
||||
acl_root=Allowed directories for locations
|
||||
acl_global=Can edit global settings?
|
||||
acl_manual=Can manually edit raw configuration files?
|
||||
acl_logs=Can configure log files?
|
||||
acl_user=Write password files as user
|
||||
|
||||
|
||||
@@ -570,6 +570,16 @@ if ($parent->{'type'}) {
|
||||
return &unique(@rv);
|
||||
}
|
||||
|
||||
# get_manual_config_files([&parent])
|
||||
# Returns all config files this user can manually edit
|
||||
sub get_manual_config_files
|
||||
{
|
||||
my ($parent) = @_;
|
||||
my @files = &get_all_config_files($parent);
|
||||
return @files if (!$access{'vhosts'});
|
||||
return grep { &can_edit_manual_file($_) } @files;
|
||||
}
|
||||
|
||||
# directive_indent(&directive, &parent, &file-lines)
|
||||
# Returns the exact whitespace prefix to use when writing a directive
|
||||
sub directive_indent
|
||||
@@ -2186,6 +2196,22 @@ return 0 if (!$name);
|
||||
return &indexoflc($name, split(/\s+/, $access{'vhosts'})) >= 0;
|
||||
}
|
||||
|
||||
# can_edit_manual_config()
|
||||
# Returns 1 if the user can manually edit raw configuration files
|
||||
sub can_edit_manual_config
|
||||
{
|
||||
return defined($access{'manual'}) ? $access{'manual'} : $access{'global'};
|
||||
}
|
||||
|
||||
# can_edit_manual_file(file)
|
||||
# Returns 1 if the user can manually edit some raw configuration file
|
||||
sub can_edit_manual_file
|
||||
{
|
||||
my ($file) = @_;
|
||||
return 1 if (!$access{'vhosts'});
|
||||
return &can_manage_server_file($file);
|
||||
}
|
||||
|
||||
# can_directory(dir)
|
||||
# Check if some directory is under one of the allowed roots
|
||||
sub can_directory
|
||||
|
||||
@@ -7,9 +7,9 @@ require './nginx-lib.pl';
|
||||
&ReadParseMime();
|
||||
our (%text, %in, %access);
|
||||
&error_setup($text{'manual_err'});
|
||||
$access{'global'} || &error($text{'index_eglobal'});
|
||||
&can_edit_manual_config() || &error($text{'manual_ecannot'});
|
||||
|
||||
my @files = &get_all_config_files();
|
||||
my @files = &get_manual_config_files();
|
||||
&indexof($in{'file'}, @files) >= 0 || &error($text{'manual_efile'});
|
||||
|
||||
# Follow links to get the real file
|
||||
|
||||
@@ -385,4 +385,62 @@ subtest 'config change apply flag tracks pending changes' => sub {
|
||||
'apply needed when config change is newer than last apply');
|
||||
};
|
||||
|
||||
subtest 'manual edit ACL is separately configurable' => sub {
|
||||
no warnings 'once';
|
||||
{
|
||||
local %main::access = ( 'global' => 1 );
|
||||
ok(main::can_edit_manual_config(),
|
||||
'manual edit defaults to global ACL for existing users');
|
||||
}
|
||||
{
|
||||
local %main::access = ( 'global' => 0 );
|
||||
ok(!main::can_edit_manual_config(),
|
||||
'manual edit is denied when global ACL default is denied');
|
||||
}
|
||||
{
|
||||
local %main::access = ( 'global' => 1, 'manual' => 0 );
|
||||
ok(!main::can_edit_manual_config(),
|
||||
'manual edit can be disabled for global users');
|
||||
}
|
||||
{
|
||||
local %main::access = ( 'global' => 0, 'manual' => 1 );
|
||||
ok(main::can_edit_manual_config(),
|
||||
'manual edit can be explicitly enabled');
|
||||
}
|
||||
};
|
||||
|
||||
subtest 'manual edit files respect vhost ACL' => sub {
|
||||
my $single = File::Spec->catfile($available, 'manual-single.conf');
|
||||
my $shared = File::Spec->catfile($available, 'manual-shared.conf');
|
||||
write_text($single,
|
||||
server_conf('single.example', "\troot /srv/single;\n"));
|
||||
write_text($shared,
|
||||
server_conf('single.example', "\troot /srv/shared-single;\n").
|
||||
server_conf('other.example', "\troot /srv/shared-other;\n"));
|
||||
symlink($single, File::Spec->catfile($enabled, 'manual-single.conf')) ||
|
||||
die "Failed to symlink manual-single: $!";
|
||||
symlink($shared, File::Spec->catfile($enabled, 'manual-shared.conf')) ||
|
||||
die "Failed to symlink manual-shared: $!";
|
||||
main::flush_config_cache();
|
||||
|
||||
{
|
||||
local %main::access = ( 'manual' => 1,
|
||||
'vhosts' => 'single.example' );
|
||||
ok(main::can_edit_manual_file($single),
|
||||
'restricted user can manually edit their own single-server file');
|
||||
ok(!main::can_edit_manual_file($shared),
|
||||
'restricted user cannot manually edit a shared server file');
|
||||
is_deeply(
|
||||
[ grep { $_ eq $single || $_ eq $shared }
|
||||
main::get_manual_config_files() ],
|
||||
[ $single ],
|
||||
'manual file list excludes shared files for restricted users');
|
||||
}
|
||||
{
|
||||
local %main::access = ( 'manual' => 1 );
|
||||
ok(main::can_edit_manual_file($shared),
|
||||
'unrestricted user can manually edit shared server files');
|
||||
}
|
||||
};
|
||||
|
||||
done_testing();
|
||||
|
||||
Reference in New Issue
Block a user