diff -urN smeserver-manager-0.1.4.old/createlinks smeserver-manager-0.1.4/createlinks
--- smeserver-manager-0.1.4.old/createlinks 2021-06-21 13:25:10.000000000 +0400
+++ smeserver-manager-0.1.4/createlinks 2022-07-18 14:14:26.458000000 +0400
@@ -47,3 +47,8 @@
event_link('systemd-default', "smeserver-manager-update", '88');
event_link('systemd-reload', "smeserver-manager-update", '89');
+
+use esmith::Build::Backup qw(:all);
+backup_includes("smeserver-manager", qw(
+/usr/share/smanager/data
+));
diff -urN smeserver-manager-0.1.4.old/root/etc/e-smith/templates/etc/httpd/conf/httpd.conf/VirtualHosts/27SManagerProxyPass smeserver-manager-0.1.4/root/etc/e-smith/templates/etc/httpd/conf/httpd.conf/VirtualHosts/27SManagerProxyPass
--- smeserver-manager-0.1.4.old/root/etc/e-smith/templates/etc/httpd/conf/httpd.conf/VirtualHosts/27SManagerProxyPass 2022-07-17 20:31:12.000000000 +0400
+++ smeserver-manager-0.1.4/root/etc/e-smith/templates/etc/httpd/conf/httpd.conf/VirtualHosts/27SManagerProxyPass 2022-07-17 21:08:53.332000000 +0400
@@ -27,19 +27,17 @@
$OUT .= " RequestHeader set X-Forwarded-Proto 'http'\n";
$OUT .= " \n";
- $OUT .= " order deny,allow\n";
- $OUT .= " deny from all\n";
if ($port eq $plainPort)
{
- $OUT .= ' allow from 127.0.0.1' . "\n";
+ $OUT .= ' Require ip 127.0.0.1' . "\n";
}
elsif (($haveSSL eq 'yes') && ($port eq $sslPort) && ($adminAccess eq 'public'))
{
$OUT .= "# public access requested in conf db\n";
- $OUT .= " allow from all\n";
+ $OUT .= " Require all granted\n";
} else {
$OUT .= "# private access by default\n";
- $OUT .= " allow from $localAccess $externalSSLAccess\n";
+ $OUT .= " Require ip $localAccess $externalSSLAccess\n";
}
$OUT .= " \n";
}
diff -urN smeserver-manager-0.1.4.old/root/etc/e-smith/templates/usr/share/smanager/conf/srvmngr.conf/25Pwdrst smeserver-manager-0.1.4/root/etc/e-smith/templates/usr/share/smanager/conf/srvmngr.conf/25Pwdrst
--- smeserver-manager-0.1.4.old/root/etc/e-smith/templates/usr/share/smanager/conf/srvmngr.conf/25Pwdrst 1970-01-01 04:00:00.000000000 +0400
+++ smeserver-manager-0.1.4/root/etc/e-smith/templates/usr/share/smanager/conf/srvmngr.conf/25Pwdrst 2022-01-24 20:32:49.549000000 +0400
@@ -0,0 +1,4 @@
+ # password reset disabled by default
+ pwdreset => { ($smanager{'PwdReset'} eq 'enabled' ? '1' : '0') || '0' },
+ # reset delay in hours
+ pwdreset_delay => 2,
diff -urN smeserver-manager-0.1.4.old/root/usr/share/smanager/conf/admin_muttrc smeserver-manager-0.1.4/root/usr/share/smanager/conf/admin_muttrc
--- smeserver-manager-0.1.4.old/root/usr/share/smanager/conf/admin_muttrc 1970-01-01 04:00:00.000000000 +0400
+++ smeserver-manager-0.1.4/root/usr/share/smanager/conf/admin_muttrc 2022-01-24 20:32:49.549000000 +0400
@@ -0,0 +1,5 @@
+set from = "admin"
+set realname = "Administrator"
+set record = "/usr/share/smanager/log/mail_sent"
+##set content_type = "text/html"
+
diff -urN smeserver-manager-0.1.4.old/root/usr/share/smanager/lib/SrvMngr/Controller/Login.pm smeserver-manager-0.1.4/root/usr/share/smanager/lib/SrvMngr/Controller/Login.pm
--- smeserver-manager-0.1.4.old/root/usr/share/smanager/lib/SrvMngr/Controller/Login.pm 2021-06-21 13:25:10.000000000 +0400
+++ smeserver-manager-0.1.4/root/usr/share/smanager/lib/SrvMngr/Controller/Login.pm 2022-01-24 20:32:49.550000000 +0400
@@ -7,6 +7,8 @@
# for information
# $r->get('/login')->to('login#main')->name('login');
# $r->post('/login')->to('login#login')->name('signin');
+# $r->get('/login2')->to('login#pwdrescue')->name('pwdresc');
+# $r->get('/loginc')->to('login#confpwd')->name('resetpwdconf');
# for information
use strict;
@@ -47,6 +49,18 @@
my $trt = $c->param('Trt');
+ # password reset request
+ if ( $trt eq 'RESET' ) {
+ my $res = $c->mail_rescue();
+ if ( $res ne 'OK' ) {
+ $c->stash( error => $res, trt => $trt );
+ return $c->render('login');
+ }
+ $c->flash( success => $c->l('use_RESET_REGISTERED') );
+ record_login_attempt($c, 'RESET');
+ return $c->redirect_to( $c->home_page );
+ }
+
# normal loggin
my $name = $c->param('Username');
my $pass = $c->param('Password');
@@ -64,7 +78,6 @@
return $c->render('login');
}
-
my $alias = SrvMngr::Model::Main->check_adminalias( $c );
if ( $alias ) {
if ( $name eq $alias ) {
@@ -79,13 +92,14 @@
if (SrvMngr::Model::Main->check_credentials($name, $pass)) {
$c->session(logged_in => 1); # set the logged_in flag
$c->session(username => $name); # keep a copy of the username
-# if ( $name eq 'admin' || $adb->is_user_in_group($name, 'AdmiN') ) { # for futur use
+# if ( $name eq 'admin' || $adb->is_user_in_group($name, 'AdmiN') ) # for futur use
if ( $name eq 'admin' ) {
$c->session(is_admin => 1);
} else {
$c->session(is_admin => 0);
}
$c->session(expiration => 600); # expire this session in 10 minutes
+
$c->flash( success => $c->l('use_WELCOME') );
record_login_attempt($c, 'SUCCESS');
} else {
@@ -102,6 +116,68 @@
}
+sub pwdrescue {
+
+ my $c = shift;
+
+ $c->stash( trt => 'RESET' );
+
+ $c->render('login');
+
+}
+
+
+sub mail_rescue {
+
+ my $c = shift;
+ my $name = $c->param('Username');
+ my $from = $c->param('From');
+
+ my $res;
+
+ $res .= $c->l('use_TOO_MANY_LOGIN') if ( is_denied($c) );
+
+# untaint
+ if ( ! $res && $name !~ /^([a-z][\-\_\.a-z0-9]*)$/ ) {
+ record_login_attempt($c, 'FAILED');
+ $res .= $c->l('use_ERR_NAME');
+ }
+
+ if ( ! $res && $name eq 'admin' ) {
+ $res .= $c->l('use_NOT_THAT_OPER');
+ }
+
+# user exists ?
+ if ( ! $res ) {
+ my $acct = $adb->get($name);
+ if ( ! $acct || $acct->prop('type') ne "user" || $acct->prop('PasswordSet') ne 'yes' ) {
+ $res .= $c->l('use_NOT_THAT_OPER');
+ }
+ }
+
+ return $res if $res;
+
+# send email
+ my $email = $name .'@'. $c->session->{DomainName};
+ my $until = time() + $RESET_DURATION;
+
+ $c->pwdrst->{$name} = {
+ email => $email,
+ date => $until,
+ confirmed => 0,
+ };
+ my $jwt = $c->jwt->claims({username => $name})->encode;
+ my $url = $c->url_for('loginc')->to_abs->query(jwt => $jwt);
+
+# $c->email( $email, $c->l('use_CONFIRM_RESET'), $c->render_to_string(inline => $c->l('use_GO_TO_URL', $url) ) );
+# directly (without minion)
+ $c->send_email( $email, $c->l('use_CONFIRM_RESET'), $c->render_to_string(inline => $c->l('use_GO_TO_URL', $url) ) );
+
+ return 'OK';
+
+}
+
+
sub logout {
my $c = shift;
@@ -109,19 +185,52 @@
$c->session( expires => 1 );
$c->flash( success => $c->l('use_BYE') );
+ $c->flash( error => 'Byegood' );
$c->redirect_to( $c->home_page );
}
+sub confpwd {
+
+ my $c = shift;
+
+ my $jwt = $c->param('jwt');
+ my $name = $c->jwt->decode($jwt)->{username};
+
+ # request already treated or outdated
+ if ( $c->pwdrst->{$name}{confirmed} != 0 or $c->pwdrst->{$name}{date} < time() ) {
+ $c->flash( error => $c->l('use_INVALID_REQUEST'));
+ return $c->redirect_to( $c->home_page );
+ }
+
+ # reset password for this account
+ $c->pwdrst->{$name}{confirmed} = 1;
+
+ $c->flash( success => $c->l('use_OK_FOR_RESET') );
+
+ # call userpassword with encoded name
+ my $url = $c->url_for('userpasswordr')->to_abs->query(jwt => $jwt);
+ # warn "confpwd: " . $url . "\n";
+
+ return $c->redirect_to( $url );
+
+}
+
+
sub record_login_attempt {
+
my ($c, $result) = @_;
my $user = $c->param('Username');
my $ip_address = $c->tx->remote_address;
- if ($result eq 'SUCCESS') {
+ if ($result eq 'RESET') {
+
+ $c->app->log->info(join "\t", "Password reset requested for : $user at ", $ip_address);
+
+ } elsif ($result eq 'SUCCESS') {
$c->app->log->info(join "\t", "Login succeeded: $user", $ip_address);
$Login_Attempts{$ip_address}->{tries} = 0; # reset the number of login attempts
diff -urN smeserver-manager-0.1.4.old/root/usr/share/smanager/lib/SrvMngr/Controller/Userpassword.pm smeserver-manager-0.1.4/root/usr/share/smanager/lib/SrvMngr/Controller/Userpassword.pm
--- smeserver-manager-0.1.4.old/root/usr/share/smanager/lib/SrvMngr/Controller/Userpassword.pm 2020-11-19 11:53:26.000000000 +0400
+++ smeserver-manager-0.1.4/root/usr/share/smanager/lib/SrvMngr/Controller/Userpassword.pm 2022-07-11 23:14:53.574000000 +0400
@@ -33,8 +33,29 @@
$pwd_datas{Account} = $c->session->{username};
$pwd_datas{trt} = 'NORM';
} else {
- $c->stash( error => 'Invalid state' );
- return $c->redirect_to ( $c->home_page );
+ my $rt = $c->current_route;
+ my $mess = '';
+ my $jwt = $c->param('jwt') || '';
+ my $name = $c->jwt->decode($jwt)->{username} || '';
+
+ $mess = 'Invalid state' unless ($jwt and $name and $rt eq 'upwdreset');
+
+ # request already treated or outdated
+ if ( $c->pwdrst->{$name}{confirmed} != 1 or $c->pwdrst->{$name}{date} < time() ) {
+ $mess = $c->l('use_INVALID_REQUEST').' -step 1-';
+ }
+
+ if ( $mess ) {
+ $c->stash( error => $mess );
+ return $c->redirect_to ( $c->home_page );
+ }
+
+ # ok for reset password for this account - step 2
+ $c->pwdrst->{$name}{confirmed} = 2;
+ $pwd_datas{Account} = $name;
+ $pwd_datas{trt} = 'RESET';
+ $pwd_datas{jwt} = $jwt;
+ $c->flash( success => $c->l('use_OK_FOR_RESET') );
}
$c->stash( pwd_datas => \%pwd_datas );
@@ -55,6 +76,31 @@
my $pass = $c->param('Pass');
my $passVerify = $c->param('Passverify');
+ my $jwt = $c->param('jwt') || '';
+ my $rt = $c->current_route;
+ my $mess = ''; my $name = '';
+ $name = $c->jwt->decode($jwt)->{username} if $jwt;
+
+ if ( $trt eq 'RESET' ) {
+ $mess = 'Invalid state' unless ($jwt and $name and ($rt eq 'upwdreset2'));
+ # request already treated or outdated
+ if ( $c->pwdrst->{$name}{confirmed} != 2 or $c->pwdrst->{$name}{date} < time() ) {
+ $mess = $c->l('use_INVALID_REQUEST').' -step 2-';
+ }
+ if ( ! $name or $c->is_logged_in or $name ne $acctName ) {
+ $mess = 'Invalid reset state';
+ }
+ } else {
+ if ( $name or $jwt or ! $c->is_logged_in ) {
+ $mess = 'Invalid update state';
+ }
+ }
+
+ if ( $mess ) {
+ $c->stash( error => $mess );
+ return $c->redirect_to ( $c->home_page );
+ }
+
$pwd_datas{Account} = $acctName;
$pwd_datas{trt} = $trt;
@@ -79,8 +125,10 @@
$res = $c->check_password( $pass );
$result .= $res . "
" unless ( $res eq 'OK' );
+ # controls old password
+ if ( $trt ne 'RESET' ) {
unless ( $oldPass ) {
- $result .= $c->l('pwd_FIELDS_REQUIRED') . "
";
+ $result .= $c->l('pwd_FIELDS_REQUIRED') . "
" unless $trt eq 'RESET';
} else {
$result .= $c->l('pwd_PASSWORD_OLD_INVALID_CHARS') . "
" unless (($oldPass) = ($oldPass =~ /^(\S+)$/ ));
}
@@ -91,8 +139,11 @@
}
# verify old password
- $result .= $c->l('pwd_ERROR_PASSWORD_CHANGE') . "
"
- unless (SrvMngr::Model::Main->check_credentials($acctName, $oldPass));
+ if ( $trt ne 'RESET') {
+ $result .= $c->l('pwd_ERROR_PASSWORD_CHANGE') . "
"
+ unless (SrvMngr::Model::Main->check_credentials($acctName, $oldPass));
+ }
+ }
# $result .= 'Blocked for test (prevents updates)
';
@@ -107,6 +158,7 @@
return $c->render( 'userpassword' );
}
+ $c->pwdrst->{$name}{confirmed} = 9 if $trt eq 'RESET';
record_password_change_attempt($c, 'SUCCESS');
$result .= $c->l('pwd_PASSWORD_CHANGE_SUCCESS');
$c->flash( success => $result );
@@ -127,7 +179,8 @@
my $acct = $adb->get($user);
return $c->l('NO_SUCH_USER', $user) unless ( $acct->prop('type') eq 'user' );
- $ret = esmith::util::setUserPasswordRequirePrevious( $user, $oldpassword, $password );
+ $ret = esmith::util::setUserPasswordRequirePrevious( $user, $oldpassword, $password ) if $trt ne 'RESET';
+ $ret = esmith::util::setUserPassword( $user, $password ) if $trt eq 'RESET';
return $c->l('pwd_ERROR_PASSWORD_CHANGE') .' '. $trt unless $ret;
diff -urN smeserver-manager-0.1.4.old/root/usr/share/smanager/lib/SrvMngr/I18N/Modules/Login/login_en.lex smeserver-manager-0.1.4/root/usr/share/smanager/lib/SrvMngr/I18N/Modules/Login/login_en.lex
--- smeserver-manager-0.1.4.old/root/usr/share/smanager/lib/SrvMngr/I18N/Modules/Login/login_en.lex 2020-11-19 11:53:26.000000000 +0400
+++ smeserver-manager-0.1.4/root/usr/share/smanager/lib/SrvMngr/I18N/Modules/Login/login_en.lex 2022-01-24 20:32:49.551000000 +0400
@@ -20,3 +20,4 @@
use_DESC_RESET => 'Please enter an account name for a password reset !',
use_RESET => 'Reset Password',
use_OK_FOR_RESET => 'You are about to reset your user account password',
+use_INVALID_REQUEST => 'Error: your request is invalid or outdated',
diff -urN smeserver-manager-0.1.4.old/root/usr/share/smanager/lib/SrvMngr.pm smeserver-manager-0.1.4/root/usr/share/smanager/lib/SrvMngr.pm
--- smeserver-manager-0.1.4.old/root/usr/share/smanager/lib/SrvMngr.pm 2022-07-17 20:31:12.000000000 +0400
+++ smeserver-manager-0.1.4/root/usr/share/smanager/lib/SrvMngr.pm 2022-07-18 13:53:55.920000000 +0400
@@ -15,6 +15,9 @@
use Mojo::File qw( path );
use Mojo::Home;
+use DBM::Deep;
+use Mojo::JWT;
+
use Mojolicious::Plugin::Config;
#use Mojolicious::Plugin::I18N;
@@ -23,7 +26,7 @@
use SrvMngr::Model::Main;
-our $VERSION = '1.411';
+our $VERSION = '1.417';
$VERSION = eval $VERSION;
use Exporter 'import';
@@ -168,6 +171,30 @@
$self->plugin( Config => { file => $self->config_file()} );
+ $self->helper( send_email => sub {
+ my ($c, $address, $subject, $body) = @_;
+
+ if (not defined $body) {
+ warn "send_email: Need 3 parameters (Address, Subject, Body)\n";
+ return;
+ }
+
+ my $rcfile = $c->app->conf_dir().'/admin_muttrc';
+
+ #warn "send_email: $rcfile * $address\n"; #$rcfile $subject $address\n";
+ system( "/bin/echo \"$body\" | /usr/bin/mutt -F $rcfile -s \"$subject\" \"$address\"" ) == 0
+ or warn "error sendmail: $address \n"; # $subject";
+ });
+
+ $self->helper( pwdrst => sub {
+ my $c = shift;
+ my $file = $c->app->data_dir().'/pwdrst.db';
+ state $db = DBM::Deep->new($file);
+ });
+
+ $self->helper( jwt => sub {
+ Mojo::JWT->new(secret => shift->app->secrets->[0] || die)
+ });
}
@@ -222,6 +249,14 @@
$r->get('/manual')->to('manual#main')->name('manual');
$r->get('/support')->to('support#main')->name('support');
+ # Password reset allowed for this server
+ if ( ( $self->config->{pwdreset} || '0') == 1 ) {
+ $r->get('/login2')->to('login#pwdrescue')->name('pwdresc');
+ $r->get('/loginc')->to('login#confpwd')->name('resetpwdconf');
+ $r->get('/userpasswordr')->to('userpassword#main')->name('upwdreset');
+ $r->post('/userpasswordr')->to('userpassword#change_password')->name('upwdreset2');
+ }
+
my $if_logged_in = $r->under( sub {
my $c =shift;
return $c->is_logged_in || $c->auth_fail($c->l("acs_LOGIN"));
diff -urN smeserver-manager-0.1.4.old/root/usr/share/smanager/t/001_load.t smeserver-manager-0.1.4/root/usr/share/smanager/t/001_load.t
--- smeserver-manager-0.1.4.old/root/usr/share/smanager/t/001_load.t 2021-06-21 13:25:11.000000000 +0400
+++ smeserver-manager-0.1.4/root/usr/share/smanager/t/001_load.t 2022-01-24 20:32:49.551000000 +0400
@@ -2,7 +2,7 @@
plan skip_all => 'unset QUICK_TEST to enable this test' if $ENV{QUICK_TEST};
-plan tests => 6;
+plan tests => 8;
use FindBin;
use lib "$FindBin::Bin/../lib";
@@ -15,3 +15,5 @@
use_ok('Mojolicious::Plugin::RenderFile');
use_ok('Mojolicious::Plugin::CSRFDefender');
use_ok('Net::Netmask');
+use_ok('DBM::Deep');
+use_ok('Mojo::JWT');
diff -urN smeserver-manager-0.1.4.old/root/usr/share/smanager/themes/default/templates/login.html.ep smeserver-manager-0.1.4/root/usr/share/smanager/themes/default/templates/login.html.ep
--- smeserver-manager-0.1.4.old/root/usr/share/smanager/themes/default/templates/login.html.ep 2022-07-17 20:31:12.000000000 +0400
+++ smeserver-manager-0.1.4/root/usr/share/smanager/themes/default/templates/login.html.ep 2022-01-24 20:32:49.552000000 +0400
@@ -4,7 +4,7 @@
- %if ($config->{debug} == 1) {
+ %if ( config 'debug' ) {
%= dumper $c->current_route
%if ( stash 'trt' ) {
@@ -19,7 +19,13 @@
%}
- % my $btn = l('use_SIGNIN');
+% my $btn = l('use_SIGNIN');
+% if ( $trt eq 'RESET' ) {
+
+ %= $c->render_to_string(inline => l 'use_DESC_RESET')
+
+ % $btn = l('use_RESET');
+%}
%=l 'use_TITLE'
@@ -33,6 +39,7 @@
%= text_field 'Username'
+% if ( $trt ne 'RESET' ) {
%=l 'PASSWORD'
@@ -41,17 +48,22 @@
% }
+%}
%= hidden_field 'From' => $c->tx->req->url
- %= hidden_field 'Trt' => stash 'trt'
+ %= hidden_field 'Trt' => $trt
%= submit_button "$btn", class => 'action'
+ %if ( config 'pwdreset' ) {
+
+ %}
% end
%end
-
diff -urN smeserver-manager-0.1.4.old/root/usr/share/smanager/themes/default/templates/partials/_header.html.ep smeserver-manager-0.1.4/root/usr/share/smanager/themes/default/templates/partials/_header.html.ep
--- smeserver-manager-0.1.4.old/root/usr/share/smanager/themes/default/templates/partials/_header.html.ep 2022-07-17 20:31:12.000000000 +0400
+++ smeserver-manager-0.1.4/root/usr/share/smanager/themes/default/templates/partials/_header.html.ep 2022-07-18 18:16:57.639000000 +0400
@@ -3,8 +3,8 @@
-
diff -urN smeserver-manager-0.1.4.old/root/usr/share/smanager/themes/default/templates/userpassword.html.ep smeserver-manager-0.1.4/root/usr/share/smanager/themes/default/templates/userpassword.html.ep
--- smeserver-manager-0.1.4.old/root/usr/share/smanager/themes/default/templates/userpassword.html.ep 2022-07-17 20:31:12.000000000 +0400
+++ smeserver-manager-0.1.4/root/usr/share/smanager/themes/default/templates/userpassword.html.ep 2022-01-24 20:32:49.552000000 +0400
@@ -21,52 +21,50 @@
%}
- % my $btn = l('pwd_PASSWORD_CHANGE');
-
-% my $url = '/userpassword';
+ % my $btn = l('pwd_PASSWORD_CHANGE');
+ % my $url = '/userpassword';
%= $c->render_to_string( inline => l('pwd_DESCRIPTION'));
+ % if ( $pwd_datas->{trt} eq 'RESET' ) {
+ % $btn = l('pwd_PASSWORD_RESET');
+ % $url = '/userpasswordr';
+ %= $c->render_to_string( inline => l('pwd_DESCRIPTION_RESET'));
+ % }
%= form_for $url => (method => 'POST') => begin
-
-
-
+
%= l 'pwd_YOUR_ACCOUNT'
%= $pwd_datas->{Account}
%= hidden_field 'User' => $pwd_datas->{Account}
%= hidden_field 'Trt' => $pwd_datas->{trt}
-
-
+ %= hidden_field 'jwt' => $pwd_datas->{jwt}
+
-
-
+ % if ( $pwd_datas->{trt} ne 'RESET' ) {
+
%= l 'pwd_PASSWORD_OLD'
%= password_field 'Oldpass', class => 'input'
-
-
-
-
-
+
+ % }
+
+
%=l 'pwd_PASSWORD_NEW'
%= password_field 'Pass', class => 'input'
-
-
-
-
-
+
+
+
%=l 'pwd_PASSWORD_VERIFY_NEW'
%= password_field 'Passverify', class => 'input'
-
-
+
%= submit_button "$btn", class => 'action'