* Decided to abandon using apache security and to integrate it into Striker itself. This will allow for layers of access, easier account sync'ing, etc. Further, there is no proper way to handle logging out on apache, and they use md5 * 1000 iterations (with a salt, to be fair) for security... Not exactly the strongest.

* Created the new Account.pm module for handling account related tasks.
* Moved 'sys::stty' to 'sys::terminal:stty'.
* Created Get->_salt which generates a (pseudo) random salt for internal passwords and Get->_wrap_to that checks the current terminal width.
* Added the parameter 'redirect_stderr' (default '1') to System->call to optionally not redirect STDERR to STDOUT (needed for tput to be useful as STDERR is a terminal of sorts).
* Disabled System->change_apache_password (to be removed entirely later).
* Created Words->_wrap_string to wrap text strings to the active terminal width.
* Added the user's table to the core SQL. Also deleted the empty Anvil/Tools.sql file.

Signed-off-by: Digimer <digimer@alteeve.ca>
main
Digimer 7 years ago
parent de333704b5
commit 9a37f66468
  1. 31
      Anvil/Tools.pm
  2. 0
      Anvil/Tools.sql
  3. 78
      Anvil/Tools/Account.pm
  4. 2
      Anvil/Tools/Alert.pm
  5. 110
      Anvil/Tools/Get.pm
  6. 6
      Anvil/Tools/Storage.pm
  7. 446
      Anvil/Tools/System.pm
  8. 150
      Anvil/Tools/Words.pm
  9. 8
      tools/anvil-change-password
  10. 67
      tools/anvil.sql

@ -38,6 +38,7 @@ binmode(STDOUT, ':encoding(utf-8)');
# I intentionally don't use EXPORT, @ISA and the like because I want my "subclass"es to be accessed in a
# somewhat more OO style. I know some may wish to strike me down for this, but I like the idea of accessing
# methods via their containing module's name. (A La: C<< $anvil->Module->method >> rather than C<< $anvil->method >>).
use Anvil::Tools::Account;
use Anvil::Tools::Alert;
use Anvil::Tools::Database;
use Anvil::Tools::Convert;
@ -113,6 +114,7 @@ sub new
my $parameter = shift;
my $self = {
HANDLE => {
ACCOUNT => Anvil::Tools::Account->new(),
ALERT => Anvil::Tools::Alert->new(),
DATABASE => Anvil::Tools::Database->new(),
CONVERT => Anvil::Tools::Convert->new(),
@ -149,6 +151,7 @@ sub new
$anvil->data->{ENV_VALUES}{START_TIME} = Time::HiRes::time;
# Get a handle on the various submodules
$anvil->Account->parent($anvil);
$anvil->Alert->parent($anvil);
$anvil->Database->parent($anvil);
$anvil->Convert->parent($anvil);
@ -197,6 +200,9 @@ sub new
$anvil->Storage->read_config({file => $anvil->data->{path}{configs}{'anvil.conf'}});
}
# Get the local host UUID.
$anvil->Get->host_uuid;
# Read in any command line switches.
$anvil->Get->switches;
@ -365,6 +371,18 @@ The methods below are used to access methods of submodules using 'C<< $anvil->Mo
=cut
=head2 Account
Access the C<Acount.pm> methods via 'C<< $anvil->Alert->method >>'.
=cut
sub Account
{
my $self = shift;
return ($self->{HANDLE}{ACCOUNT});
}
=head2 Alert
Access the C<Alert.pm> methods via 'C<< $anvil->Alert->method >>'.
@ -700,8 +718,12 @@ sub _set_defaults
user => "admin",
},
host_type => "",
host_uuid => "",
log_file => "/var/log/anvil.log",
stty => "",
terminal => {
columns => 80,
stty => "",
},
use_base2 => 1,
};
$anvil->data->{defaults} = {
@ -762,7 +784,7 @@ sub _set_paths
},
data => {
group => "/etc/group",
htpasswd => "/var/www/home/htpasswd",
'.htpasswd' => "/etc/httpd/.htpasswd",
host_uuid => "/etc/anvil/host.uuid",
passwd => "/etc/passwd",
},
@ -824,6 +846,7 @@ sub _set_paths
systemctl => "/usr/bin/systemctl",
timeout => "/usr/bin/timeout",
touch => "/usr/bin/touch",
tput => "/usr/bin/tput",
'tr' => "/usr/bin/tr",
usermod => "/usr/sbin/usermod",
uuidgen => "/usr/bin/uuidgen",
@ -929,11 +952,11 @@ sub catch_sig
{
print "Process with PID: [$$] exiting on SIG".$signal.".\n";
if ($anvil->data->{sys}{stty})
if ($anvil->data->{sys}{terminal}{stty})
{
# Restore the terminal.
print "Restoring the terminal\n";
$anvil->System->call({shell_call => $anvil->data->{path}{exe}{stty}." ".$anvil->data->{sys}{stty}});
$anvil->System->call({shell_call => $anvil->data->{path}{exe}{stty}." ".$anvil->data->{sys}{terminal}{stty}});
}
}
$anvil->nice_exit({code => 255});

