diff --git a/acl/acl-lib.pl b/acl/acl-lib.pl index 4735bd41b..1f0b89fe7 100755 --- a/acl/acl-lib.pl +++ b/acl/acl-lib.pl @@ -1173,9 +1173,9 @@ $miniserv{'anonymous'} = join(" ", @anon); sub split_userdb_string { my ($str) = @_; -if ($str =~ /^([a-z]+):\/\/([^:]+):([^\@]+)\@([a-z0-9\.\-\_]+)\/([^\?]+)(\?(.*))?$/) { +if ($str =~ /^([a-z]+):\/\/([^:]*):([^\@]*)\@([a-z0-9\.\-\_]+)\/([^\?]+)(\?(.*))?$/) { my ($proto, $user, $pass, $host, $prefix, $argstr) = - ($1, $2, $3, $3, $5, $7); + ($1, $2, $3, $4, $5, $7); my %args = map { split(/=/, $_, 2) } split(/\&/, $argstr); return ($proto, $user, $pass, $host, $prefix, \%args); } @@ -1195,11 +1195,11 @@ if (keys %$args) { return $proto."://".$user.":".$pass."\@".$host."/".$prefix.$argstr; } -# validate_userdb(string) +# validate_userdb(string, [no-table-check]) # Checks if some user database is usable, and if not returns an error message sub validate_userdb { -my ($str) = @_; +my ($str, $notablecheck) = @_; my ($proto, $user, $pass, $host, $prefix, $args) = &split_userdb_string($str); if ($proto eq "mysql" || $proto eq "postgresql") { # Load DBI driver @@ -1223,22 +1223,25 @@ if ($proto eq "mysql" || $proto eq "postgresql") { ref($dbh) || return $dbh; # Validate critical tables - my %tables = ( "webmin_user" => [ "id", "name", "pass" ], - "webmin_group" => [ "id", "name", "desc" ], - "webmin_user_attr" => [ "id", "attr", "value" ], - "webmin_group_attr" => [ "id", "attr", "value" ], - "webmin_user_acl" => [ "id", "module", "attr", "value" ], - "webmin_group_acl" => [ "id", "module", "attr", "value"], - ); - foreach my $t (keys %tables) { - my @cols = @{$tables{$t}}; - my $sql = "select ".join(",", @cols)." from $t limit 1"; - my $cmd = $dbh->prepare($sql); - if (!$cmd || !$cmd->execute()) { - return &text('sql_etable', $t, - &html_escape($dbh->errstr)); + if (!$notablecheck) { + my %tables = + ( "webmin_user" => [ "id", "name", "pass" ], + "webmin_group" => [ "id", "name", "desc" ], + "webmin_user_attr" => [ "id", "attr", "value" ], + "webmin_group_attr" => [ "id", "attr", "value" ], + "webmin_user_acl" => [ "id", "module", "attr", "value" ], + "webmin_group_acl" => [ "id", "module", "attr", "value"], + ); + foreach my $t (keys %tables) { + my @cols = @{$tables{$t}}; + my $sql = "select ".join(",", @cols)." from $t limit 1"; + my $cmd = $dbh->prepare($sql); + if (!$cmd || !$cmd->execute()) { + return &text('sql_etable', $t, + &html_escape($dbh->errstr)); + } + $cmd->finish(); } - $cmd->finish(); } &disconnect_userdb($str, $dbh); return undef; @@ -1304,5 +1307,19 @@ elsif ($str =~ /^ldap:/) { } } +# userdb_table_sql(string) +# Returns SQL statements needed to create all required tables +sub userdb_table_sql +{ +my ($str) = @_; +return ( "create table webmin_user (id int(20), name varchar(255), pass varchar(255))", + "create table webmin_group (id init(20), name varchar(255), desc varchar(255))", + "create table webmin_user_attr (id int(20), attr varchar(32), value varchar(255))", + "create table webmin_group_attr (id int(20), attr varchar(32), value varchar(255))", + "create table webmin_user_acl (id int(20), module varchar(32), attr varchar(32), value varchar(255))", + "create table webmin_group_acl (id int(20), module varchar(32), attr varchar(32), value varchar(255))", + ); +} + 1; diff --git a/acl/edit_sql.cgi b/acl/edit_sql.cgi new file mode 100644 index 000000000..3e6b6d585 --- /dev/null +++ b/acl/edit_sql.cgi @@ -0,0 +1,79 @@ +#!/usr/local/bin/perl +# Show form for an external user / group database + +require './acl-lib.pl'; +$access{'sql'} || &error($text{'sql_ecannot'}); +&ui_print_header(undef, $text{'sql_title'}, ""); +&get_miniserv_config(\%miniserv); + +print &ui_form_start("save_sql.cgi"); +print &ui_table_start($text{'sql_header'}, undef, 2); + +($proto, $user, $pass, $host, $prefix, $args) = + &split_userdb_string($miniserv{'userdb'}); + +# Build inputs for MySQL backend +@mysqlgrid = ( ); +push(@mysqlgrid, + $text{'sql_host'}, + &ui_textbox("mysql_host", $proto eq "mysql" ? $host : "", 30)); +push(@mysqlgrid, + $text{'sql_user'}, + &ui_textbox("mysql_user", $proto eq "mysql" ? $user : "", 30)); +push(@mysqlgrid, + $text{'sql_pass'}, + &ui_textbox("mysql_pass", $proto eq "mysql" ? $pass : "", 30)); +push(@mysqlgrid, + $text{'sql_db'}, + &ui_textbox("mysql_db", $proto eq "mysql" ? $prefix : "", 30)); +$mysqlgrid = &ui_grid_table(\@mysqlgrid, 2, 100); + +# Build inputs for PostgreSQL backend +@postgresqlgrid = ( ); +push(@postgresqlgrid, + $text{'sql_host'}, + &ui_textbox("postgresql_host", $proto eq "postgresql" ? $host : "", 30)); +push(@postgresqlgrid, + $text{'sql_user'}, + &ui_textbox("postgresql_user", $proto eq "postgresql" ? $user : "", 30)); +push(@postgresqlgrid, + $text{'sql_pass'}, + &ui_textbox("postgresql_pass", $proto eq "postgresql" ? $pass : "", 30)); +push(@postgresqlgrid, + $text{'sql_db'}, + &ui_textbox("postgresql_db", $proto eq "postgresql" ? $prefix : "", 30)); +$postgresqlgrid = &ui_grid_table(\@postgresqlgrid, 2, 100); + +# Build inputs for LDAP backend +@ldapgrid = ( ); +push(@ldapgrid, + $text{'sql_host'}, + &ui_textbox("ldap_host", $proto eq "ldap" ? $host : "", 30)); +push(@ldapgrid, + $text{'sql_user'}, + &ui_textbox("ldap_user", $proto eq "ldap" ? $user : "", 30)); +push(@ldapgrid, + $text{'sql_pass'}, + &ui_textbox("ldap_pass", $proto eq "ldap" ? $pass : "", 30)); +push(@ldapgrid, + $text{'sql_prefix'}, + &ui_textbox("ldap_prefix", $proto eq "ldap" ? $prefix : "", 30)); +# XXX object classes? +$ldapgrid = &ui_grid_table(\@ldapgrid, 2, 100); + +print &ui_table_row(undef, + &ui_radio_table("proto", $proto, + [ [ '', $text{'sql_none'} ], + [ 'mysql', $text{'sql_mysql'}, $mysqlgrid ], + [ 'postgresql', $text{'sql_postgresql'}, $postgresqlgrid ], + [ 'ldap', $text{'sql_ldap'}, $ldapgrid ] ]), 2); + +print &ui_table_row(undef, + &ui_radio("addto", int($miniserv{'userdb_addto'}), + [ [ 0, $text{'sql_addto0'} ], + [ 1, $text{'sql_addto1'} ] ]), 2); + +print &ui_table_end(); +print &ui_form_end([ [ undef, $text{'save'} ] ]); + +&ui_print_footer("", $text{'index_return'}); diff --git a/acl/lang/en b/acl/lang/en index 833763dba..8239ec49f 100644 --- a/acl/lang/en +++ b/acl/lang/en @@ -218,6 +218,7 @@ log_delete_groups=Deleted $1 Webmin groups log_pass=Changed password restrictions log_unix=Changed unix user authentication log_sync=Changed unix user synchronization +log_sql=Changed user and group database gedit_ecannot=You are not allowed to edit groups gedit_title=Edit Webmin Group @@ -396,3 +397,14 @@ sql_emysqlconnect=Failed to connect to MySQL database : $1 sql_epostgresqldriver=Failed to load PostgreSQL DBI driver sql_epostgresqlconnect=Failed to connect to PostgreSQL database : $1 sql_etable=Failed to query required table $1 : $2 +sql_err=Failed to save user and group database settings +sql_ehost=Missing or un-resolvable hostname +sql_euser=Missing or invalid username (no spaces allowed) +sql_epass=Invalid password (no spaces allowed) +sql_edb=Invalid database name (no spaces allowed) +sql_eprefix=Missing or invalid base DN (no spaces allowed) +sql_eprefix2=Invalid-looking base DN - should be like dc=mydomain,dc=com +sql_title2=Create Missing Tables +sql_tableerr=User and group database settings were successfully saved, but some tables needed by Webmin are missing : $1 +sql_tableerr2=Click the Create Tables to have them created automatically, or manually run the SQL below. +sql_make=Create Tables diff --git a/acl/save_sql.cgi b/acl/save_sql.cgi new file mode 100755 index 000000000..2e793df29 --- /dev/null +++ b/acl/save_sql.cgi @@ -0,0 +1,70 @@ +#!/usr/local/bin/perl +# Save user and group database + +require './acl-lib.pl'; +$access{'pass'} || &error($text{'sql_ecannot'}); +&get_miniserv_config(\%miniserv); +&ReadParse(); +&error_setup($text{'sql_err'}); +$p = $in{'proto'}; + +# Parse inputs +if ($p eq 'mysql' || $p eq 'postgresql' || $p eq 'ldap') { + gethostbyname($in{$p."_host"}) || + $in{$p."_host"} =~ /^(\S+):(\d+)$/ && gethostbyname($1) || + &error($text{'sql_ehost'}); + $in{$p."_user"} =~ /^\S+$/ || &error($text{'sql_euser'}); + $in{$p."_pass"} =~ /^\S*$/ || &error($text{'sql_epass'}); + $host = $in{$p."_host"}; + $user = $in{$p."_user"}; + $pass = $in{$p."_pass"}; + } +if ($p eq 'mysql' || $p eq 'postgresql') { + $in{$p."_db"} =~ /^\S+$/ || &error($text{'sql_edb'}); + $prefix = $in{$p."_db"}; + } +elsif ($p eq 'ldap') { + $in{$p."_prefix"} =~ /^\S+$/ || &error($text{'sql_eprefix'}); + $in{$p."_prefix"} =~ /=/ || &error($text{'sql_eprefix2'}); + $prefix = $in{$p."_prefix"}; + } + +# Create and test connection string +if ($p) { + $str = &join_userdb_string($p, $user, $pass, $host, + $prefix, $args); + $err = &validate_userdb($str, 1); + &error($err) if ($err); + } + +&lock_file($ENV{'MINISERV_CONFIG'}); +$miniserv{'userdb'} = $str; +$miniserv{'userdb_addto'} = $in{'addto'}; +&put_miniserv_config(\%miniserv); +&unlock_file($ENV{'MINISERV_CONFIG'}); +&reload_miniserv(); +&webmin_log("sql"); + +# Make sure tables exist +$err = &validate_userdb($str, 0); +if ($err) { + &ui_print_header(undef, $text{'sql_title2'}, ""); + + print &text('sql_tableerr', $err),"

\n"; + print $text{'sql_tableerr2'},"

\n"; + print &ui_form_start("maketables.sql"); + print &ui_form_end([ [ undef, $text{'sql_make'} ] ]); + + print &ui_table_start(undef, undef, 2); + foreach $sql (&userdb_table_sql($str)) { + print &ui_table_row(undef, + "

".&html_escape($sql)."
", 2); + } + print &ui_table_end(); + + &ui_print_footer("", $text{'index_return'}); + } +else { + &redirect(""); + } +