diff --git a/Anvil/Tools.pm b/Anvil/Tools.pm index 55ae8dc8..2677f6ca 100755 --- a/Anvil/Tools.pm +++ b/Anvil/Tools.pm @@ -150,6 +150,19 @@ sub new # Record the start time. $anvil->data->{ENV_VALUES}{START_TIME} = Time::HiRes::time; + # Set passed parameters if needed. + if (ref($parameter) eq "HASH") + { + ### TODO: Calls to allow the user to override defaults... + # Local parameters... + } + elsif ($parameter) + { + # Um... + print $THIS_FILE." ".__LINE__."; Anvil::Tools->new() invoked with an invalid parameter. Expected a hash reference, but got: [$parameter]\n"; + exit(1); + } + # Get a handle on the various submodules $anvil->Account->parent($anvil); $anvil->Alert->parent($anvil); @@ -207,20 +220,7 @@ sub new $anvil->Get->switches; # Read in the local Anvil! version. - - # Set passed parameters if needed. - if (ref($parameter) eq "HASH") - { - ### TODO: Calls to allow the user to override defaults... - # Local parameters... - } - elsif ($parameter) - { - # Um... - print $THIS_FILE." ".__LINE__."; Anvil::Tools->new() invoked with an invalid parameter. Expected a hash reference, but got: [$parameter]\n"; - exit(1); - } return ($self); } diff --git a/Anvil/Tools/Validate.pm b/Anvil/Tools/Validate.pm index 6f5c656d..0330c10a 100755 --- a/Anvil/Tools/Validate.pm +++ b/Anvil/Tools/Validate.pm @@ -88,7 +88,7 @@ sub parent This validates that a given HTML form field is valid. It takes an input ID and the type of data that is expected. If it is sane, C<< 1 >> is returned. If it fails to validate, C<< 0 >> is returned and C<< cgi::::alert >> is set to C<< 1 >>. -=head2 Parameters; +Parameters; =head3 empty_ok (optional) @@ -222,7 +222,7 @@ NOTE: An empty string is considered invalid. print "The string: [$string] is valid!\n"; } -=head2 Parameters; +Parameters; =head3 string (required) @@ -266,7 +266,7 @@ Checks if the passed-in string is a valid domain name. Returns 'C<< 1 >>' if OK, print "The domain name: [$name] is valid!\n"; } -=head2 Parameters; +Parameters; =head3 name (required) @@ -310,7 +310,7 @@ Checks if the passed-in string is an IPv4 address. Returns 'C<< 1 >>' if OK, 'C< print "The IP address: [$ip] is valid!\n"; } -=head2 Parameters; +Parameters; =head3 ip (required) @@ -367,7 +367,7 @@ sub is_ipv4 Checks if the passed-in string is a valid network MAC address. Returns 'C<< 1 >>' if OK, 'C<< 0 >>' if not. -=head2 Parameters; +Parameters; =head3 mac (required) @@ -408,7 +408,7 @@ NOTE: This method is strict and will only validate numbers without decimal place print "The number: [$number] is valid!\n"; } -=head2 Parameters; +Parameters; =head3 number (required) @@ -458,7 +458,7 @@ sub is_positive_integer This method takes a subnet string and checks to see if it is a valid IPv4 address or CIDR notation. It returns 'C<< 1 >>' if it is a valid address. Otherwise it returns 'C<< 0 >>'. -=head2 Parameters; +Parameters; =head3 subnet (required) @@ -520,7 +520,7 @@ NOTE: This method is strict and will only validate UUIDs that are lower case! print "The UUID: [$string] is valid!\n"; } -=head2 Parameters; +Parameters; =head3 uuid (required) diff --git a/cgi-bin/home b/cgi-bin/home index a14a502e..779a31ad 100755 --- a/cgi-bin/home +++ b/cgi-bin/home @@ -30,6 +30,7 @@ my $anvil = Anvil::Tools->new(); # Set the log level to 2. Setting 3 slows he program down a LOT. $anvil->Log->level({set => 2}); +$anvil->Log->secure({set => 0}); # Read the config and then connect to the database. $anvil->Storage->read_config(); @@ -133,7 +134,7 @@ my $right_buttons = $anvil->Template->get({file => "main.html", name => "button_ configure_button => $anvil->data->{sys}{users}{user_name} ? $anvil->Template->get({file => "main.html", name => "configure_button_on"}) : $anvil->Template->get({file => "main.html", name => "configure_button_off"}), user_button => $anvil->data->{sys}{users}{user_name} ? $anvil->Template->get({file => "main.html", name => "user_button_on"}) : $anvil->Template->get({file => "main.html", name => "user_button_off"}), }}); -my $footer = $anvil->Template->get({file => "main.html", name => "footer", variables => { +my $footer = $anvil->Template->get({file => "main.html", name => "footer", variables => { user => $anvil->data->{sys}{users}{user_name} ? "#!string!message_0034!#" : " ", }}); @@ -261,10 +262,40 @@ sub process_sync_page { my ($anvil) = @_; + # Setup some CGI values we might use. + $anvil->data->{cgi}{new_peer_access}{value} = "" if not defined $anvil->data->{cgi}{new_peer_access}{value}; + $anvil->data->{cgi}{new_peer_password}{value} = "" if not defined $anvil->data->{cgi}{new_peer_password}{value}; + $anvil->data->{cgi}{save}{value} = "" if not defined $anvil->data->{cgi}{save}{value}; + $anvil->data->{cgi}{confirm}{value} = "" if not defined $anvil->data->{cgi}{confirm}{value}; + + # This handles checkboxes + if (defined $anvil->data->{cgi}{new_peer_ping}{value}) + { + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { ">> cgi::new_peer_ping::value" => $anvil->data->{cgi}{new_peer_ping}{value} }}); + $anvil->data->{cgi}{new_peer_ping}{value} = $anvil->data->{cgi}{new_peer_ping}{value} eq "off" ? 0 : 1; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "<< cgi::new_peer_ping::value" => $anvil->data->{cgi}{new_peer_ping}{value} }}); + } + else + { + $anvil->data->{cgi}{new_peer_ping}{value} = 0; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "cgi::new_peer_ping::value" => $anvil->data->{cgi}{new_peer_ping}{value} }}); + } + if (defined $anvil->data->{cgi}{new_peer_bidirection}{value}) + { + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { ">> cgi::new_peer_bidirection::value" => $anvil->data->{cgi}{new_peer_bidirection}{value} }}); + $anvil->data->{cgi}{new_peer_bidirection}{value} = $anvil->data->{cgi}{new_peer_bidirection}{value} eq "off" ? 0 : 1; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "<< cgi::new_peer_bidirection::value" => $anvil->data->{cgi}{new_peer_bidirection}{value} }}); + } + else + { + $anvil->data->{cgi}{new_peer_bidirection}{value} = 0; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "cgi::new_peer_bidirection::value" => $anvil->data->{cgi}{new_peer_bidirection}{value} }}); + } + # Are we adding a new peer? - if ($anvil->data->{cgi}{action}{value} eq "add") + if (($anvil->data->{cgi}{new_peer_access}{value}) && ($anvil->data->{cgi}{new_peer_password}{value} ne "")) { - #add_sync_peer($anvil); + add_sync_peer($anvil); } elsif ($anvil->data->{cgi}{action}{value} eq "remove") { @@ -372,13 +403,130 @@ sub process_sync_page # Build the menu. $anvil->data->{form}{body} = $anvil->Template->get({file => "striker.html", name => "striker-sync", variables => { - inbound_table => $inbound_table, - peer_table => $peer_table, + inbound_table => $inbound_table, + peer_table => $peer_table, + new_peer_access => defined $anvil->data->{cgi}{new_peer_access}{value} ? $anvil->data->{cgi}{new_peer_access}{value} : "", + new_peer_password => defined $anvil->data->{cgi}{new_peer_password}{value} ? $anvil->data->{cgi}{new_peer_password}{value} : "", }}); return(0); } +# This adds a new peer to anvil.conf. +sub add_sync_peer +{ + my ($anvil) = @_; + + # Break up the user, host and port. If anything goes wrong, we'll set an error and send it back. + my $user = $anvil->data->{sys}{database}{user}; + my $host = $anvil->data->{cgi}{new_peer_access}{value}; + my $name = $anvil->data->{sys}{database}{name}; + my $port = 5432; + my $ssh_tcp = 22; + my $peer_uuid = ""; + my $peer_host = ""; + if ($anvil->data->{cgi}{new_peer_access}{value} =~ /,ssh=(\d+)$/) + { + $ssh_tcp = $1; + $anvil->data->{cgi}{new_peer_access}{value} =~ s/,ssh=\d+$//; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + ssh_tcp => $ssh_tcp, + "cgi::new_peer_access::value" => $anvil->data->{cgi}{new_peer_access}{value}, + }}); + } + if ($anvil->data->{cgi}{new_peer_access}{value} =~ /^(.*?)\@(.*?):(\d+)$/) + { + $user = $1; + $host = $2; + $port = $3; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + host => $host, + port => $port, + user => $user, + }}); + } + elsif ($anvil->data->{cgi}{new_peer_access}{value} =~ /^(.*?)\@(.*?)$/) + { + $user = $1; + $host = $2; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + host => $host, + user => $user, + }}); + } + elsif ($anvil->data->{cgi}{new_peer_access}{value} =~ /^(.*?):(\d+)$/) + { + $host = $1; + $port = $2; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + host => $host, + port => $port, + }}); + } + + # Is the host a domain or IP? + # If so, and 'bi-directional' is set, verify we can ssh into the peer. + if ((not $anvil->Validate->is_domain_name({name => $host})) or + (not $anvil->Validate->is_ipv4({ip => $host})) or + ($port < 1) or + ($port > 65536)) + { + # Bad host. + $anvil->data->{form}{error_massage} = $anvil->Template->get({file => "main.html", name => "error_message", variables => { error_message => $anvil->Words->string({key => "striker_warning_0002"}) }}); + } + else + { + # Can we connect to the peer? + (my $error, $peer_uuid) = $anvil->Remote->call({ + password => $anvil->data->{cgi}{new_peer_password}{value}, + target => $ssh_tcp != 22 ? $host.":".$ssh_tcp : $host, + shell_call => $anvil->data->{path}{exe}{dmidecode}." --string system-uuid", + }); + if ($error) + { + # No access + $anvil->data->{form}{error_massage} = $anvil->Template->get({file => "main.html", name => "error_message", variables => { error_message => $anvil->Words->string({key => "striker_warning_0003"}) }}); + } + else + { + # We got the peer's UUID. Get the hostname as well. + $peer_uuid = lc($peer_uuid); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { peer_uuid => $peer_uuid }}); + + (my $error, $peer_host) = $anvil->Remote->call({ + password => $anvil->data->{cgi}{new_peer_password}{value}, + target => $ssh_tcp != 22 ? $host.":".$ssh_tcp : $host, + shell_call => $anvil->data->{path}{exe}{hostnamectl}." --static", + }); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { peer_host => $peer_host }}); + } + } + + # Lastly, verify we can access the peer database. This will involve writting out a .pgpass file, then making a local system call. + my $password = $anvil->data->{cgi}{new_peer_password}{value}; + $password =~ s/:/\:/g; + my $pgpass = $host.":".$port.":".$name.":".$user.":".$password; + + # Write out the .pgpass file. + # TODO: Left off here, write out .pgpass, set the mode to 0600, then call 'psql --host 10.1.4.1 --port 5432 --dbname anvil --username admin --no-password --command "SELECT 1" ' + + my $db_access = $anvil->System->call({uuid => $peer_uuid}); + + # Is it confirmed? + if (not $anvil->data->{cgi}{confirm}{value}) + { + # Show the screen the confirm the addition. + $anvil->data->{form}{body} = $anvil->Template->get({file => "striker.html", name => "confirm-new-peer", variables => { + user + }}); + } + else + { + } + + return(0); +} + # This shows the menus for configuring Striker. sub configure_striker { diff --git a/html/skins/alteeve/striker.html b/html/skins/alteeve/striker.html index 12a486b3..5e2a1e39 100644 --- a/html/skins/alteeve/striker.html +++ b/html/skins/alteeve/striker.html @@ -1,6 +1,65 @@ + + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ About to add a new peer +
+ Striker User: + + #!variable!user!# +
+ Host: + + #!variable!host!# +
+ Port: + + #!variable!port!# +
+ Ping before Connect: + + #!variable!ping!# +
+ Bi-directional: + + #!variable!bidirectional!# +
+ When the peer is added, all data from this host will be copied to the peer's database. Depending on how much data this is, how fast the connection is, and how fast the local machine is, this sync process could take a little time. +
+ - +
@@ -156,10 +215,11 @@ + +
- + - + + #!string!striker_0071!#: @@ -170,9 +230,11 @@
- +
@@ -190,7 +252,8 @@ #!string!striker_0075!#:   - #!string!striker_0076!# + #!string!striker_0076!#
+ #!string!striker_0077!# @@ -289,4 +352,3 @@ - diff --git a/share/words.xml b/share/words.xml index 7b9292d2..061a89f7 100644 --- a/share/words.xml +++ b/share/words.xml @@ -400,6 +400,7 @@ Here we will inject 't_0006', which injects 't_0001' which has a variable: [#!st When checked, the peer will be configure to add the local database as a peer at the same time that we add it to this system. Access admin', and the default port is '5432'. If the peer uses these, then you only need to specify the IP address or hostname of the peer. If the user name is not 'admin', then you need to use the format 'user@host. If the TCP port is not '5432', then you need to use 'host:port. If both user and port are different, use the format 'user@host:port'.]]> + 22', you can append: ',ssh=X' where 'X' is the SSH TCP port.]]> Configure Network @@ -407,6 +408,8 @@ Here we will inject 't_0006', which injects 't_0001' which has a variable: [#!st The IP address will change. You will need to reconnect after applying these changes. + The access information appears to not be valid. + Test access to the peer (using SSH) failed. There may be details in the log file. There are not enough network interfaces on this machine. You have: [#!variable!interface_count!#] interface(s), and you need at least: [#!variable!required_interfaces_for_single!#] interfaces to connect to the requested networks (one for Back-Channel and one for each Internet-Facing network).