@ -0,0 +1,78 @@
package Anvil::Tools::Account;
#
# This module contains methods used to handle user accounts, logging in and out.
#
use strict;
use warnings;
use Scalar::Util qw(weaken isweak);
our $VERSION = "3.0.0";
my $THIS_FILE = "Account.pm";
### Methods;
#
=pod
=encoding utf8
=head1 NAME
Anvil::Tools::Account
Provides all methods related to user management and log in/out features.
=head1 SYNOPSIS
use Anvil::Tools;
# Get a common object handle on all Anvil::Tools modules.
my $anvil = Anvil::Tools->new();
# Access to methods using '$anvil->Account->X'.
=head1 METHODS
Methods in the core module;
=cut
sub new
{
my $class = shift;
my $self = {};
bless $self, $class;
return ($self);
}
# Get a handle on the Anvil::Tools object. I know that technically that is a sibling module, but it makes more
# sense in this case to think of it as a parent.
sub parent
{
my $self = shift;
my $parent = shift;
$self->{HANDLE}{TOOLS} = $parent if $parent;
# Defend against memory leads. See Scalar::Util'.
if (not isweak($self->{HANDLE}{TOOLS}))
{
weaken($self->{HANDLE}{TOOLS});;
}
return ($self->{HANDLE}{TOOLS});
}
#############################################################################################################
# Public methods #
#############################################################################################################
1;

@ -23,7 +23,7 @@ my $THIS_FILE = "Alert.pm";
Anvil::Tools::Alert
Provides all methods related warnings and alerts.
Provides all methods related to warnings and alerts.
=head1 SYNOPSIS

@ -22,6 +22,8 @@ my $THIS_FILE = "Get.pm";
# switches
# users_home
# uuid
# _salt
# _wrap_to
=pod
@ -444,7 +446,7 @@ sub host_uuid
my $anvil = $self->parent;
my $debug = defined $parameter->{debug} ? $parameter->{debug} : 3;
my $set = defined $parameter->{set} ? $parameter->{set} : "";
my $set = defined $parameter->{set} ? $parameter->{set} : "";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { set => $set }});
if ($set)
@ -514,7 +516,13 @@ sub host_uuid
}
}
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { "HOST::UUID" => $anvil->data->{HOST}{UUID} }});
# We'll also store the host UUID in a variable.
if ((not $anvil->data->{sys}{host_uuid}) && ($anvil->data->{HOST}{UUID}))
{
$anvil->data->{sys}{host_uuid} = $anvil->data->{HOST}{UUID};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { "sys::host_uuid" => $anvil->data->{sys}{host_uuid} }});
}
return($anvil->data->{HOST}{UUID});
}
@ -784,7 +792,7 @@ sub uuid
my $debug = defined $parameter->{debug} ? $parameter->{debug} : 3;
### TODO: System calls are slow, find a pure-perl UUID generator
my $uuid = $anvil->System->call({shell_call => $anvil->data->{path}{exe}{uuidgen}." --random"});
my $uuid = $anvil->System->call({debug => $debug, shell_call => $anvil->data->{path}{exe}{uuidgen}." --random"});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { uuid => $uuid }});
return($uuid);
@ -799,3 +807,99 @@ sub uuid
#############################################################################################################
# Private functions #
#############################################################################################################
=head2 _salt
This generates a random salt string for use with internal Striker passwords.
=cut
sub _salt
{
my $self = shift;
my $parameter = shift;
my $anvil = $self->parent;
my $debug = defined $parameter->{debug} ? $parameter->{debug} : 3;
my $salt = "";
my $salt_length = 16;
my @seed = (" ", "~", "`", "!", "#", "^", "&", "*", "(", ")", "-", "_", "+", "=", "{", "[", "}", "]", "|", ":", ";", "'", ",", "<", ".", ">", "/");
my @alpha = ("a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z");
my $seed_count = @seed;
my $alpha_count = @alpha;
my $skip_count = 0;
for (1..$salt_length)
{
# We want to have a little randomness in the salt length, but not skip tooooo many times.
if ((int(rand(20)) == 2) && ($skip_count <= 3))
{
$skip_count++;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { skip_count => $skip_count }});
next;
}
# What character will this string be?
my $this_integer = int(rand(3));
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { this_integer => $this_integer }});
if ($this_integer == 0)
{
# Inject a random digit
$salt .= int(rand(10));
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { salt => $salt }});
}
elsif ($this_integer == 1)
{
# Inject a random letter
$salt .= $alpha[int(rand($alpha_count))];
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { salt => $salt }});
}
else
{
# Inject a random character
$salt .= $seed[int(rand($seed_count))];
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { salt => $salt }});
}
}
return($salt);
}
=head2 _wrap_to
This determines how wide the user's terminal currently is and returns that width, as well as store it in C<< sys::terminal::columns >>.
This takes no parameters. If there is a problem reading the column width, C<< 0 >> will be returned.
=cut
sub _wrap_to
{
my $self = shift;
my $parameter = shift;
my $anvil = $self->parent;
my $debug = defined $parameter->{debug} ? $parameter->{debug} : 3;
# Get the column width
my $shell_call = $anvil->data->{path}{exe}{tput}." cols";
my $columns = $anvil->System->call({
debug => $debug,
redirect_stderr => 0,
shell_call => $shell_call,
});
if ((not defined $columns) or ($columns !~ /^\d+$/))
{
# Set 0.
$columns = 0;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { columns => $columns }});
}
else
{
# Got a good value
$anvil->data->{sys}{terminal}{columns} = $columns;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { 'sys::terminal::columns' => $anvil->data->{sys}{terminal}{columns} }});
}
return($columns);
}
1;

