diff --git a/Anvil/Tools.pm b/Anvil/Tools.pm index e0eb6bc3..e9980019 100755 --- a/Anvil/Tools.pm +++ b/Anvil/Tools.pm @@ -215,7 +215,7 @@ sub new ### TODO: Calls to allow the user to override defaults... # Local parameters... } - elsif($parameter) + elsif ($parameter) { # Um... print $THIS_FILE." ".__LINE__."; Anvil::Tools->new() invoked with an invalid parameter. Expected a hash reference, but got: [$parameter]\n"; @@ -733,7 +733,12 @@ sub _set_defaults stty => "", }, use_base2 => 1, - user => "admin", + user => { + name => "admin", + cookie_valid => 0, + language => "en_CA", + skin => "alteeve", + }, }; $anvil->data->{defaults} = { ## Network stuff... The second octet auto-increments to handle N-number of netowrks. As such, diff --git a/Anvil/Tools/Account.pm b/Anvil/Tools/Account.pm index 567b698c..64c9269e 100755 --- a/Anvil/Tools/Account.pm +++ b/Anvil/Tools/Account.pm @@ -13,8 +13,13 @@ my $THIS_FILE = "Account.pm"; ### Methods; # encrypt_password +# login +# logout +# read_cookies # read_details # validate_password +# _build_cookie_hash +# _write_cookies =pod @@ -90,10 +95,24 @@ If anything goes wrong, all four keys will have empty strings. Parameters +=head3 algorithm (optional) + +If set, the password will be encrypted using the given algoritm. Otherwise, c<< sys::password::algorithm >> is used. If that is not set, C<< sha256 >> is used. + +=head3 hash_count (Optional, default 500000) + +This controls how many times we re-encrypt the password hash. This is designed to slow down how quickly a brute-force attacker can test hashes. This should be a high enough number to take some time (~0.5 seconds) on a modern machine, but not so high that it noticeably slows down user login attempts. + +If set to C<< 0 >>, no re-hashing will occur, but the initial hash still will. + =head3 password (required) This is the password (string) to encrypt. +=head3 salt (optional) + +This is the salt to use when hashing the password. If this is not passed, a new salt will be generated. + =cut sub encrypt_password { @@ -102,11 +121,15 @@ sub encrypt_password my $anvil = $self->parent; my $debug = defined $parameter->{debug} ? $parameter->{debug} : 3; - my $user_password_hash = defined $parameter->{password} ? $parameter->{password} : ""; - my $user_hash_count = $anvil->data->{sys}{password}{hash_count} =~ /^\d+$/ ? $anvil->data->{sys}{password}{hash_count} : 500000; - my $user_algorithm = $anvil->data->{sys}{password}{algorithm} ? $anvil->data->{sys}{password}{algorithm} : "sha512"; + my $algorithm = defined $parameter->{algorithm} ? $parameter->{algorithm} : ""; + my $hash_count = defined $parameter->{hash_count} ? $parameter->{hash_count} : ""; + my $password = defined $parameter->{password} ? $parameter->{password} : ""; + my $salt = defined $parameter->{salt} ? $parameter->{salt} : ""; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { - password => $anvil->Log->secure ? $user_password_hash : "--", + algorithm => $algorithm, + hash_count => $hash_count, + password => $anvil->Log->secure ? $password : "#!string!log_0186!#", + salt => $salt, }}); # We'll fill these out below if we succeed. @@ -118,18 +141,37 @@ sub encrypt_password }; # Make sure we got a string - if (not $user_password_hash) + if (not $password) { $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0020", variables => { method => "Account->encrypt_password()", parameter => "password" }}); return($answer); } + my $user_password_hash = $password; + + # Set the re-hash count, if not already set. + my $user_hash_count = $hash_count; + if ($user_hash_count eq "") + { + $user_hash_count = $anvil->data->{sys}{password}{hash_count} =~ /^\d+$/ ? $anvil->data->{sys}{password}{hash_count} : 500000; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { user_hash_count => $user_hash_count }}); + } # Generate a salt. - my $user_salt = $anvil->Get->_salt; - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { user_salt => $user_salt }}); + my $user_salt = $salt; + if (not $user_salt) + { + $user_salt = $anvil->Get->_salt; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { user_salt => $user_salt }}); + } ### TODO: Look at using/support bcrypt as the default algorithm. Needed RPMs are already in the el7 AN!Repo. # We support sha256, sha384 and sha512, possible new ones later. + my $user_algorithm = $algorithm; + if (not $algorithm) + { + $user_algorithm = $anvil->data->{sys}{password}{algorithm} ? $anvil->data->{sys}{password}{algorithm} : "sha512"; + } + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { user_algorithm => $user_algorithm }}); if ($user_algorithm eq "sha256" ) { $user_password_hash = sha256_base64($user_password_hash.$user_salt); @@ -193,21 +235,307 @@ sub encrypt_password 'answer->user_salt' => $answer->{user_salt}, 'answer->user_hash_count' => $answer->{user_hash_count}, 'answer->user_algorithm' => $answer->{user_algorithm}, - }}); return($answer); } +=head2 login + +This checks to see if the CGI C<< username >> and C<< password >> passed in are for a valid user or not. If so, their details are loaded and C<< 0 >> is returned. If not, C<< 1 >> is returned. + +This method takes no parameters. + +=cut +sub login +{ + my $self = shift; + my $parameter = shift; + my $anvil = $self->parent; + my $debug = defined $parameter->{debug} ? $parameter->{debug} : 3; + + if ((not $anvil->data->{cgi}{username}{value}) or (not $anvil->data->{cgi}{password}{value})) + { + # The user forgot something... + $anvil->data->{form}{error_massage} = $anvil->Template->get({file => "main.html", name => "error_message", variables => { error_message => $anvil->Words->string({key => "error_0027"}) }}); + return(1); + } + + my $query = " +SELECT + user_uuid, + user_password_hash, + user_salt, + user_algorithm, + user_hash_count +FROM + users +WHERE + user_algorithm != 'DELETED' +AND + user_name = ".$anvil->data->{sys}{use_db_fh}->quote($anvil->data->{cgi}{username}{value})." +;"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { query => $query }}); + + my $results = $anvil->Database->query({query => $query, source => $THIS_FILE, line => __LINE__}); + my $count = @{$results}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + results => $results, + count => $count, + }}); + + if (not $count) + { + # User not found. + $anvil->data->{form}{error_massage} = $anvil->Template->get({file => "main.html", name => "error_message", variables => { error_message => $anvil->Words->string({key => "error_0027"}) }}); + return(1); + } + + my $user_uuid = $results->[0]->[0]; + my $user_password_hash = $results->[0]->[1]; + my $user_salt = $results->[0]->[2]; + my $user_algorithm = $results->[0]->[3]; + my $user_hash_count = $results->[0]->[4]; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + user_uuid => $user_uuid, + user_password_hash => $user_password_hash, + user_salt => $user_salt, + user_algorithm => $user_algorithm, + user_hash_count => $user_hash_count, + }}); + + # Test the passed-in password. + my $test_password_answer = $anvil->Account->encrypt_password({ + password => $anvil->data->{cgi}{password}{value}, + salt => $user_salt, + algorithm => $user_algorithm, + hash_count => $user_hash_count, + }); + my $test_password_hash = $test_password_answer->{user_password_hash}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { test_password_hash => $test_password_hash }}); + + if ($test_password_hash eq $user_password_hash) + { + # User passed a valid username/password. Create a session hash. + my ($session_hash, $session_salt) = $anvil->Account->_build_cookie_hash({uuid => $anvil->data->{cookie}{anvil_user_uuid}, offset => 0}); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + session_hash => $session_hash, + session_salt => $session_salt, + }}); + + my $query = " +UPDATE + users +SET + user_session_salt = ".$anvil->data->{sys}{use_db_fh}->quote($session_salt).", + modified_date = ".$anvil->data->{sys}{use_db_fh}->quote($anvil->data->{sys}{db_timestamp})." +WHERE + user_uuid = ".$anvil->data->{sys}{use_db_fh}->quote($user_uuid)." +;"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { query => $query }}); + $anvil->Database->write({query => $query, source => $THIS_FILE, line => __LINE__}); + + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "log_0183", variables => { user => $anvil->data->{cgi}{username}{value} }}); + $anvil->Account->_write_cookies({ + debug => $debug, + hash => $session_hash, + uuid => $user_uuid, + }); + } + else + { + # User DID NOT passed a valid username/password. + $anvil->data->{form}{error_massage} = $anvil->Template->get({file => "main.html", name => "error_message", variables => { error_message => $anvil->Words->string({key => "error_0027"}) }}); + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "log_0184", variables => { + user_agent => $ENV{HTTP_USER_AGENT} ? $ENV{HTTP_USER_AGENT} : "#!string!log_0185!#", + source_ip => $ENV{REMOTE_ADDR} ? $ENV{REMOTE_ADDR} : "#!string!log_0185!#", + user => $anvil->data->{cgi}{username}{value}, + }}); + + # Slow them down a bit... + sleep 5; + + return(1); + } + + return(0); +} + +=head2 logout + +This deletes the user's UUID and hash cookies, which effectively logs them out. + +This methods takes no parameters and always returns C<< 0 >>. + +=cut +sub logout +{ + my $self = shift; + my $parameter = shift; + my $anvil = $self->parent; + my $debug = defined $parameter->{debug} ? $parameter->{debug} : 3; + + # Delete the user's cookie data. Sending nothing to '_write_cookies' does this. + $anvil->Account->_write_cookies({debug => $debug}); + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "log_0179"}); + + return(0); +} + +=head2 read_cookies + +This method (tries to) read the user's cookies to see if their session is valid. If so, it will read in their account details. + +This method takes no parameters. + +Return codes; + +=head3 0 + +The cookies were read, the account was validated and the user's details were loaded. + +=head3 1 + +No cookie was found or read. The user needs to log in + +=head3 2 + +There was a problem reading the user's UUID (it wasn't found in the database), so the cookies were deleted (via C<< Account->logout() >>. The user needs to log back in. + +=head3 3 + +There user's hash is invalid, it is probably expired. The user has been logged out and needs to log back in. + +=cut +sub read_cookies +{ + my $self = shift; + my $parameter = shift; + my $anvil = $self->parent; + my $debug = defined $parameter->{debug} ? $parameter->{debug} : 3; + + # Read in any cookies + if (defined $ENV{HTTP_COOKIE}) + { + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { "ENV{HTTP_COOKIE}" => $ENV{HTTP_COOKIE} }}); + my @data = (split /; /, $ENV{HTTP_COOKIE}); + foreach my $pair (@data) + { + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { pair => $pair }}); + + my ($key, $value) = split/=/, $pair; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + key => $key, + value => $value, + }}); + + next if ((not defined $value) or ($value eq "")); + if ($key =~ /^anvil_/) + { + $anvil->data->{cookie}{$key} = $value; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { "cookie::${key}" => $anvil->data->{cookie}{$key} }}); + } + } + } + + # Did we read a cookie? + if ((not defined $anvil->data->{cookie}{anvil_user_uuid}) or (not $anvil->data->{cookie}{anvil_user_uuid})) + { + # No cookie read. + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "log_0177"}); + return(1); + } + elsif (not defined $anvil->data->{cookie}{anvil_user_hash}) + { + $anvil->data->{cookie}{anvil_user_hash} = ""; + } + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + "cookie::anvil_user_uuid" => $anvil->data->{cookie}{anvil_user_uuid}, + "cookie::anvil_user_hash" => $anvil->data->{cookie}{anvil_user_hash}, + }}); + + # Validate the cookie if there is a User UUID. Pick the random number up from the database. + my $query = "SELECT user_session_salt FROM users WHERE user_uuid = ".$anvil->data->{sys}{use_db_fh}->quote($anvil->data->{cookie}{anvil_user_uuid}).";"; + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, key => "log_0124", variables => { query => $query }}); + my $results = $anvil->Database->query({query => $query, source => $THIS_FILE, line => __LINE__}); + my $count = @{$results}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + results => $results, + count => $count, + }}); + + if ($count < 1) + { + + die; + # The user in the cookie isn't in the database. The user was deleted? + $anvil->Account->logout(); + + # Record the error message for the user. + $anvil->data->{form}{error_massage} = $anvil->Template->get({file => "main.html", name => "error_message", variables => { error_message => $anvil->Words->string({key => "error_0023"}) }}); + + # We're done. + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "log_0178", variables => { uuid => $anvil->data->{cookie}{anvil_user_uuid} }}); + return(2); + } + + # Read in their "rand" value + $anvil->data->{users}{user_session_salt} = $anvil->Database->query({query => $query, source => $THIS_FILE, line => __LINE__})->[0]->[0]; + $anvil->data->{users}{user_session_salt} = "" if not defined $anvil->data->{users}{user_session_salt}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { "users::user_session_salt" => $anvil->data->{users}{user_session_salt} }}); + + # Generate a hash using today and yesterday's date. + my ($today_hash) = $anvil->Account->_build_cookie_hash({uuid => $anvil->data->{cookie}{anvil_user_uuid}, offset => 0}); + my ($yesterday_hash) = $anvil->Account->_build_cookie_hash({uuid => $anvil->data->{cookie}{anvil_user_uuid}, offset => -86400}); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + today_hash => $today_hash, + yesterday_hash => $yesterday_hash, + }}); + + # See if either hash matches what the user has stored. + if ($anvil->data->{cookie}{anvil_user_hash} eq $today_hash) + { + # Valid hash, user can proceed. + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, key => "log_0180"}); + + # Load the account details + $anvil->Account->read_details({debug => $debug}); + } + elsif ($anvil->data->{cookie}{anvil_user_hash} eq $yesterday_hash) + { + # The hash was valid yesterday, so we'll update the cookie with today's hash and proceed + # (which also loads the user's details). + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "log_0181"}); + $anvil->Account->_write_cookies({ + debug => $debug, + hash => $today_hash, + uuid => $anvil->data->{cookie}{anvil_user_uuid}, + }); + } + else + { + # The user's cookie is invalid, log the user out. + $anvil->Account->logout(); + + # Record the error message for the user. + $anvil->data->{form}{error_massage} = $anvil->Template->get({file => "main.html", name => "error_message", variables => { error_message => $anvil->Words->string({key => "error_0024"}) }}); + + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "log_0182"}); + return(3); + } + + return(0); +} + =head2 read_details -This method takes a user name and, if the user is found, reads in the details and sets C<< sys::user:: >>. If the user is found, C<< 1 >> is returned. If not, C<< 0 >> is returned. +This method takes a user uuid and, if the user is found, reads in the details and sets C<< sys::users:: >>. If the user is found, C<< 1 >> is returned. If not, C<< 0 >> is returned. Parameters; -=head3 user_name (required) +=head3 user_uuid (optional) -This is the user name being searched for. It is case sensitive. +This is the user UUID being searched for. If it is not set, C<< cookie::anvil_user_uuid >> =cut sub read_details @@ -217,12 +545,18 @@ sub read_details my $anvil = $self->parent; my $debug = defined $parameter->{debug} ? $parameter->{debug} : 3; - my $user_name = defined $parameter->{user_name} ? $parameter->{user_name} : ""; - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { user_name => $user_name }}); + my $user_uuid = defined $parameter->{user_uuid} ? $parameter->{user_uuid} : $anvil->data->{cookie}{anvil_user_uuid}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { user_uuid => $user_uuid }}); + + if (not $anvil->Validate->is_uuid({uuid => $user_uuid})) + { + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "error_0025", variables => { uuid => $user_uuid }}); + return(0); + } my $query = " SELECT - user_uuid, + user_name, user_password_hash, user_salt, user_algorithm, @@ -234,7 +568,7 @@ SELECT FROM users WHERE - user_name = ".$anvil->data->{sys}{use_db_fh}->quote($user_name)." + user_uuid = ".$anvil->data->{sys}{use_db_fh}->quote($user_uuid)." ;"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { query => $query }}); @@ -247,9 +581,10 @@ WHERE if (not $count) { # User doesn't exist. + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "error_0026", variables => { uuid => $user_uuid }}); return(0); } - my $user_uuid = $results->[0]->[0]; + my $user_name = $results->[0]->[0]; my $user_password_hash = $results->[0]->[1]; my $user_salt = $results->[0]->[2]; my $user_algorithm = $results->[0]->[3]; @@ -259,7 +594,7 @@ WHERE my $user_is_experienced = $results->[0]->[7]; my $user_is_trusted = $results->[0]->[8]; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { - user_uuid => $user_uuid, + user_name => $user_name, user_password_hash => $user_password_hash, user_salt => $user_salt, user_algorithm => $user_algorithm, @@ -269,30 +604,37 @@ WHERE user_is_experienced => $user_is_experienced, user_is_trusted => $user_is_trusted, }}); - - $anvil->data->{sys}{user}{user_name} = $user_name; - $anvil->data->{sys}{user}{user_uuid} = $user_uuid; - $anvil->data->{sys}{user}{user_password_hash} = $user_password_hash, - $anvil->data->{sys}{user}{user_salt} = $user_salt, - $anvil->data->{sys}{user}{user_algorithm} = $user_algorithm, - $anvil->data->{sys}{user}{user_hash_count} = $user_hash_count, - $anvil->data->{sys}{user}{user_language} = $user_language, - $anvil->data->{sys}{user}{user_is_admin} = $user_is_admin, - $anvil->data->{sys}{user}{user_is_experienced} = $user_is_experienced, - $anvil->data->{sys}{user}{user_is_trusted} = $user_is_trusted, + + $anvil->data->{sys}{users}{user_name} = $user_name; + $anvil->data->{sys}{users}{user_uuid} = $user_uuid; + $anvil->data->{sys}{users}{user_password_hash} = $user_password_hash, + $anvil->data->{sys}{users}{user_salt} = $user_salt, + $anvil->data->{sys}{users}{user_algorithm} = $user_algorithm, + $anvil->data->{sys}{users}{user_hash_count} = $user_hash_count, + $anvil->data->{sys}{users}{user_language} = $user_language, + $anvil->data->{sys}{users}{user_is_admin} = $user_is_admin, + $anvil->data->{sys}{users}{user_is_experienced} = $user_is_experienced, + $anvil->data->{sys}{users}{user_is_trusted} = $user_is_trusted, $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { - 'sys::user::user_name' => $anvil->data->{sys}{user}{user_name}, - 'sys::user::user_uuid' => $anvil->data->{sys}{user}{user_uuid}, - 'sys::user::user_password_hash' => $anvil->data->{sys}{user}{user_password_hash}, - 'sys::user::user_salt' => $anvil->data->{sys}{user}{user_salt}, - 'sys::user::user_algorithm' => $anvil->data->{sys}{user}{user_algorithm}, - 'sys::user::user_hash_count' => $anvil->data->{sys}{user}{user_hash_count}, - 'sys::user::user_language' => $anvil->data->{sys}{user}{user_language}, - 'sys::user::user_is_admin' => $anvil->data->{sys}{user}{user_is_admin}, - 'sys::user::user_is_experienced' => $anvil->data->{sys}{user}{user_is_experienced}, - 'sys::user::user_is_trusted' => $anvil->data->{sys}{user}{user_is_trusted}, + 'sys::users::user_name' => $anvil->data->{sys}{users}{user_name}, + 'sys::users::user_uuid' => $anvil->data->{sys}{users}{user_uuid}, + 'sys::users::user_password_hash' => $anvil->data->{sys}{users}{user_password_hash}, + 'sys::users::user_salt' => $anvil->data->{sys}{users}{user_salt}, + 'sys::users::user_algorithm' => $anvil->data->{sys}{users}{user_algorithm}, + 'sys::users::user_hash_count' => $anvil->data->{sys}{users}{user_hash_count}, + 'sys::users::user_language' => $anvil->data->{sys}{users}{user_language}, + 'sys::users::user_is_admin' => $anvil->data->{sys}{users}{user_is_admin}, + 'sys::users::user_is_experienced' => $anvil->data->{sys}{users}{user_is_experienced}, + 'sys::users::user_is_trusted' => $anvil->data->{sys}{users}{user_is_trusted}, }}); + # Change the active language, if needed + if ($anvil->data->{sys}{users}{user_language}) + { + # Switch to the user's language + $anvil->Words->language({set => $anvil->data->{sys}{users}{user_language}}); + } + return(1); } @@ -325,7 +667,7 @@ sub validate_password my $valid = 0; my $hash = ""; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { - password => $anvil->Log->secure ? $password : "--", + password => $anvil->Log->secure ? $password : "#!string!log_0186!#", user => $user, }}); @@ -441,4 +783,128 @@ WHERE return($valid); } + +# =head3 +# +# Private Functions; +# +# =cut + +############################################################################################################# +# Private functions # +############################################################################################################# + +=head2 _build_cookie_hash + +This takes a (user) UUID and offset (stated as seconds) and builds a hash approporiate for use in cookies (or a test hash to validate a read cookie hash). + +Parameters; + +=head3 offset (optional, default '0') + +This is used to offset the date when generating the date part of the string to hash. It is passed as-is directly to C<< Get->date_and_time >>. + +=head3 user_agent (optional, default 'HTTP_USER_AGENT' environment variable) + +This is the user agent to use when generating the string to hash. + +=head3 uuid (optional, default 'cookie::anvil_user_uuid') + +This is the UUID to use when generating the string to hash. Generally it is the user's UUID. + +=cut +sub _build_cookie_hash +{ + my $self = shift; + my $parameter = shift; + my $anvil = $self->parent; + my $debug = defined $parameter->{debug} ? $parameter->{debug} : 3; + + my $offset = defined $parameter->{offset} ? $parameter->{offset} : 0; + my $user_agent = defined $parameter->{user_agent} ? $parameter->{user_agent} : $ENV{HTTP_USER_AGENT}; + my $uuid = defined $parameter->{uuid} ? $parameter->{uuid} : $anvil->data->{cookie}{anvil_user_uuid}; + # I know I could do chained conditionals, but it gets hard to read. + $user_agent = "" if not defined $user_agent; + $uuid = "" if not defined $uuid; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + offset => $offset, + user_agent => $user_agent, + uuid => $uuid, + }}); + + my $date = $anvil->Get->date_and_time({date_only => 1, offset => $offset}); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { date => $date }}); + + my $session_string = $uuid.$date.$user_agent; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { session_string => $session_string }}); + + # Generate a hash, but unike normal passwords, we won't re-encrypt it. + my $answer = $anvil->Account->encrypt_password({password => $session_string, hash_count => 0}); + my $hash = $answer->{user_password_hash}; + my $salt = $answer->{user_salt}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + hash => $hash, + salt => $salt, + }}); + + return($hash, $salt); +} + +=head2 _write_cookies + +This sets (or clears) the user's cookies. + +Parameters; + +=head3 hash (optional) + +This is the hash to use for the session. If it is blank, it will log the user out. + +=head3 uuid (optional) + +This is the UUID of the user. If it is blank, it will log the user out. + +=cut +sub _write_cookies +{ + my $self = shift; + my $parameter = shift; + my $anvil = $self->parent; + my $debug = defined $parameter->{debug} ? $parameter->{debug} : 3; + + my $hash = defined $parameter->{hash} ? $parameter->{hash} : ""; + my $uuid = defined $parameter->{uuid} ? $parameter->{uuid} : ""; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + hash => $hash, + uuid => $uuid, + }}); + + # If we have a users ID, load the user details + if (($hash) && ($uuid)) + { + # Write the cookies + print "Set-Cookie:anvil_user_uuid=".$uuid.";\n"; + print "Set-Cookie:anvil_user_hash=".$hash.";\n"; + + # Load the user's details + $anvil->Account->read_details({ + debug => $debug, + user_uuid => $uuid}); + + # Update the active language, if needed. + if ($anvil->data->{sys}{users}{user_language}) + { + # Switch to the user's language + $anvil->Words->language({set => $anvil->data->{sys}{users}{user_language}}); + } + } + else + { + print "Set-Cookie:anvil_user_uuid=; expires=-1d;\n"; + print "Set-Cookie:anvil_user_hash=; expires=-1d;\n"; + } + + return(0); +} + 1; diff --git a/Anvil/Tools/Database.pm b/Anvil/Tools/Database.pm index be2c311a..9d724330 100755 --- a/Anvil/Tools/Database.pm +++ b/Anvil/Tools/Database.pm @@ -676,7 +676,7 @@ sub connect port => $port, name => $name, user => $user, - password => $anvil->Log->secure ? $password : "--", + password => $anvil->Log->secure ? $password : "#!string!log_0186!#", }}); # If not set, we will always ping before connecting. @@ -711,7 +711,7 @@ sub connect port => $port, name => $name, user => $user, - password => $anvil->Log->secure ? $password : "--", + password => $anvil->Log->secure ? $password : "#!string!log_0186!#", }}); ### TODO: Can we do a telnet port ping with a short timeout instead of a shell ping call? @@ -975,14 +975,14 @@ sub connect # Report any failed DB connections foreach my $uuid (@{$failed_connections}) { - my $database_name = defined $anvil->data->{database}{$uuid}{name} ? $anvil->data->{database}{$uuid}{name} : "--"; - my $database_user = defined $anvil->data->{database}{$uuid}{user} ? $anvil->data->{database}{$uuid}{user} : "--"; + my $database_name = defined $anvil->data->{database}{$uuid}{name} ? $anvil->data->{database}{$uuid}{name} : "#!string!log_0185!#"; + my $database_user = defined $anvil->data->{database}{$uuid}{user} ? $anvil->data->{database}{$uuid}{user} : "#!string!log_0185!#"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { "database::${uuid}::host" => $anvil->data->{database}{$uuid}{host}, "database::${uuid}::port" => $anvil->data->{database}{$uuid}{port}, "database::${uuid}::name" => $database_name, "database::${uuid}::user" => $database_user, - "database::${uuid}::password" => $anvil->Log->secure ? $anvil->data->{database}{$uuid}{password} : "--", + "database::${uuid}::password" => $anvil->Log->secure ? $anvil->data->{database}{$uuid}{password} : "#!string!log_0186!#", }}); # Copy my alert hash before I delete the uuid. @@ -1034,14 +1034,14 @@ sub connect # Send an 'all clear' message if a now-connected DB previously wasn't. foreach my $uuid (@{$successful_connections}) { - my $database_name = defined $anvil->data->{database}{$uuid}{name} ? $anvil->data->{database}{$uuid}{name} : "--"; - my $database_user = defined $anvil->data->{database}{$uuid}{user} ? $anvil->data->{database}{$uuid}{user} : "--"; + my $database_name = defined $anvil->data->{database}{$uuid}{name} ? $anvil->data->{database}{$uuid}{name} : "#!string!log_0185!#"; + my $database_user = defined $anvil->data->{database}{$uuid}{user} ? $anvil->data->{database}{$uuid}{user} : "#!string!log_0185!#"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { "database::${uuid}::host" => $anvil->data->{database}{$uuid}{host}, "database::${uuid}::port" => $anvil->data->{database}{$uuid}{port}, "database::${uuid}::name" => $database_name, "database::${uuid}::user" => $database_user, - "database::${uuid}::password" => $anvil->Log->secure ? $anvil->data->{database}{$uuid}{password} : "--", + "database::${uuid}::password" => $anvil->Log->secure ? $anvil->data->{database}{$uuid}{password} : "#!string!log_0186!#", }}); ### TODO: Is this still an issue? If so, then we either need to require that the DB host @@ -3373,7 +3373,7 @@ sub insert_or_update_users line => $line, user_uuid => $user_uuid, user_name => $user_name, - user_password_hash => (($anvil->Log->secure) or ($user_salt)) ? $user_password_hash : "--" , + user_password_hash => (($anvil->Log->secure) or ($user_salt)) ? $user_password_hash : "#!string!log_0186!#" , user_salt => $user_salt, user_algorithm => $user_algorithm, user_hash_count => $user_hash_count, @@ -3423,7 +3423,7 @@ sub insert_or_update_users $user_algorithm = $answer->{user_algorithm}; $user_hash_count = $answer->{user_hash_count}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { - user_password_hash => (($anvil->Log->secure) or ($user_salt)) ? $user_password_hash : "--" , + user_password_hash => (($anvil->Log->secure) or ($user_salt)) ? $user_password_hash : "#!string!log_0186!#" , user_salt => $user_salt, user_algorithm => $user_algorithm, user_hash_count => $user_hash_count, @@ -4343,7 +4343,7 @@ sub query uuid => $uuid, "cache::db_fh::${uuid}" => $anvil->data->{cache}{db_fh}{$uuid}, line => $line, - query => ((not $secure) or (($secure) && (not $anvil->Log->secure))) ? $query : "--", + query => ((not $secure) or (($secure) && (not $anvil->Log->secure))) ? $query : "#!string!log_0186!#", secure => $secure, source => $source, }}); @@ -4390,14 +4390,14 @@ sub query # Do the query. my $DBreq = $anvil->data->{cache}{db_fh}{$uuid}->prepare($query) or $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0075", variables => { - query => ((not $secure) or (($secure) && (not $anvil->Log->secure))) ? $query : "--", + query => ((not $secure) or (($secure) && (not $anvil->Log->secure))) ? $query : "#!string!log_0186!#", server => $say_server, db_error => $DBI::errstr, }}); # Execute on the query $DBreq->execute() or $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0076", variables => { - query => ((not $secure) or (($secure) && (not $anvil->Log->secure))) ? $query : "--", + query => ((not $secure) or (($secure) && (not $anvil->Log->secure))) ? $query : "#!string!log_0186!#", server => $say_server, db_error => $DBI::errstr, }}); @@ -4915,7 +4915,7 @@ sub write uuid => $uuid, "cache::db_fh::${uuid}" => $anvil->data->{cache}{db_fh}{$uuid}, line => $line, - query => ((not $secure) or (($secure) && (not $anvil->Log->secure))) ? $query : "--", + query => ((not $secure) or (($secure) && (not $anvil->Log->secure))) ? $query : "#!string!log_0186!#", secure => $secure, source => $source, reenter => $reenter, @@ -5064,7 +5064,7 @@ sub write # Do the do. $anvil->data->{cache}{db_fh}{$uuid}->do($query) or $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0090", variables => { - query => ((not $secure) or (($secure) && (not $anvil->Log->secure))) ? $query : "--", + query => ((not $secure) or (($secure) && (not $anvil->Log->secure))) ? $query : "#!string!log_0186!#", server => $say_server, db_error => $DBI::errstr, }}); @@ -5275,14 +5275,14 @@ sub _find_behind_databases my $source_updated_time = 0; foreach my $uuid (sort {$a cmp $b} keys %{$anvil->data->{database}}) { - my $database_name = defined $anvil->data->{database}{$uuid}{name} ? $anvil->data->{database}{$uuid}{name} : "--"; - my $database_user = defined $anvil->data->{database}{$uuid}{user} ? $anvil->data->{database}{$uuid}{user} : "--"; + my $database_name = defined $anvil->data->{database}{$uuid}{name} ? $anvil->data->{database}{$uuid}{name} : "#!string!log_0185!#"; + my $database_user = defined $anvil->data->{database}{$uuid}{user} ? $anvil->data->{database}{$uuid}{user} : "#!string!log_0185!#"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { "database::${uuid}::host" => $anvil->data->{database}{$uuid}{host}, "database::${uuid}::port" => $anvil->data->{database}{$uuid}{port}, "database::${uuid}::name" => $database_name, "database::${uuid}::user" => $database_user, - "database::${uuid}::password" => $anvil->Log->secure ? $anvil->data->{database}{$uuid}{password} : "--", + "database::${uuid}::password" => $anvil->Log->secure ? $anvil->data->{database}{$uuid}{password} : "#!string!log_0186!#", }}); # Loop through the tables in this DB. For each table, we'll record the most recent time diff --git a/Anvil/Tools/Get.pm b/Anvil/Tools/Get.pm index 6dde2ea5..eace8ce9 100755 --- a/Anvil/Tools/Get.pm +++ b/Anvil/Tools/Get.pm @@ -125,7 +125,7 @@ sub anvil_version my $target = defined $parameter->{target} ? $parameter->{target} : "local"; my $version = 0; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { - password => $anvil->Log->secure ? $password : "--", + password => $anvil->Log->secure ? $password : "#!string!log_0186!#", port => $port, remote_user => $remote_user, target => $target, @@ -318,7 +318,7 @@ sub cgi if ((($variable =~ /passwd/) or ($variable =~ /password/)) && (not $anvil->Log->secure)) { # This is a password and we're not logging sensitive data, obfuscate it. - $censored_value = "--"; + $censored_value = "#!string!log_0186!#"; } $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 1, list => { "cgi::${variable}::$say_value" => $censored_value, @@ -375,6 +375,7 @@ sub date_and_time my $time_only = defined $parameter->{time_only} ? $parameter->{time_only} : 0; my $date_only = defined $parameter->{date_only} ? $parameter->{date_only} : 0; + # NOTE: This is used too early for normal error handling. # Are things sane? if ($use_time =~ /D/) { diff --git a/Anvil/Tools/Remote.pm b/Anvil/Tools/Remote.pm index 9180fe20..77afaa0b 100755 --- a/Anvil/Tools/Remote.pm +++ b/Anvil/Tools/Remote.pm @@ -294,9 +294,9 @@ sub call # NOTE: The shell call might contain sensitive data, so we show '--' if 'secure' is set and $anvil->Log->secure is not. $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $log_level, list => { 'close' => $close, - password => $anvil->Log->secure ? $password : "--", + password => $anvil->Log->secure ? $password : "#!string!log_0186!#", secure => $secure, - shell_call => ((not $anvil->Log->secure) && ($secure)) ? "--" : $shell_call, + shell_call => ((not $anvil->Log->secure) && ($secure)) ? "#!string!log_0186!#" : $shell_call, ssh_fh => $ssh_fh, start_time => $start_time, remote_user => $remote_user, @@ -467,7 +467,7 @@ sub call { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $log_level, list => { remote_user => $remote_user, - password => $anvil->Log->secure ? $password : "--", + password => $anvil->Log->secure ? $password : "#!string!log_0186!#", }}); if (not $ssh_fh->auth_password($remote_user, $password)) { diff --git a/Anvil/Tools/Storage.pm b/Anvil/Tools/Storage.pm index 1c28cb9a..663e347d 100755 --- a/Anvil/Tools/Storage.pm +++ b/Anvil/Tools/Storage.pm @@ -144,7 +144,7 @@ sub backup $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { fatal => $fatal, port => $port, - password => $anvil->Log->secure ? $password : "--", + password => $anvil->Log->secure ? $password : "#!string!log_0186!#", target => $target, remote_user => $remote_user, source_file => $source_file, @@ -609,7 +609,7 @@ sub copy_file my $target = defined $parameter->{target} ? $parameter->{target} : ""; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { overwrite => $overwrite, - password => $anvil->Log->secure ? $password : "--", + password => $anvil->Log->secure ? $password : "#!string!log_0186!#", remote_user => $remote_user, source_file => $source_file, target_file => $target_file, @@ -904,7 +904,7 @@ sub make_directory group => $group, mode => $mode, port => $port, - password => $anvil->Log->secure ? $password : "--", + password => $anvil->Log->secure ? $password : "#!string!log_0186!#", remote_user => $remote_user, target => $target, user => $user, @@ -1234,7 +1234,7 @@ sub read_file file => $file, force_read => $force_read, port => $port, - password => $anvil->Log->secure ? $password : "--", + password => $anvil->Log->secure ? $password : "#!string!log_0186!#", remote_user => $remote_user, secure => $secure, target => $target, @@ -1546,7 +1546,7 @@ sub rsync my $failed = 0; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, secure => 0, list => { destination => $destination, - password => $anvil->Log->secure ? $password : "--", + password => $anvil->Log->secure ? $password : "#!string!log_0186!#", port => $port, source => $source, switches => $switches, @@ -1860,11 +1860,11 @@ sub update_config my $update = 0; my $new_file = ""; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, secure => 0, list => { - password => $anvil->Log->secure ? $password : "--", + password => $anvil->Log->secure ? $password : "#!string!log_0186!#", port => $port, secure => $secure, target => $target, - value => ((not $secure) or ($anvil->Log->secure)) ? $value : "--", + value => ((not $secure) or ($anvil->Log->secure)) ? $value : "#!string!log_0186!#", variable => $variable, remote_user => $remote_user, }}); @@ -1901,7 +1901,7 @@ sub update_config my $is_secure = $this_variable =~ /passw/i ? 1 : 0; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, secure => 0, list => { this_variable => $this_variable, - this_value => ((not $is_secure) or ($anvil->Log->secure)) ? $this_value : "--", + this_value => ((not $is_secure) or ($anvil->Log->secure)) ? $this_value : "#!string!log_0186!#", }}); if ($this_variable eq $variable) { @@ -2060,7 +2060,7 @@ sub write_file mode => $mode, overwrite => $overwrite, port => $port, - password => $anvil->Log->secure ? $password : "--", + password => $anvil->Log->secure ? $password : "#!string!log_0186!#", secure => $secure, target => $target, user => $user, @@ -2326,7 +2326,7 @@ sub _create_rsync_wrapper my $target = defined $parameter->{target} ? $parameter->{target} : ""; my $password = defined $parameter->{password} ? $parameter->{password} : ""; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, secure => 0, list => { - password => $anvil->Log->secure ? $password : "--", + password => $anvil->Log->secure ? $password : "#!string!log_0186!#", target => $target, }}); diff --git a/Anvil/Tools/System.pm b/Anvil/Tools/System.pm index bed13d6b..8f972217 100755 --- a/Anvil/Tools/System.pm +++ b/Anvil/Tools/System.pm @@ -251,8 +251,8 @@ sub change_shell_user_password target => $target, port => $port, remote_user => $remote_user, - new_password => $anvil->Log->secure ? $new_password : "--", - password => $anvil->Log->secure ? $password : "--", + new_password => $anvil->Log->secure ? $new_password : "#!string!log_0186!#", + password => $anvil->Log->secure ? $password : "#!string!log_0186!#", }}); # Do I have a user? @@ -1335,7 +1335,7 @@ sub ping count => $count, fragment => $fragment, payload => $payload, - password => $anvil->Log->secure ? $password : "--", + password => $anvil->Log->secure ? $password : "#!string!log_0186!#", ping => $ping, port => $port, remote_user => $remote_user, diff --git a/cgi-bin/home b/cgi-bin/home index 4dfc2a13..04763582 100755 --- a/cgi-bin/home +++ b/cgi-bin/home @@ -26,8 +26,7 @@ if (($running_directory =~ /^\./) && ($ENV{PWD})) my $anvil = Anvil::Tools->new(); -# Print the html headers, with a new line to break the header from the body. -print $anvil->Template->get({file => "shared.html", name => "http_headers"})."\n"; +### NOTE: We'll print the headers only when we need to. If we print them hear, it will block cookies being set. # Set the log level to 2. Setting 3 slows he program down a LOT. $anvil->Log->level({set => 2}); @@ -51,6 +50,7 @@ $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, key => " if (not $connections) { # No databases, exit. + print $anvil->Template->get({file => "shared.html", name => "http_headers"})."\n"; print $anvil->Words->string({key => "error_0003"}); $anvil->nice_exit({exit_code => 2}); } @@ -107,6 +107,7 @@ my $template = $anvil->Template->get({file => "main.html", name => "master", var footer => $footer, }}); +print $anvil->Template->get({file => "shared.html", name => "http_headers"})."\n"; print $template; $anvil->nice_exit({exit_code => 0}); @@ -121,7 +122,65 @@ sub process_task { my ($anvil) = @_; - my $body = "hi"; + # Is the user trying to log in? + my $body = "hi"; + my $logged_in = 0; + if ((defined $anvil->data->{cgi}{login}) && ($anvil->data->{cgi}{login}{value})) + { + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "cgi::login::value" => $anvil->data->{cgi}{login}{value} }}); + + # Woot! + my $failed = $anvil->Account->login({debug => 2}); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { failed => $failed }}); + if (not $failed) + { + $logged_in = 1; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { logged_in => $logged_in }}); + } + } + else + { + # Is the user logged in? + # 0 - The cookies were read, the account was validated and the user's details were loaded. + # 1 - No cookie was found or read. The user needs to log in + # 2 - There was a problem reading the user's UUID (it wasn't found in the database), so the + # cookies were deleted (via C<< Account->_logout() >>. The user needs to log back in. + # 3 - There user's hash is invalid, it is probably expired. The user has been logged out and + # needs to log back in. + my $cookie_problem = $anvil->Account->read_cookies({debug => 2}); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { cookie_problem => $cookie_problem }}); + if (not $cookie_problem) + { + $logged_in = 1; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { logged_in => $logged_in }}); + } + } + + # Show the login screen, if the user isn't logged in. + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { logged_in => $logged_in }}); + if (not $logged_in) + { + $body = $anvil->Template->get({file => "main.html", name => "striker-login", variables => { + user => $anvil->data->{cgi}{username}{value}, + password => "", + }}); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { body => $body }}); + return($body); + } + + # If we're here, the user is logged in! + $body = "logged in as: [".$anvil->data->{sys}{users}{user_name}."]\n"; + +# $anvil->data->{sys}{users}{user_name} = $user_name; +# $anvil->data->{sys}{users}{user_uuid} = $user_uuid; +# $anvil->data->{sys}{users}{user_password_hash} = $user_password_hash, +# $anvil->data->{sys}{users}{user_salt} = $user_salt, +# $anvil->data->{sys}{users}{user_algorithm} = $user_algorithm, +# $anvil->data->{sys}{users}{user_hash_count} = $user_hash_count, +# $anvil->data->{sys}{users}{user_language} = $user_language, +# $anvil->data->{sys}{users}{user_is_admin} = $user_is_admin, +# $anvil->data->{sys}{users}{user_is_experienced} = $user_is_experienced, +# $anvil->data->{sys}{users}{user_is_trusted} = $user_is_trusted, return($body); } diff --git a/html/skins/alteeve/main.css b/html/skins/alteeve/main.css index 681a5540..c6eaeeb7 100644 --- a/html/skins/alteeve/main.css +++ b/html/skins/alteeve/main.css @@ -10,6 +10,16 @@ Colours; - Footer text: #515151 */ +.login { + width: 600px; + height: 50px; + position: fixed; + margin-left: -300px; /* half of width */ + margin-top: -25px; /* half of height */ + top: 50%; + left: 50%; +} + .error_message { border-left: 3px solid #d02724; border-right: 3px solid #d02724; @@ -28,7 +38,7 @@ input[type=text].input_alert, input[type=number].input_alert, select.input_alert border: 1px solid #ff3f3f; } -input[type=text], input[type=number] { +input[type=text], input[type=number], input[type=password] { width: 100%; padding: 6px 10px; margin: 2px 0; @@ -54,6 +64,10 @@ select { font-size: 1.0em; } +.code { + font-family: 'Dejavu Sans Mono', Courier; +} + body { font-family: 'Dejavu Sans', Arial, Helvetica, Verdana, Sans-Serif; background-image: url("/skins/alteeve/images/Texture.jpg"); @@ -175,6 +189,7 @@ td { .subtle_text { color: #9D9D9D; text-align: left; + font-size: 0.9em; } .header { diff --git a/html/skins/alteeve/main.html b/html/skins/alteeve/main.html index af8b9443..b47416d5 100644 --- a/html/skins/alteeve/main.html +++ b/html/skins/alteeve/main.html @@ -330,6 +330,38 @@ + + + +
+
+ + + + + + + + + + + + + + + diff --git a/share/words.xml b/share/words.xml index 53781403..fb78d299 100644 --- a/share/words.xml +++ b/share/words.xml @@ -57,6 +57,7 @@ NOTE: You must update the password of any other system using this host's Failed to write the new password to the temporary file: [#!variable!file!#]. Please check the logs for details. Beginning configuration of local system. + Use 'anvil-change-password' from the console to reset it.]]> Starting: [#!variable!program!#]. @@ -268,6 +269,16 @@ The database connection error was: The IP hash key: [#!variable!ip_key!#] does not exist, skipping it. + No cookies were read, the use is not logged in. + The user's UUID: [#!variable!uuid!#] was read, but it didn't match any known users. + The user has been logged out. + The user hash in the user's cookie is valid. + The user hash in the user's cookie was valid yesterday, updating the stored hash and allowing the user to proceed. + The user hash in the user's coolkie is invalid. It is probably expired. + The user: [#!variable!user!#] logged in successfully. + Theew was a failed login attempt from: [#!variable!user_agent!#], trying to log in as: [#!variable!user!#]. log in rejected. + ]]> + ]]> Test @@ -350,6 +361,9 @@ Here we will inject 't_0006', which injects 't_0001' which has a variable: [#!st Offline... A job to reconfigure this Striker is underway. It is: [#!variable!percent!#%] done. It last updated its progress at: [#!variable!timestamp!#] (#!variable!seconds_ago!# seconds ago). Please try again shortly. This indicates that this machine has been configured. After an initial install, this variable won't exist. If it is set to '0', it will trigger a reconfiguration of the local system. + Log in + User name + Password Configure Network @@ -381,6 +395,11 @@ Here we will inject 't_0006', which injects 't_0001' which has a variable: [#!st The: [#!variable!field!#] field can't be empty. The prefix needs to be set, and be between 1 and 5 characters long. The: [#!variable!field!#] must be a positive integer. + There was a problem reading your session details. To be safe, you have been logged out. Please try logging back in. + It appears that your session has expired. To be safe, you have been logged out. Please try logging back in. + The user ID passed in to Account->read_details: [#!variable!uuid!#] is not a valid UUID. + The user ID passed in to Account->read_details: [#!variable!uuid!#] was not found in the database. + Login failed, please try again. Up diff --git a/tools/anvil-change-password b/tools/anvil-change-password index 72ea53c0..258fd9ec 100755 --- a/tools/anvil-change-password +++ b/tools/anvil-change-password @@ -220,7 +220,7 @@ sub update_local_passwords print $anvil->Words->string({key => "message_0026", variables => { user => $user }}); my $query = "ALTER ROLE ".$user." WITH PASSWORD ".$dbh->quote($anvil->data->{switches}{'new-password'}); $dbh->do($query) or $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0090", variables => { - query => $anvil->Log->secure ? $query : "--", + query => $anvil->Log->secure ? $query : "#!string!log_0186!#", server => "localhost", db_error => $DBI::errstr, }}); diff --git a/tools/anvil.sql b/tools/anvil.sql index 98bd53d5..931b6e23 100644 --- a/tools/anvil.sql +++ b/tools/anvil.sql @@ -44,6 +44,7 @@ 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_hash text not null, -- A user without a password is disabled. + user_session_salt text not null, -- This is used when generating a session hash for a user when they log in. user_salt text not null, -- This is used to enhance the security of the user's password. user_algorithm text not null, -- This is the algorithm used to encrypt the password and salt. user_hash_count text not null, -- This is the number of times that the password+salt was re-hashed through the algorithm. @@ -60,6 +61,7 @@ CREATE TABLE history.users ( user_uuid uuid, user_name text, user_password_hash text, + user_session_salt text, user_salt text, user_algorithm text, user_hash_count text, @@ -81,6 +83,7 @@ BEGIN (user_uuid, user_name, user_password_hash, + user_session_salt, user_salt, user_algorithm, user_hash_count, @@ -93,6 +96,7 @@ BEGIN (history_users.user_uuid, history_users.user_name, history_users.user_password_hash, + history_users.user_session_salt, history_users.user_salt, history_users.user_algorithm, history_users.user_hash_count,