smeserver-manager/root/usr/share/smanager/lib/SrvMngr/Controller/Userpassword.pm

258 lines
7.0 KiB
Perl

package SrvMngr::Controller::Userpassword;
#----------------------------------------------------------------------
# heading : Current User
# description : Change password
# navigation : 1000 250
# menu : U
#
# routes : end
#----------------------------------------------------------------------
use strict;
use warnings;
use Mojo::Base 'Mojolicious::Controller';
use esmith::util;
use esmith::ConfigDB;
use esmith::AccountsDB;
use Locale::gettext;
use SrvMngr::I18N;
use SrvMngr qw( theme_list init_session is_normal_password );
our $cdb = esmith::ConfigDB->open_ro || die "Couldn't open configuration db";
sub main {
my $c = shift;
my %pwd_datas = ();
if ( $c->is_logged_in ) {
$pwd_datas{Account} = $c->session->{username};
$pwd_datas{trt} = 'NORM';
} else {
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 );
$c->render( 'userpassword' );
}
sub change_password {
my $c = shift;
my $result;
my $res;
my %pwd_datas = ();
my $trt = $c->param('Trt');
my $acctName = $c->param('User');
my $oldPass = $c->param('Oldpass') || '';
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;
# common controls
if ( $acctName eq 'admin' ) {
$result .= "Admin password should not be reset here !";
} else {
unless ( $pass && $passVerify ) {
$result .= $c->l('pwd_FIELDS_REQUIRED') . "<br>";
} else {
$result .= $c->l('pwd_PASSWORD_INVALID_CHARS') . "<br>" unless (($pass) = ($pass =~ /^([ -~]+)$/ ));
$result .= $c->l('pwd_PASSWORD_VERIFY_ERROR') . "<br>" unless ( $pass eq $passVerify );
}
}
if ( $result ne '' ) {
$c->stash( error => $result, pwd_datas => \%pwd_datas );
return $c->render( 'userpassword' );
}
# validate new password
$res = $c->check_password( $pass );
$result .= $res . "<br>" unless ( $res eq 'OK' );
# controls old password
if ( $trt ne 'RESET' ) {
unless ( $oldPass ) {
$result .= $c->l('pwd_FIELDS_REQUIRED') . "<br>" unless $trt eq 'RESET';
} else {
$result .= $c->l('pwd_PASSWORD_OLD_INVALID_CHARS') . "<br>" unless (($oldPass) = ($oldPass =~ /^(\S+)$/ ));
}
if ( $result ne '' ) {
$c->stash( error => $result, pwd_datas => \%pwd_datas );
return $c->render( 'userpassword' );
}
# verify old password
if ( $trt ne 'RESET') {
$result .= $c->l('pwd_ERROR_PASSWORD_CHANGE') . "<br>"
unless (SrvMngr::Model::Main->check_credentials($acctName, $oldPass));
}
}
# $result .= 'Blocked for test (prevents updates)<br>';
if ( ! $result ) {
my $res = $c->reset_password( $trt, $acctName, $pass, $oldPass );
$result .= $res unless $res eq 'OK';
}
if ( $result ) {
record_password_change_attempt($c, 'FAILED');
$c->stash( error => $result, pwd_datas => \%pwd_datas );
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 );
$c->redirect_to( $c->home_page );
}
sub reset_password {
my ($c, $trt, $user, $password, $oldpassword) = @_;
my $ret;
return $c->l('usr_TAINTED_USER') unless (($user) = ($user =~ /^(\w[\-\w_\.]*)$/));
$user = $1;
my $adb = esmith::AccountsDB->open();
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 ) if $trt ne 'RESET';
$ret = esmith::util::setUserPassword( $user, $password ) if $trt eq 'RESET';
return $c->l('pwd_ERROR_PASSWORD_CHANGE') .' '. $trt unless $ret;
$acct->set_prop("PasswordSet", "yes");
undef $adb;
if (system("/sbin/e-smith/signal-event", "password-modify", $user)) {
$adb = esmith::AccountsDB->open();
return $c->l("usr_ERR_OCCURRED_MODIFYING_PASSWORD");
}
$adb = esmith::AccountsDB->open();
return 'OK';
}
sub record_password_change_attempt {
my ($c, $result) = @_;
my $user = $c->param('User');
my $ip_address = $c->tx->remote_address;
if ($result eq 'SUCCESS') {
$c->app->log->info(join "\t", "Password change succeeded: $user", $ip_address);
} else {
$c->app->log->info(join "\t", "Password change FAILED: $user", $ip_address);
}
}
sub check_password {
my $c = shift;
my $password = shift;
my $strength;
my $rec = $cdb->get('passwordstrength');
$strength = ($rec ? ($rec->prop('Users') || 'none') : 'none');
return validate_password( $c, $strength, $password );
}
sub validate_password {
my ($c, $strength, $pass) = @_;
use Crypt::Cracklib;
if ($strength eq "none") {
return $c->l("Passwords must be at least 7 characters long") unless ( length( $pass ) > 6 );
return "OK";
}
my $reason = is_normal_password($c, $pass, undef);
return $reason unless ($reason eq "OK");
return "OK" unless ($strength eq "strong");
if ( -f '/usr/lib64/cracklib_dict.pwd' ) {
$reason = fascist_check($pass, '/usr/lib64/cracklib_dict');
} else {
$reason = fascist_check($pass, '/usr/lib/cracklib_dict');
}
$reason ||= "Software error: password check failed";
return "OK" if ($reason eq "ok");
return $c->l("Bad Password Choice") . ": "
. $c->l("The password you have chosen is not a good choice, because")
. " " . $c->($reason) . ".";
}
1;