@ -1808,7 +1808,7 @@ This writes out a file, either locally or on a remote system. It can optionally
mode => "0644",
});
If it fails to write the file, an alert will be logged.
Returns C<< 0 >> on success. C<< 1 >> or an error string will be returned otherwise.
Parameters;
@ -1879,6 +1879,7 @@ sub write_file
my $target = defined $parameter->{target} ? $parameter->{target} : "";
my $user = defined $parameter->{user} ? $parameter->{user} : "root";
my $remote_user = defined $parameter->{remote_user} ? $parameter->{remote_user} : "root";
my $error = 0;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, secure => $secure, list => {
body => $body,
file => $file,
@ -1901,7 +1902,6 @@ sub write_file
user => $user,
}});
my $error = 0;
# Make sure the passed file is a full path and file name.
if ($file !~ /^\/\w/)
@ -2111,7 +2111,7 @@ fi";
}
}
return(0);
return($error);
}

@ -15,7 +15,7 @@ my $THIS_FILE = "System.pm";
### Methods;
# call
# change_apache_password
# (disabled) change_apache_password
# change_shell_user_password
# check_daemon
# check_memory
@ -105,6 +105,10 @@ Parameters;
This is the line number of the source file that called this method. Useful for logging and debugging.
=head3 redirect_stderr (optional, default '1')
By default, C<< STDERR >> is redirected to C<< STDOUT >>. If this is set to C<< 0 >>, this is disabled.
=head3 secure (optional)
If set to 'C<< 1 >>', the shell call will be treated as if it contains a password or other sensitive data for logging.
@ -125,11 +129,17 @@ sub call
my $anvil = $self->parent;
my $debug = defined $parameter->{debug} ? $parameter->{debug} : 3;
my $line = defined $parameter->{line} ? $parameter->{line} : __LINE__;
my $shell_call = defined $parameter->{shell_call} ? $parameter->{shell_call} : "";
my $secure = defined $parameter->{secure} ? $parameter->{secure} : 0;
my $source = defined $parameter->{source} ? $parameter->{source} : $THIS_FILE;
$anvil->Log->variables({source => $source, line => $line, level => $debug, secure => $secure, list => { shell_call => $shell_call }});
my $line = defined $parameter->{line} ? $parameter->{line} : __LINE__;
my $redirect_stderr = defined $parameter->{redirect_stderr} ? $parameter->{redirect_stderr} : 1;
my $shell_call = defined $parameter->{shell_call} ? $parameter->{shell_call} : "";
my $secure = defined $parameter->{secure} ? $parameter->{secure} : 0;
my $source = defined $parameter->{source} ? $parameter->{source} : $THIS_FILE;
my $redirect = $redirect_stderr ? " 2>&1" : "";
$anvil->Log->variables({source => $source, line => $line, level => $debug, secure => $secure, list => {
shell_call => $shell_call,
redirect => $redirect,
redirect_stderr => $redirect_stderr,
}});
my $output = "#!error!#";
if (not $shell_call)
@ -168,7 +178,7 @@ sub call
# Make the system call
$output = "";
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, secure => $secure, key => "log_0011", variables => { shell_call => $shell_call }});
open (my $file_handle, $shell_call." 2>&1 |") or $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, secure => $secure, priority => "err", key => "log_0014", variables => { shell_call => $shell_call, error => $! }});
open (my $file_handle, $shell_call.$redirect." |") or $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, secure => $secure, priority => "err", key => "log_0014", variables => { shell_call => $shell_call, error => $! }});
while(<$file_handle>)
{
chomp;
@ -187,165 +197,261 @@ sub call
return($output);
}
=head2 change_apache_password
This changes the password used to connet to Striker's web interface. If the C<< .htpasswd >> file isn't found, this method will effectively enable the password feature.
The return code will be C<< 255 >> on internal error. Otherwise, it will be the code returned from the C<< passwd >> call.
Parameters;
=head3 new_password (required)
This is the new password to set. The user should be encouraged to select a good (long) password.
=head3 password (optional)
If you are changing the apache password on a remote machine, this is the password used to connect to that machine. If not passed, an attempt to connect with passwordless SSH will be made (but this won't be the case in most instances). Ignored if C<< target >> is not given.
=head3 port (optional, default 22)
This is the TCP port number to use if connecting to a remote machine over SSH. Ignored if C<< target >> is not given.
=head3 remote_user (optional, default root)
If C<< target >> is set and we're changing the password for a remote user, this is the user we B<< log into >> the remote machine as, B<< not >> the user whose password we will change.
=head3 target (optional)
This is the IP address or (resolvable) host name of the target machine whose user account you want to change the password
=head3 user (optional, default 'sys::apache::user' or 'admin')
This is the apache user name to use. If another name existed before in C<< .htpasswd >>, that old user name will be removed.
=cut
sub change_apache_password
{
my $self = shift;
my $parameter = shift;
my $anvil = $self->parent;
my $debug = defined $parameter->{debug} ? $parameter->{debug} : 3;
my $new_password = defined $parameter->{new_password} ? $parameter->{new_password} : "";
my $password = defined $parameter->{password} ? $parameter->{password} : "";
my $port = defined $parameter->{port} ? $parameter->{port} : "";
my $remote_user = defined $parameter->{remote_user} ? $parameter->{remote_user} : $anvil->data->{sys}{apache}{user};
my $target = defined $parameter->{target} ? $parameter->{target} : "";
my $user = defined $parameter->{user} ? $parameter->{user} : "";
my $return_code = 255;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, secure => 0, list => {
user => $user,
target => $target,
port => $port,
remote_user => $remote_user,
new_password => $anvil->Log->secure ? $new_password : "--",
password => $anvil->Log->secure ? $password : "--",
}});
# Set the user to 'admin' if it's not set.
if (not $user)
{
$user = "admin";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, secure => 0, list => { user => $user }});
}
# OK, what about a password?
if (not $new_password)
{
# Um...
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0020", variables => { method => "Systeme->change_apache_password()", parameter => "new_password" }});
return($return_code);
}
# Only the root user can do this!
# $< == real UID, $> == effective UID
if (($< != 0) && ($> != 0))
{
# Not root
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0156", variables => { method => "Systeme->change_apache_password()" }});
return($return_code);
}
# Read httpd.conf and make sure apache is configured for .htpasswd.
my $httpd_conf = $anvil->Storage->read_file({
file => $anvil->data->{path}{configs}{'httpd.conf'},
debug => $debug,
target => $target,
port => $port,
remote_user => $remote_user,
password => $password,
});
if ($httpd_conf eq "!!error!!")
{
# We're done.
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, secure => 0, list => { httpd_conf => $httpd_conf }});
return($return_code);
}
my $rewrite = 0;
my $new_file = "";
my $in_directory = 0;
foreach my $line (split/\n/, $httpd_conf)
{
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, secure => 0, list => { line => $line }});
if ($in_directory)
{
if ($line =~ /^(\s+)AllowOverride None/i)
{
# We need to update.
my $space = $1;
$rewrite = 1;
$new_file .= $space."AllowOverride AuthConfig\n";
next;
}
elsif ($line eq "</Directory>")
{
$in_directory = 0;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, secure => 0, list => { in_directory => $in_directory }});
}
}
elsif ($line eq '<Directory "/var/www/html">')
{
$in_directory = 1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, secure => 0, list => { in_directory => $in_directory }});
}
$new_file .= $line."\n";
}
if ($rewrite)
{
# Back it up first.
my $backup_file = $anvil->Storage->backup({
file => $anvil->data->{path}{configs}{'httpd.conf'},
debug => $debug,
target => $target,
port => $port,
remote_user => $remote_user,
password => $password,
});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { backup_file => $backup_file }});
if ($backup_file)
{
# Proceed.
$anvil->Storage->write_file({
body => $new_file,
debug => $debug,
file => $anvil->data->{path}{configs}{'httpd.conf'},
overwrite => 1,
secure => 0,
target => $target,
port => $port,
remote_user => $remote_user,
password => $password,
});
}
}
return($return_code);
}
# =head2 change_apache_password
#
# NOTE: Likely to be removed.
#
# This changes the password used to connet to Striker's web interface. If the C<< .htpasswd >> file isn't found, this method will effectively enable the password feature.
#
# The return code will be C<< 255 >> on internal error. Otherwise, it will be the code returned from the C<< passwd >> call.
#
# Parameters;
#
# =head3 new_password (required)
#
# This is the new password to set. The user should be encouraged to select a good (long) password.
#
# =head3 password (optional)
#
# If you are changing the apache password on a remote machine, this is the password used to connect to that machine. If not passed, an attempt to connect with passwordless SSH will be made (but this won't be the case in most instances). Ignored if C<< target >> is not given.
#
# =head3 port (optional, default 22)
#
# This is the TCP port number to use if connecting to a remote machine over SSH. Ignored if C<< target >> is not given.
#
# =head3 remote_user (optional, default root)
#
# If C<< target >> is set and we're changing the password for a remote user, this is the user we B<< log into >> the remote machine as, B<< not >> the user whose password we will change.
#
# =head3 target (optional)
#
# This is the IP address or (resolvable) host name of the target machine whose user account you want to change the password
#
# =head3 user (optional, default 'sys::apache::user' or 'admin')
#
# This is the apache user name to use. If another name existed before in C<< .htpasswd >>, that old user name will be removed.
#
# =cut
# sub change_apache_password
# {
# my $self = shift;
# my $parameter = shift;
# my $anvil = $self->parent;
# my $debug = defined $parameter->{debug} ? $parameter->{debug} : 3;
#
# my $new_password = defined $parameter->{new_password} ? $parameter->{new_password} : "";
# my $password = defined $parameter->{password} ? $parameter->{password} : "";
# my $port = defined $parameter->{port} ? $parameter->{port} : "";
# my $remote_user = defined $parameter->{remote_user} ? $parameter->{remote_user} : "";
# my $target = defined $parameter->{target} ? $parameter->{target} : "";
# my $user = defined $parameter->{user} ? $parameter->{user} : $anvil->data->{sys}{apache}{user};
# my $return_code = 255;
# $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, secure => 0, list => {
# user => $user,
# target => $target,
# port => $port,
# remote_user => $remote_user,
# new_password => $anvil->Log->secure ? $new_password : "--",
# password => $anvil->Log->secure ? $password : "--",
# }});
#
# # Set the user to 'admin' if it's not set.
# if (not $user)
# {
# $user = "admin";
# $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, secure => 0, list => { user => $user }});
# }
#
# # OK, what about a password?
# if (not $new_password)
# {
# # Um...
# $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0020", variables => { method => "Systeme->change_apache_password()", parameter => "new_password" }});
# return($return_code);
# }
#
# # Only the root user can do this!
# # $< == real UID, $> == effective UID
# if (($< != 0) && ($> != 0))
# {
# # Not root
# $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0156", variables => { method => "Systeme->change_apache_password()" }});
# return($return_code);
# }
#
# # Read httpd.conf and make sure apache is configured for .htpasswd.
# my $httpd_conf = $anvil->Storage->read_file({
# file => $anvil->data->{path}{configs}{'httpd.conf'},
# debug => $debug,
# target => $target,
# port => $port,
# remote_user => $remote_user,
# password => $password,
# });
# if ($httpd_conf eq "!!error!!")
# {
# # We're done.
# $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, secure => 0, list => { httpd_conf => $httpd_conf }});
# return($return_code);
# }
# my $rewrite = 0;
# my $new_file = "";
# my $in_html_directory = 0;
# my $in_cgi_directory = 0;
# my $cgi_first_line = 0;
# foreach my $line (split/\n/, $httpd_conf)
# {
# $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, secure => 0, list => { line => $line, in_cgi_directory => $in_cgi_directory }});
#
# if ($in_html_directory)
# {
# if ($line =~ /^(\s+)AllowOverride None/i)
# {
# # We need to update.
# my $space = $1;
# $rewrite = 1;
# $new_file .= $space."AllowOverride AuthConfig\n";
# next;
# }
# elsif ($line eq "</Directory>")
# {
# $in_html_directory = 0;
# $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, secure => 0, list => { in_html_directory => $in_html_directory }});
# }
# }
# elsif ($line eq '<Directory "/var/www/html">')
# {
# $in_html_directory = 1;
# $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, secure => 0, list => { in_html_directory => $in_html_directory }});
# }
#
# if ($in_cgi_directory)
# {
# $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, secure => 0, list => { cgi_first_line => $cgi_first_line, line => $line }});
# if ((not $cgi_first_line) && ($line =~ /^(\s+)AllowOverride None/i))
# {
# # We need to update.
# my $space = $1;
# $rewrite = 1;
# $new_file .= $space."# Password login\n";
# $new_file .= $space."AuthType Basic\n";
# $new_file .= $space."AuthName \"Striker - The Anvil! Dashboard\"\n";
# $new_file .= $space."AuthUserFile ".$anvil->data->{path}{data}{'.htpasswd'}."\n";
# $new_file .= $space."Require valid-user \n";
#
# $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, secure => 0, list => { space => $space, rewrite => $rewrite }});
# }
# elsif ($line eq "</Directory>")
# {
# $in_cgi_directory = 0;
# $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, secure => 0, list => { in_cgi_directory => $in_cgi_directory }});
# }
# elsif ($line =~ /Require all granted/)
# {
# # We don't want this line.
# $rewrite = 1;
# $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, secure => 0, list => { rewrite => $rewrite }});
# next;
# }
# $cgi_first_line = 1;
# }
# elsif ($line eq '<Directory "/var/www/cgi-bin">')
# {
# $in_cgi_directory = 1;
# $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, secure => 0, list => { in_cgi_directory => $in_cgi_directory }});
# }
#
# $new_file .= $line."\n";
# }
#
# # Update the file if needed.
# if ($rewrite)
# {
# # Back it up first.
# my $backup_file = $anvil->Storage->backup({
# file => $anvil->data->{path}{configs}{'httpd.conf'},
# debug => $debug,
# target => $target,
# port => $port,
# remote_user => $remote_user,
# password => $password,
# });
# $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { backup_file => $backup_file }});
#
# if ($backup_file)
# {
# # Proceed.
# $anvil->Storage->write_file({
# body => $new_file,
# debug => $debug,
# file => $anvil->data->{path}{configs}{'httpd.conf'},
# overwrite => 1,
# secure => 0,
# target => $target,
# port => $port,
# remote_user => $remote_user,
# password => $password,
# });
# }
# }
#
# # Generate the htpasswd md5 (special flavour) hash string.
# # See: https://httpd.apache.org/docs/2.4/misc/password_encryptions.html
#
# # <user>:$apr1$<salt>$<hash>
# #admin:$apr1$Upbg/ujf$SJNCufHfq76uo2t0rZTZI/
#
# # my $salt = $anvil->System->call({debug => $debug, shell_call => $anvil->data->{path}{exe}{openssl}." rand 1000 | ".$anvil->data->{path}{exe}{strings}." | ".$anvil->data->{path}{exe}{'grep'}." -io [0-9A-Za-z\.\/] | ".$anvil->data->{path}{exe}{head}." -n 16 | ".$anvil->data->{path}{exe}{'tr'}." -d '\n'" });
# # my $new_hash = crypt($new_password,"\$6\$".$salt."\$");
# # $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, secure => 0, list => {
# # salt => $salt,
# # new_hash => $new_hash,
# # }});
#
# # htpasswd -cb /etc/httpd/.htpasswd admin Initial2
# # chown apache:apache /etc/httpd/.htpasswd
# # chmod 0660 /etc/httpd/.htpasswd
#
#
# # (re)write the htpasswd file.
# ### TODO: Temporary!
# $new_password =~ s/"/\"/g;
# my $shell_call = $anvil->data->{path}{exe}{htpasswd}." -nb ".$user." \"".$new_password."\"";
# my $output = $anvil->System->call({debug => $debug, secure => 1, shell_call => $shell_call});
# my $hash_string = "";
# $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, secure => 0, list => { output => $output }});
# foreach my $line (split/\n/, $output)
# {
# $hash_string = $line;
# $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, secure => 0, list => { hash_string => $hash_string }});
# last;
# }
#
# # Write out the file.
# $return_code = $anvil->Storage->write_file({
# body => $hash_string."\n\n",
# debug => $debug,
# file => $anvil->data->{path}{data}{'.htpasswd'},
# group => "apache",
# mode => "0660",
# overwrite => 1,
# secure => 0,
# target => $target,
# user => "apache",
# port => $port,
# remote_user => $remote_user,
# password => $password,
# });
# $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, secure => 0, list => { return_code => $return_code }});
#
# if (not $return_code)
# {
# # Restart apache
# $anvil->System->reload_daemon({debug => $debug, daemon => "httpd"});
# }
#
# return($return_code);
# }
=head2 change_shell_user_password
@ -1686,13 +1792,13 @@ sub stty_echo
if ($set eq "off")
{
$anvil->data->{sys}{stty} = $anvil->System->call({shell_call => $anvil->data->{path}{exe}{stty}." --save"});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, secure => 0, list => { 'sys::stty' => $anvil->data->{sys}{stty} }});
$anvil->data->{sys}{terminal}{stty} = $anvil->System->call({shell_call => $anvil->data->{path}{exe}{stty}." --save"});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, secure => 0, list => { 'sys::terminal::stty' => $anvil->data->{sys}{terminal}{stty} }});
$anvil->System->call({shell_call => $anvil->data->{path}{exe}{stty}." -echo"});
}
elsif (($set eq "on") && ($anvil->data->{sys}{stty}))
elsif (($set eq "on") && ($anvil->data->{sys}{terminal}{stty}))
{
$anvil->System->call({shell_call => $anvil->data->{path}{exe}{stty}." ".$anvil->data->{sys}{stty}});
$anvil->System->call({shell_call => $anvil->data->{path}{exe}{stty}." ".$anvil->data->{sys}{terminal}{stty}});
}
return(0);

@ -22,6 +22,7 @@ my $THIS_FILE = "Words.pm";
# language
# read
# string
# _wrap_string
=pod
@ -339,7 +340,7 @@ This time, requesting 'C<< t_0002 >>' and passing in two variables. Note that 'C
So to request this string in Canadian English is the two variables inserted, we would call:
my $string = $anvil->Words->string({
my $string = $anvil->Words->string({
language => 'en_CA',
key => 't_0002',
variables => {
@ -605,4 +606,151 @@ sub string
# Private functions #
#############################################################################################################
=head2 _wrap_string
When printing strings to the console, this will wrap the string based on the current output of C<< $anvil->Get->_wrap_to >> (which itself updates C<< sys::terminal::columns >>).
This method looks for a string that starts with spaces or C<< [ foo ] - >> type leader and preserves the spacing when wrapping lines.
This returns the wrapped string as a simple string variable.
Parameters;
=head3 string
This is the string to wrap. If no string is passed in, a blank string will be returned.
=cut
sub _wrap_string
{
my $self = shift;
my $parameter = shift;
my $anvil = $self->parent;
my $debug = defined $parameter->{debug} ? $parameter->{debug} : 3;
# Get the string to wrap.
my $string = defined $parameter->{string} ? $parameter->{string} : "";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { string => $string }});
# Update the wrap length
$anvil->Get->_wrap_to;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { 'sys::terminal::columns' => $anvil->data->{sys}{terminal}{columns} }});
# If the given line starts with tabs, convert them to 8 spaces.
my $start_spaces = "";
if ($string =~ /^(\s+)/)
{
$start_spaces = $1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { start_spaces => $start_spaces }});
# Now strip the leading space, convert any tabs to spaces and then bolt the new spacing back
# on.
$string =~ s/^\s+//;
$start_spaces =~ s/\t/ /g;
$string = $start_spaces.$string;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
start_spaces => $start_spaces,
string => $string,
}});
}
# This will contain the wrapped string
my $wrapped_string = "";
if ($string)
{
# Create the space prefix for wrapped lines.
my $prefix_spaces = "";
if ($string =~ /^\[ (.*?) \] - /)
{
my $prefix = "[ $1 ] - ";
my $wrap_spaces = length($prefix);
for (1..$wrap_spaces)
{
$prefix_spaces .= " ";
}
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
prefix => $prefix,
wrap_spaces => $wrap_spaces,
prefix_spaces => $prefix_spaces,
}});
}
elsif ($string =~/^(\s+)/)
{
# We have some number of white spaces.
my $prefix = $1;
my $say_prefix = $prefix;
my $wrap_spaces = length($say_prefix);
for (1..$wrap_spaces)
{
$prefix_spaces .= " ";
}
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
prefix => $prefix,
wrap_spaces => $wrap_spaces,
say_prefix => $say_prefix,
prefix_spaces => $prefix_spaces,
}});
}
my $this_line = $prefix_spaces;
$string =~ s/^\s+//;
foreach my $word (split/ /, $string)
{
# Store the line as it was before in case the next word pushes line line past the
# 'wrap_to' value. Then append this word and see if we're over the width of the
# terminal. If we are, we'll use 'last_line' to append to 'wrapped_string' and use
# this word to start the next line.
my $last_line = $this_line;
$this_line .= $word;
my $line_length = length($this_line);
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
's1:last_line' => $last_line,
's2:word' => $word,
}});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
's1:line_length' => $line_length,
's2:this_line' => $this_line,
}});
if ((not $last_line) && ($line_length >= $anvil->data->{sys}{terminal}{columns}))
{
# This one word goes over the length of the column, so we have to store it as
# it's own line.
$wrapped_string .= $word."\n";
$this_line = $prefix_spaces;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
this_line => $this_line,
wrapped_string => $wrapped_string,
}});
}
elsif ($line_length > $anvil->data->{sys}{terminal}{columns})
{
# This word appended to the line pushes over the terminal width, so store the
# 'last_line' and use this word to start the next line.
$last_line =~ s/\s+$//;
$wrapped_string .= $last_line."\n";
$this_line = $prefix_spaces.$word." ";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
this_line => $this_line,
wrapped_string => $wrapped_string,
}});
}
else
{
# Just add a space after this word, we're not at the edge yet.
$this_line .= " ";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { this_line => $this_line }});
}
}
# We're out of the loop, so store the 'last_line' and remove the last space.
$this_line =~ s/\s+$//;
$wrapped_string .= $this_line;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { wrapped_string => $wrapped_string }});
}
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { wrapped_string => $wrapped_string }});
return($wrapped_string);
}
1;

@ -171,14 +171,16 @@ sub update_local_passwords
foreach my $user ("admin", "root")
{
print "Updating: [$user] with password: [".$anvil->data->{switches}{'new-password'}."]\n";
$anvil->System->change_shell_user_password({debug => 2, user => $user, new_password => $anvil->data->{switches}{'new-password'}});
# $anvil->System->change_shell_user_password({debug => 2, user => $user, new_password => $anvil->data->{switches}{'new-password'}});
}
### TODO: Put the database into maintenance mode, then check for any known nodes and update their
### password for us.
# Update the database password.
my $apache_user = $anvil->data->{sys}{apache}{user} ? $anvil->data->{sys}{apache}{user} : "admin";
$anvil->System->change_apache_password({debug => 2, new_password => $anvil->data->{switches}{'new-password'}});
#my $apache_user = $anvil->data->{sys}{apache}{user} ? $anvil->data->{sys}{apache}{user} : "admin";
#$anvil->System->change_apache_password({debug => 2, new_password => $anvil->data->{switches}{'new-password'}});
return(0);

@ -38,6 +38,73 @@ BEGIN
END
$$;
-- This stores information about users.
-- Note that is all permissions are left false, the user can still interact with the Anvil! doing safe things, like changing optical media, perform migrations, start servers (but not stop them), etc.
CREATE TABLE users (
user_uuid uuid not null primary key, -- This is the single most important record in Anvil!. Everything links back to here.
user_name text not null,
user_password text, -- A user without a password is disabled.
user_salt text, -- This is used to enhance the security of the user's password.
user_language text, -- If set, this will choose a different language over the default.
user_is_admin boolean not null default false, -- If true, all aspects of the program are available to the user.
user_is_experienced boolean not null default false, -- If true, user is allowed to delete a server, alter disk size, alter hardware and do other potentially risky things. They will also get fewer confirmation dialogues.
user_is_trusted boolean not null default false, -- If true, user is allowed to do things that would cause interruptions, like force-reset and gracefully stop servers, withdraw nodes, and stop the Anvil! entirely.
modified_date timestamp with time zone not null
);
ALTER TABLE users OWNER TO #!variable!user!#;
CREATE TABLE history.users (
history_id bigserial,
user_uuid uuid,
user_name text,
user_password text,
user_salt text,
user_language text,
user_is_admin boolean,
user_is_experienced boolean,
user_is_trusted boolean,
modified_date timestamp with time zone not null
);
ALTER TABLE history.users OWNER TO #!variable!user!#;
CREATE FUNCTION history_users() RETURNS trigger
AS $$
DECLARE
history_users RECORD;
BEGIN
SELECT INTO history_users * FROM users WHERE user_uuid = new.user_uuid;
INSERT INTO history.users
(user_uuid,
user_name,
user_password,
user_salt,
user_language,
user_is_admin,
user_is_experienced,
user_is_trusted,
modified_date)
VALUES
(history_users.user_uuid,
history_users.user_name,
history_users.user_password,
history_users.user_salt,
history_users.user_language,
history_users.user_is_admin,
history_users.user_is_experienced,
history_users.user_is_trusted,
history_users.modified_date);
RETURN NULL;
END;
$$
LANGUAGE plpgsql;
ALTER FUNCTION history_users() OWNER TO #!variable!user!#;
CREATE TRIGGER trigger_users
AFTER INSERT OR UPDATE ON users
FOR EACH ROW EXECUTE PROCEDURE history_users();
-- This stores information about the host machine. This is the master table that everything will be linked
-- to.
CREATE TABLE hosts (

Loading…
Cancel
Save