2019-01-16 23:00:38 +01:00
|
|
|
package Lemonldap::NG::Portal::Plugins::MailPasswordReset;
|
|
|
|
|
|
|
|
use strict;
|
|
|
|
use Encode;
|
|
|
|
use Mouse;
|
|
|
|
use POSIX qw(strftime);
|
|
|
|
use Lemonldap::NG::Common::FormEncode;
|
|
|
|
use Lemonldap::NG::Portal::Main::Constants qw(
|
2020-10-09 22:26:00 +02:00
|
|
|
PE_OK
|
|
|
|
PE_MAILOK
|
|
|
|
PE_NOTOKEN
|
|
|
|
PE_MAILERROR
|
|
|
|
PE_PASSWORD_OK
|
2019-02-05 23:12:17 +01:00
|
|
|
PE_BADMAILTOKEN
|
|
|
|
PE_CAPTCHAEMPTY
|
|
|
|
PE_CAPTCHAERROR
|
|
|
|
PE_MAILNOTFOUND
|
2020-10-09 22:26:00 +02:00
|
|
|
PE_TOKENEXPIRED
|
|
|
|
PE_USERNOTFOUND
|
|
|
|
PE_MAILCONFIRMOK
|
2019-02-05 23:12:17 +01:00
|
|
|
PE_MALFORMEDUSER
|
2020-10-09 22:26:00 +02:00
|
|
|
PE_MAILFORMEMPTY
|
|
|
|
PE_BADCREDENTIALS
|
|
|
|
PE_MAILFIRSTACCESS
|
2019-02-05 23:12:17 +01:00
|
|
|
PE_PASSWORDFORMEMPTY
|
|
|
|
PE_PASSWORD_MISMATCH
|
2020-10-09 22:26:00 +02:00
|
|
|
PE_PASSWORDFIRSTACCESS
|
2019-09-12 17:54:43 +02:00
|
|
|
PE_PP_PASSWORD_TOO_SHORT
|
|
|
|
PE_PP_PASSWORD_TOO_YOUNG
|
|
|
|
PE_PP_PASSWORD_IN_HISTORY
|
2020-10-09 22:26:00 +02:00
|
|
|
PE_MAILCONFIRMATION_ALREADY_SENT
|
|
|
|
PE_PP_INSUFFICIENT_PASSWORD_QUALITY
|
2019-01-16 23:00:38 +01:00
|
|
|
);
|
|
|
|
|
2020-10-09 22:26:00 +02:00
|
|
|
our $VERSION = '2.0.10';
|
2019-01-16 23:00:38 +01:00
|
|
|
|
2020-10-09 22:26:00 +02:00
|
|
|
extends qw(
|
|
|
|
Lemonldap::NG::Portal::Lib::SMTP
|
|
|
|
Lemonldap::NG::Portal::Main::Plugin
|
|
|
|
Lemonldap::NG::Portal::Lib::_tokenRule
|
|
|
|
);
|
2019-01-16 23:00:38 +01:00
|
|
|
|
|
|
|
# PROPERTIES
|
|
|
|
|
|
|
|
# Mail timeout token generator
|
|
|
|
# Form timout token generator (used even if requireToken is not set)
|
|
|
|
has ott => (
|
|
|
|
is => 'rw',
|
|
|
|
lazy => 1,
|
|
|
|
default => sub {
|
2019-02-05 23:12:17 +01:00
|
|
|
my $ott =
|
|
|
|
$_[0]->{p}->loadModule('Lemonldap::NG::Portal::Lib::OneTimeToken');
|
2019-01-16 23:00:38 +01:00
|
|
|
$ott->timeout( $_[0]->conf->{formTimeout} );
|
|
|
|
return $ott;
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
|
|
|
# Captcha generator
|
|
|
|
has captcha => ( is => 'rw' );
|
|
|
|
|
|
|
|
# INITIALIZATION
|
|
|
|
|
|
|
|
sub init {
|
|
|
|
my ($self) = @_;
|
|
|
|
|
|
|
|
# Declare REST route
|
|
|
|
$self->addUnauthRoute( resetpwd => 'resetPwd', [ 'POST', 'GET' ] );
|
|
|
|
|
|
|
|
# Initialize Captcha if needed
|
|
|
|
if ( $self->conf->{captcha_mail_enabled} ) {
|
|
|
|
$self->captcha( $self->p->loadModule('::Lib::Captcha') ) or return 0;
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
# RUNNIG METHODS
|
|
|
|
|
|
|
|
# Handle reset requests
|
|
|
|
sub resetPwd {
|
|
|
|
my ( $self, $req ) = @_;
|
|
|
|
|
|
|
|
$self->p->controlUrl($req);
|
|
|
|
|
|
|
|
# Check parameters
|
|
|
|
$req->error( $self->_reset($req) );
|
|
|
|
|
|
|
|
# Display form
|
|
|
|
my ( $tpl, $prms ) = $self->display($req);
|
|
|
|
return $self->p->sendHtml( $req, $tpl, params => $prms );
|
|
|
|
}
|
|
|
|
|
|
|
|
sub _reset {
|
|
|
|
my ( $self, $req ) = @_;
|
2019-04-05 10:37:48 +02:00
|
|
|
my ( $mailToken, %tplPrms );
|
2019-01-16 23:00:38 +01:00
|
|
|
|
|
|
|
# PASSWORD CHANGE FORM => changePwd()
|
2019-02-05 23:12:17 +01:00
|
|
|
if (
|
|
|
|
$req->method =~ /^POST$/i
|
2019-01-16 23:00:38 +01:00
|
|
|
and ( $req->param('newpassword')
|
|
|
|
or $req->param('confirmpassword')
|
|
|
|
or $req->param('reset') )
|
2019-02-05 23:12:17 +01:00
|
|
|
)
|
2019-01-16 23:00:38 +01:00
|
|
|
{
|
|
|
|
return $self->changePwd($req);
|
|
|
|
}
|
|
|
|
|
|
|
|
# FIRST FORM
|
|
|
|
$mailToken = $req->data->{mailToken} = $req->param('mail_token');
|
|
|
|
unless ( $req->param('mail') || $mailToken ) {
|
|
|
|
$self->setSecurity($req);
|
|
|
|
return PE_MAILFIRSTACCESS if ( $req->method eq 'GET' );
|
|
|
|
return PE_MAILFORMEMPTY;
|
|
|
|
}
|
|
|
|
|
|
|
|
my $searchByMail = 1;
|
|
|
|
|
|
|
|
# OTHER FORMS
|
|
|
|
if ($mailToken) {
|
2019-04-05 20:03:37 +02:00
|
|
|
$self->logger->debug("Token given for password reset: $mailToken");
|
2019-01-16 23:00:38 +01:00
|
|
|
|
|
|
|
# Check if token is valid
|
2019-05-12 20:04:38 +02:00
|
|
|
my $mailSession =
|
|
|
|
$self->p->getApacheSession( $mailToken, kind => "TOKEN" );
|
2019-01-16 23:00:38 +01:00
|
|
|
unless ($mailSession) {
|
|
|
|
$self->userLogger->warn('Bad reset token');
|
|
|
|
return PE_BADMAILTOKEN;
|
|
|
|
}
|
|
|
|
|
|
|
|
$req->{user} = $mailSession->data->{user};
|
2019-02-05 23:12:17 +01:00
|
|
|
$req->data->{mailAddress} =
|
|
|
|
$mailSession->data->{ $self->conf->{mailSessionKey} };
|
2019-01-16 23:00:38 +01:00
|
|
|
$self->logger->debug( 'User associated to: ' . $req->{user} );
|
|
|
|
|
|
|
|
# Restore pdata if any
|
|
|
|
$req->pdata( $mailSession->data->{_pdata} || {} );
|
|
|
|
$searchByMail = 0 unless ( $req->{user} =~ /\@/ );
|
|
|
|
}
|
|
|
|
|
|
|
|
# Check for values posted
|
|
|
|
else {
|
|
|
|
|
|
|
|
# Use submitted value
|
|
|
|
$req->{user} = $req->param('mail');
|
|
|
|
|
|
|
|
# Check if token exists
|
|
|
|
my $token;
|
2019-04-05 20:03:37 +02:00
|
|
|
if ( $self->ottRule->( $req, {} ) or $self->captcha ) {
|
2019-01-16 23:00:38 +01:00
|
|
|
$token = $req->param('token');
|
|
|
|
unless ($token) {
|
|
|
|
$self->setSecurity($req);
|
|
|
|
$self->userLogger->warn('Reset try without token');
|
|
|
|
return PE_NOTOKEN;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
# Captcha for register form
|
|
|
|
if ( $self->captcha ) {
|
|
|
|
my $captcha = $req->param('captcha');
|
|
|
|
|
|
|
|
unless ($captcha) {
|
2019-02-05 23:12:17 +01:00
|
|
|
$self->userLogger->notice('Reset try with captcha not filled');
|
2019-01-16 23:00:38 +01:00
|
|
|
|
|
|
|
# Set captcha or token
|
|
|
|
$self->setSecurity($req);
|
|
|
|
return PE_CAPTCHAEMPTY;
|
|
|
|
}
|
|
|
|
|
|
|
|
# Check captcha
|
|
|
|
unless ( $self->captcha->validateCaptcha( $token, $captcha ) ) {
|
|
|
|
$self->userLogger->info('Captcha failed: wrong code');
|
|
|
|
|
|
|
|
# Set captcha or token
|
|
|
|
$self->setSecurity($req);
|
|
|
|
return PE_CAPTCHAERROR;
|
|
|
|
}
|
2019-04-05 10:37:48 +02:00
|
|
|
$self->logger->debug('Captcha code verified');
|
2019-01-16 23:00:38 +01:00
|
|
|
}
|
2019-04-05 20:03:37 +02:00
|
|
|
elsif ( $self->ottRule->( $req, {} ) ) {
|
2019-01-16 23:00:38 +01:00
|
|
|
unless ( $self->ott->getToken($token) ) {
|
|
|
|
$self->setSecurity($req);
|
|
|
|
$self->userLogger->warn('Reset try with expired/bad token');
|
|
|
|
return PE_TOKENEXPIRED;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
unless ( $req->{user} =~ /$self->{conf}->{userControl}/o ) {
|
|
|
|
$self->setSecurity($req);
|
|
|
|
return PE_MALFORMEDUSER;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
# Search user in database
|
2019-02-07 09:27:56 +01:00
|
|
|
$req->steps( [
|
2019-12-16 23:21:43 +01:00
|
|
|
'getUser', 'setSessionInfo',
|
|
|
|
$self->p->groupsAndMacros, 'setPersistentSessionInfo',
|
|
|
|
'setLocalGroups'
|
2019-01-16 23:00:38 +01:00
|
|
|
]
|
|
|
|
);
|
|
|
|
if ( my $error = $self->p->process( $req, useMail => $searchByMail ) ) {
|
|
|
|
if ( $error == PE_USERNOTFOUND or $error == PE_BADCREDENTIALS ) {
|
2020-08-28 21:53:19 +02:00
|
|
|
$self->userLogger->warn( 'Reset asked for an invalid user ('
|
2019-02-05 23:12:17 +01:00
|
|
|
. $req->param('mail')
|
2019-04-05 10:37:48 +02:00
|
|
|
. ')' );
|
2019-01-16 23:00:38 +01:00
|
|
|
|
|
|
|
# To avoid mail enumeration, return OK
|
|
|
|
# unless portalErrorOnMailNotFound is set
|
|
|
|
|
|
|
|
if ( $self->conf->{portalErrorOnMailNotFound} ) {
|
|
|
|
$self->setSecurity($req);
|
|
|
|
return PE_MAILNOTFOUND;
|
|
|
|
}
|
|
|
|
|
2019-02-05 23:12:17 +01:00
|
|
|
my $mailTimeout =
|
|
|
|
$self->conf->{mailTimeout} || $self->conf->{timeout};
|
2019-01-24 19:45:43 +01:00
|
|
|
my $expTimestamp = time() + $mailTimeout;
|
2019-02-05 23:12:17 +01:00
|
|
|
$req->data->{expMailDate} =
|
2019-04-05 10:37:48 +02:00
|
|
|
strftime( '%d/%m/%Y', localtime $expTimestamp );
|
2019-02-05 23:12:17 +01:00
|
|
|
$req->data->{expMailTime} =
|
2019-04-05 10:37:48 +02:00
|
|
|
strftime( '%H:%M', localtime $expTimestamp );
|
2019-01-16 23:00:38 +01:00
|
|
|
return PE_MAILCONFIRMOK;
|
|
|
|
}
|
|
|
|
return $error;
|
|
|
|
}
|
|
|
|
|
|
|
|
# Build temporary session
|
|
|
|
my $mailSession = $self->getMailSession( $req->{user} );
|
|
|
|
unless ( $mailSession or $mailToken ) {
|
|
|
|
|
|
|
|
# Create a new session
|
|
|
|
|
|
|
|
my $infos = {};
|
|
|
|
|
|
|
|
# Set _utime for session autoremove
|
|
|
|
# Use default session timeout and mail session timeout to compute it
|
|
|
|
my $time = time();
|
|
|
|
my $timeout = $self->conf->{timeout};
|
2019-01-24 19:45:43 +01:00
|
|
|
my $mailTimeout = $self->conf->{mailTimeout} || $timeout;
|
2019-01-16 23:00:38 +01:00
|
|
|
|
2019-01-24 19:45:43 +01:00
|
|
|
$infos->{_utime} = $time + ( $mailTimeout - $timeout );
|
2019-01-16 23:00:38 +01:00
|
|
|
|
|
|
|
# Store expiration timestamp for further use
|
2019-01-24 19:45:43 +01:00
|
|
|
$infos->{mailSessionTimeoutTimestamp} = $time + $mailTimeout;
|
2019-01-16 23:00:38 +01:00
|
|
|
|
|
|
|
# Store start timestamp for further use
|
|
|
|
$infos->{mailSessionStartTimestamp} = $time;
|
|
|
|
|
|
|
|
# Store mail
|
2019-02-05 23:12:17 +01:00
|
|
|
$infos->{ $self->conf->{mailSessionKey} } =
|
|
|
|
$self->p->getFirstValue(
|
2019-01-16 23:00:38 +01:00
|
|
|
$req->{sessionInfo}->{ $self->conf->{mailSessionKey} } );
|
|
|
|
|
|
|
|
# Store user
|
|
|
|
$infos->{user} = $req->{user};
|
|
|
|
|
|
|
|
# Store type
|
2019-04-05 10:37:48 +02:00
|
|
|
$infos->{_type} = 'mail';
|
2019-01-16 23:00:38 +01:00
|
|
|
|
|
|
|
# Store pdata
|
|
|
|
$infos->{_pdata} = $req->pdata;
|
|
|
|
|
|
|
|
# create session
|
2019-05-12 20:04:38 +02:00
|
|
|
$mailSession =
|
|
|
|
$self->p->getApacheSession( undef, kind => "TOKEN", info => $infos );
|
2019-01-16 23:00:38 +01:00
|
|
|
|
|
|
|
$req->id( $mailSession->id );
|
|
|
|
}
|
|
|
|
elsif ($mailSession) {
|
|
|
|
$self->logger->debug( 'Mail session found: ' . $mailSession->id );
|
|
|
|
$req->id( $mailSession->id );
|
|
|
|
$req->data->{mailAlreadySent} = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
# Send confirmation mail
|
|
|
|
unless ($mailToken) {
|
|
|
|
|
|
|
|
# Mail session expiration date
|
|
|
|
my $expTimestamp = $mailSession->data->{mailSessionTimeoutTimestamp};
|
|
|
|
|
|
|
|
$self->logger->debug("Mail expiration timestamp: $expTimestamp");
|
|
|
|
|
2019-02-05 23:12:17 +01:00
|
|
|
$req->data->{expMailDate} =
|
2019-04-05 10:37:48 +02:00
|
|
|
strftime( '%d/%m/%Y', localtime $expTimestamp );
|
2019-02-05 23:12:17 +01:00
|
|
|
$req->data->{expMailTime} =
|
2019-04-05 10:37:48 +02:00
|
|
|
strftime( '%H:%M', localtime $expTimestamp );
|
2019-01-16 23:00:38 +01:00
|
|
|
|
|
|
|
# Mail session start date
|
|
|
|
my $startTimestamp = $mailSession->data->{mailSessionStartTimestamp};
|
|
|
|
|
|
|
|
$self->logger->debug("Mail start timestamp: $startTimestamp");
|
2019-02-05 23:12:17 +01:00
|
|
|
$req->data->{startMailDate} =
|
2019-04-05 10:37:48 +02:00
|
|
|
strftime( '%d/%m/%Y', localtime $startTimestamp );
|
2019-02-05 23:12:17 +01:00
|
|
|
$req->data->{startMailTime} =
|
2019-04-05 10:37:48 +02:00
|
|
|
strftime( '%H:%M', localtime $startTimestamp );
|
2019-01-16 23:00:38 +01:00
|
|
|
|
|
|
|
# Ask if user wants an another confirmation email
|
|
|
|
if ( $req->data->{mailAlreadySent}
|
2019-04-05 10:37:48 +02:00
|
|
|
and not $req->param('resendconfirmation') )
|
2019-01-16 23:00:38 +01:00
|
|
|
{
|
|
|
|
$self->userLogger->notice(
|
|
|
|
'Reset mail already sent to ' . $req->{user} );
|
|
|
|
|
|
|
|
# Return mail already sent only if it is allowed at previous step
|
|
|
|
if ( $self->conf->{portalErrorOnMailNotFound} ) {
|
|
|
|
$self->setSecurity($req);
|
|
|
|
return PE_MAILCONFIRMATION_ALREADY_SENT;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
# Get mail address
|
2019-02-05 23:12:17 +01:00
|
|
|
$req->data->{mailAddress} ||=
|
|
|
|
$self->p->getFirstValue(
|
2019-01-16 23:00:38 +01:00
|
|
|
$req->{sessionInfo}->{ $self->conf->{mailSessionKey} } );
|
|
|
|
return PE_MAILERROR unless ( $req->data->{mailAddress} );
|
|
|
|
|
|
|
|
# Build confirmation url
|
|
|
|
my $req_url = $req->data->{_url};
|
|
|
|
my $skin = $self->p->getSkin($req);
|
2019-02-05 23:12:17 +01:00
|
|
|
my $url =
|
|
|
|
$self->conf->{mailUrl} . '?'
|
|
|
|
. build_urlencoded(
|
2019-01-16 23:00:38 +01:00
|
|
|
mail_token => $req->{id},
|
|
|
|
skin => $skin,
|
|
|
|
( $req_url ? ( url => $req_url ) : () ),
|
2019-02-05 23:12:17 +01:00
|
|
|
);
|
2019-01-16 23:00:38 +01:00
|
|
|
|
|
|
|
# Build mail content
|
|
|
|
$tplPrms{MAIN_LOGO} = $self->conf->{portalMainLogo};
|
|
|
|
my $tr = $self->translate($req);
|
2019-01-24 19:45:43 +01:00
|
|
|
my $subject = $self->conf->{mailConfirmSubject};
|
2019-01-16 23:00:38 +01:00
|
|
|
unless ($subject) {
|
2019-01-24 19:45:43 +01:00
|
|
|
$subject = 'mailConfirmSubject';
|
2019-01-16 23:00:38 +01:00
|
|
|
$tr->( \$subject );
|
|
|
|
}
|
|
|
|
my $body;
|
|
|
|
my $html;
|
2019-01-24 19:45:43 +01:00
|
|
|
if ( $self->conf->{mailConfirmBody} ) {
|
2019-01-16 23:00:38 +01:00
|
|
|
|
|
|
|
# We use a specific text message, no html
|
2019-01-30 12:06:05 +01:00
|
|
|
$body = $self->conf->{mailConfirmBody};
|
2019-01-16 23:00:38 +01:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
|
|
|
|
# Use HTML template
|
2019-08-22 15:41:51 +02:00
|
|
|
$body = $self->loadMailTemplate(
|
2019-06-28 13:40:56 +02:00
|
|
|
$req,
|
2019-01-16 23:00:38 +01:00
|
|
|
'mail_confirm',
|
|
|
|
filter => $tr,
|
|
|
|
params => \%tplPrms
|
|
|
|
);
|
|
|
|
$html = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
# Replace variables in body
|
|
|
|
$body =~ s/\$expMailDate/$req->data->{expMailDate}/ge;
|
|
|
|
$body =~ s/\$expMailTime/$req->data->{expMailTime}/ge;
|
|
|
|
$body =~ s/\$url/$url/g;
|
|
|
|
$body =~ s/\$(\w+)/$req->{sessionInfo}->{$1} || ''/ge;
|
|
|
|
|
2020-10-09 22:26:00 +02:00
|
|
|
$self->logger->info( "User "
|
|
|
|
. $req->data->{mailAddress}
|
|
|
|
. " is trying to reset his/her password" );
|
2020-07-28 15:04:55 +02:00
|
|
|
|
2019-01-16 23:00:38 +01:00
|
|
|
# Send mail
|
|
|
|
unless (
|
|
|
|
$self->send_mail(
|
|
|
|
$req->data->{mailAddress},
|
|
|
|
$subject, $body, $html
|
|
|
|
)
|
2019-02-05 23:12:17 +01:00
|
|
|
)
|
2019-01-16 23:00:38 +01:00
|
|
|
{
|
|
|
|
$self->logger->debug('Unable to send reset mail');
|
|
|
|
|
|
|
|
# Don't return an error here to avoid enumeration
|
|
|
|
}
|
|
|
|
return PE_MAILCONFIRMOK;
|
|
|
|
}
|
|
|
|
|
|
|
|
# User has a valid mailToken, allow to change password
|
|
|
|
# A token is required
|
|
|
|
$self->ott->setToken(
|
|
|
|
$req,
|
2019-02-05 23:12:17 +01:00
|
|
|
{
|
|
|
|
%{ $req->sessionInfo },
|
2019-01-16 23:00:38 +01:00
|
|
|
pwdAllowed => $self->conf->{passwordResetAllowedRetries}
|
|
|
|
}
|
|
|
|
);
|
|
|
|
return PE_PASSWORDFIRSTACCESS if ( $req->method eq 'GET' );
|
|
|
|
return PE_PASSWORDFORMEMPTY;
|
|
|
|
}
|
|
|
|
|
|
|
|
sub changePwd {
|
|
|
|
my ( $self, $req ) = @_;
|
|
|
|
my %tplPrms;
|
|
|
|
$self->logger->debug('Change password form response');
|
|
|
|
|
|
|
|
if ( my $token = $req->param('token') ) {
|
|
|
|
$req->sessionInfo( $self->ott->getToken($token) );
|
|
|
|
unless ( $req->sessionInfo ) {
|
|
|
|
$self->userLogger->warn(
|
|
|
|
'User tries to change password with an invalid or expired token'
|
|
|
|
);
|
|
|
|
return PE_NOTOKEN;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
# These 2 cases means that a user tries to change password without
|
|
|
|
# following valid links!!!
|
|
|
|
else {
|
2019-02-05 23:12:17 +01:00
|
|
|
$self->userLogger->error('User tries to change password without token');
|
2019-01-16 23:00:38 +01:00
|
|
|
return PE_NOTOKEN;
|
|
|
|
}
|
|
|
|
|
|
|
|
unless ( $req->sessionInfo->{pwdAllowed}-- ) {
|
|
|
|
$self->userLogger->error(
|
|
|
|
'User tries to use another token to change a password');
|
|
|
|
return PE_NOTOKEN;
|
|
|
|
}
|
|
|
|
|
2020-06-17 16:29:31 +02:00
|
|
|
# Remove the mail token session if mail token is provided
|
|
|
|
my $mailToken = $req->param('mail_token');
|
|
|
|
if ($mailToken) {
|
|
|
|
$self->logger->debug("Token given for password reset: $mailToken");
|
|
|
|
|
|
|
|
# Check if token is valid
|
|
|
|
my $mailSession =
|
|
|
|
$self->p->getApacheSession( $mailToken, kind => "TOKEN" );
|
|
|
|
unless ($mailSession) {
|
|
|
|
$self->userLogger->warn('Bad reset token');
|
|
|
|
return PE_BADMAILTOKEN;
|
|
|
|
}
|
|
|
|
|
|
|
|
$self->logger->debug("Delete token $mailToken");
|
|
|
|
$mailSession->remove;
|
|
|
|
}
|
|
|
|
|
2019-01-16 23:00:38 +01:00
|
|
|
# Check if user wants to generate the new password
|
|
|
|
if ( $req->param('reset') ) {
|
|
|
|
$self->logger->debug(
|
2019-04-05 20:03:37 +02:00
|
|
|
"Reset password request for $req->{sessionInfo}->{_user}");
|
2019-01-16 23:00:38 +01:00
|
|
|
|
|
|
|
# Generate a complex password
|
2019-02-05 23:12:17 +01:00
|
|
|
my $password =
|
|
|
|
$self->gen_password( $self->conf->{randomPasswordRegexp} );
|
2019-04-05 20:03:37 +02:00
|
|
|
$self->logger->debug("Generated password: $password");
|
2019-01-16 23:00:38 +01:00
|
|
|
$req->data->{newpassword} = $password;
|
|
|
|
$req->data->{confirmpassword} = $password;
|
|
|
|
$req->data->{forceReset} = 1;
|
|
|
|
$tplPrms{RESET} = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
# Else a password is required in request
|
|
|
|
else {
|
|
|
|
$req->data->{newpassword} = $req->param('newpassword');
|
|
|
|
$req->data->{confirmpassword} = $req->param('confirmpassword');
|
|
|
|
unless ($req->data->{newpassword}
|
|
|
|
and $req->data->{confirmpassword}
|
|
|
|
and $req->data->{newpassword} eq $req->data->{confirmpassword} )
|
|
|
|
{
|
|
|
|
$self->ott->setToken( $req, $req->sessionInfo );
|
|
|
|
( $req->data->{newpassword} && $req->data->{confirmpassword} )
|
2019-02-05 23:12:17 +01:00
|
|
|
? return PE_PASSWORD_MISMATCH
|
|
|
|
: return PE_PASSWORDFORMEMPTY;
|
2019-01-16 23:00:38 +01:00
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-05 12:02:51 +02:00
|
|
|
# Check password quality
|
2019-09-05 13:30:49 +02:00
|
|
|
require Lemonldap::NG::Portal::Password::Base;
|
|
|
|
my $cpq =
|
|
|
|
$self->Lemonldap::NG::Portal::Password::Base::checkPasswordQuality(
|
|
|
|
$req->data->{newpassword} );
|
2019-09-12 17:54:43 +02:00
|
|
|
unless ( $cpq == PE_OK ) {
|
|
|
|
$self->ott->setToken( $req, $req->sessionInfo );
|
|
|
|
return $cpq;
|
|
|
|
}
|
2019-09-05 12:02:51 +02:00
|
|
|
|
2019-01-16 23:00:38 +01:00
|
|
|
# Modify the password TODO: change this
|
|
|
|
# Populate $req->{user} for logging purpose
|
|
|
|
my $tmp = $self->conf->{portalRequireOldPassword};
|
|
|
|
$self->conf->{portalRequireOldPassword} = 0;
|
|
|
|
$req->user( $req->{sessionInfo}->{_user} );
|
2019-02-05 23:12:17 +01:00
|
|
|
my $result =
|
|
|
|
$self->p->_passwordDB->modifyPassword( $req,
|
2019-01-16 23:00:38 +01:00
|
|
|
$req->data->{newpassword}, 1 );
|
|
|
|
$req->{user} = undef;
|
|
|
|
$self->conf->{portalRequireOldPassword} = $tmp;
|
|
|
|
|
|
|
|
# Mail token can be used only one time, delete the session if all is ok
|
2019-09-12 17:54:43 +02:00
|
|
|
unless ( $result == PE_PASSWORD_OK or $result == PE_OK ) {
|
|
|
|
$self->ott->setToken( $req, $req->sessionInfo );
|
|
|
|
return $result;
|
|
|
|
}
|
2019-01-16 23:00:38 +01:00
|
|
|
|
2020-11-29 18:02:13 +01:00
|
|
|
my $userlog = $req->sessionInfo->{ $self->conf->{whatToTrace} };
|
|
|
|
my $iplog = $req->sessionInfo->{ipAddr};
|
|
|
|
$self->userLogger->notice("Password changed for $userlog ($iplog)")
|
|
|
|
if ( defined $userlog and $iplog );
|
|
|
|
|
2019-01-16 23:00:38 +01:00
|
|
|
# Send mail containing the new password
|
2019-02-05 23:12:17 +01:00
|
|
|
$req->data->{mailAddress} ||=
|
|
|
|
$self->p->getFirstValue(
|
2019-01-16 23:00:38 +01:00
|
|
|
$req->{sessionInfo}->{ $self->conf->{mailSessionKey} } );
|
|
|
|
|
|
|
|
# Build mail content
|
|
|
|
$tplPrms{MAIN_LOGO} = $self->conf->{portalMainLogo};
|
|
|
|
my $tr = $self->translate($req);
|
2019-01-24 19:45:43 +01:00
|
|
|
my $subject = $self->conf->{mailSubject};
|
2019-01-16 23:00:38 +01:00
|
|
|
unless ($subject) {
|
2019-01-24 19:45:43 +01:00
|
|
|
$subject = 'mailSubject';
|
2019-01-16 23:00:38 +01:00
|
|
|
$tr->( \$subject );
|
|
|
|
}
|
|
|
|
my $body;
|
|
|
|
my $html;
|
2019-01-24 19:45:43 +01:00
|
|
|
if ( $self->conf->{mailBody} ) {
|
2019-01-16 23:00:38 +01:00
|
|
|
|
|
|
|
# We use a specific text message, no html
|
2019-01-30 12:06:05 +01:00
|
|
|
$body = $self->conf->{mailBody};
|
2019-01-16 23:00:38 +01:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
|
|
|
|
# Use HTML template
|
2019-08-22 15:41:51 +02:00
|
|
|
$body = $self->loadMailTemplate(
|
2019-06-28 13:40:56 +02:00
|
|
|
$req,
|
2019-01-16 23:00:38 +01:00
|
|
|
'mail_password',
|
|
|
|
filter => $tr,
|
|
|
|
params => \%tplPrms
|
|
|
|
);
|
|
|
|
$html = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
# Replace variables in body
|
|
|
|
my $password = $req->data->{newpassword};
|
|
|
|
$body =~ s/\$password/$password/g;
|
|
|
|
$body =~ s/\$(\w+)/$req->{sessionInfo}->{$1} || ''/ge;
|
|
|
|
|
|
|
|
# Send mail
|
|
|
|
return PE_MAILERROR
|
2019-02-05 23:12:17 +01:00
|
|
|
unless $self->send_mail( $req->data->{mailAddress}, $subject, $body,
|
2019-01-16 23:00:38 +01:00
|
|
|
$html );
|
|
|
|
|
2019-04-05 10:37:48 +02:00
|
|
|
return PE_MAILOK;
|
2019-01-16 23:00:38 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
sub setSecurity {
|
|
|
|
my ( $self, $req ) = @_;
|
|
|
|
if ( $self->captcha ) {
|
|
|
|
$self->captcha->setCaptcha($req);
|
|
|
|
}
|
2019-04-05 20:03:37 +02:00
|
|
|
elsif ( $self->ottRule->( $req, {} ) ) {
|
2019-01-16 23:00:38 +01:00
|
|
|
$self->ott->setToken($req);
|
|
|
|
}
|
2019-04-05 10:37:48 +02:00
|
|
|
return 1;
|
2019-01-16 23:00:38 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
sub display {
|
|
|
|
my ( $self, $req ) = @_;
|
2020-04-03 00:21:15 +02:00
|
|
|
my $speChars = $self->conf->{passwordPolicySpecialChar};
|
|
|
|
$speChars =~ s/\s+/ /g;
|
|
|
|
$speChars =~ s/(?:^\s|\s$)//g;
|
2019-01-16 23:00:38 +01:00
|
|
|
$self->logger->debug( 'Display called with code: ' . $req->error );
|
2020-05-24 00:04:33 +02:00
|
|
|
|
2019-01-16 23:00:38 +01:00
|
|
|
my %tplPrm = (
|
|
|
|
SKIN_PATH => $self->conf->{staticPrefix},
|
|
|
|
SKIN => $self->p->getSkin($req),
|
|
|
|
SKIN_BG => $self->conf->{portalSkinBackground},
|
|
|
|
MAIN_LOGO => $self->conf->{portalMainLogo},
|
|
|
|
AUTH_ERROR => $req->error,
|
|
|
|
AUTH_ERROR_TYPE => $req->error_type,
|
|
|
|
AUTH_URL => $req->data->{_url},
|
|
|
|
CHOICE_VALUE => $req->{_authChoice},
|
|
|
|
EXPMAILDATE => $req->data->{expMailDate},
|
|
|
|
EXPMAILTIME => $req->data->{expMailTime},
|
|
|
|
STARTMAILDATE => $req->data->{startMailDate},
|
|
|
|
STARTMAILTIME => $req->data->{startMailTime},
|
|
|
|
MAILALREADYSENT => $req->data->{mailAlreadySent},
|
|
|
|
MAIL => (
|
2020-04-02 13:30:22 +02:00
|
|
|
$self->p->checkXSSAttack( 'mail', $req->{user} ) ? ''
|
2019-01-16 23:00:38 +01:00
|
|
|
: $req->{user}
|
|
|
|
),
|
|
|
|
DISPLAY_FORM => 0,
|
|
|
|
DISPLAY_RESEND_FORM => 0,
|
|
|
|
DISPLAY_CONFIRMMAILSENT => 0,
|
|
|
|
DISPLAY_MAILSENT => 0,
|
|
|
|
DISPLAY_PASSWORD_FORM => 0,
|
2019-09-05 13:02:51 +02:00
|
|
|
DISPLAY_PPOLICY => $self->conf->{portalDisplayPasswordPolicy},
|
2019-09-05 12:46:32 +02:00
|
|
|
PPOLICY_MINSIZE => $self->conf->{passwordPolicyMinSize},
|
|
|
|
PPOLICY_MINLOWER => $self->conf->{passwordPolicyMinLower},
|
|
|
|
PPOLICY_MINUPPER => $self->conf->{passwordPolicyMinUpper},
|
|
|
|
PPOLICY_MINDIGIT => $self->conf->{passwordPolicyMinDigit},
|
2020-04-03 00:21:15 +02:00
|
|
|
PPOLICY_ALLOWEDSPECHAR => $speChars,
|
2020-04-02 13:30:22 +02:00
|
|
|
(
|
2020-04-03 00:21:15 +02:00
|
|
|
$speChars
|
2020-04-02 13:30:22 +02:00
|
|
|
? ( PPOLICY_MINSPECHAR => $self->conf->{passwordPolicyMinSpeChar} )
|
|
|
|
: ()
|
|
|
|
),
|
2019-09-12 18:26:56 +02:00
|
|
|
DISPLAY_GENERATE_PASSWORD =>
|
|
|
|
$self->conf->{portalDisplayGeneratePassword},
|
2019-01-16 23:00:38 +01:00
|
|
|
);
|
|
|
|
if ( $req->data->{mailToken}
|
2019-04-05 20:03:37 +02:00
|
|
|
and
|
|
|
|
not $self->p->checkXSSAttack( 'mail_token', $req->data->{mailToken} ) )
|
2019-01-16 23:00:38 +01:00
|
|
|
{
|
|
|
|
$tplPrm{MAIL_TOKEN} = $req->data->{mailToken};
|
|
|
|
}
|
|
|
|
|
|
|
|
# Display captcha if it's enabled
|
|
|
|
if ( $req->captcha ) {
|
|
|
|
$tplPrm{CAPTCHA_SRC} = $req->captcha;
|
|
|
|
$tplPrm{CAPTCHA_SIZE} = $self->conf->{captcha_size};
|
|
|
|
}
|
|
|
|
if ( $req->token ) {
|
|
|
|
$tplPrm{TOKEN} = $req->token;
|
|
|
|
}
|
|
|
|
|
|
|
|
# Display form the first time
|
2019-02-07 09:27:56 +01:00
|
|
|
if ( (
|
2019-02-05 23:12:17 +01:00
|
|
|
$req->error == PE_MAILFORMEMPTY
|
2019-01-16 23:00:38 +01:00
|
|
|
or $req->error == PE_MAILFIRSTACCESS
|
|
|
|
or $req->error == PE_MAILNOTFOUND
|
|
|
|
or $req->error == PE_CAPTCHAERROR
|
|
|
|
or $req->error == PE_CAPTCHAEMPTY
|
|
|
|
)
|
2019-04-05 10:37:48 +02:00
|
|
|
and not $req->data->{mailToken}
|
2019-02-05 23:12:17 +01:00
|
|
|
)
|
2019-01-16 23:00:38 +01:00
|
|
|
{
|
|
|
|
$self->logger->debug('Display form');
|
|
|
|
$tplPrm{DISPLAY_FORM} = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
# Display mail confirmation resent form
|
|
|
|
elsif ( $req->error == PE_MAILCONFIRMATION_ALREADY_SENT ) {
|
|
|
|
$self->logger->debug('Display resend form');
|
|
|
|
$tplPrm{DISPLAY_RESEND_FORM} = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
# Display confirmation mail sent
|
|
|
|
elsif ( $req->error == PE_MAILCONFIRMOK ) {
|
|
|
|
$self->logger->debug('Display "confirm mail sent"');
|
|
|
|
$tplPrm{DISPLAY_CONFIRMMAILSENT} = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
# Display mail sent
|
|
|
|
elsif ( $req->error == PE_MAILOK ) {
|
|
|
|
$self->logger->debug('Display "mail sent"');
|
|
|
|
$tplPrm{DISPLAY_MAILSENT} = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
# Display password change form
|
|
|
|
elsif ( $req->data->{mailToken}
|
|
|
|
and $req->error != PE_MAILERROR
|
|
|
|
and $req->error != PE_BADMAILTOKEN
|
|
|
|
and $req->error != PE_MAILOK )
|
|
|
|
{
|
|
|
|
$self->logger->debug('Display password form');
|
|
|
|
$tplPrm{DISPLAY_PASSWORD_FORM} = 1;
|
|
|
|
}
|
|
|
|
|
2019-09-12 17:54:43 +02:00
|
|
|
# Display password change form again
|
|
|
|
# - if passwords mismatch
|
|
|
|
# - if password quality check fail
|
2019-01-16 23:00:38 +01:00
|
|
|
elsif ($req->error == PE_PASSWORDFORMEMPTY
|
2019-09-12 17:54:43 +02:00
|
|
|
|| $req->error == PE_PASSWORD_MISMATCH
|
|
|
|
|| $req->error == PE_PP_INSUFFICIENT_PASSWORD_QUALITY
|
|
|
|
|| $req->error == PE_PP_PASSWORD_TOO_SHORT
|
|
|
|
|| $req->error == PE_PP_PASSWORD_TOO_YOUNG
|
|
|
|
|| $req->error == PE_PP_PASSWORD_IN_HISTORY )
|
2019-01-16 23:00:38 +01:00
|
|
|
{
|
|
|
|
$self->logger->debug('Display password form');
|
|
|
|
$tplPrm{DISPLAY_PASSWORD_FORM} = $req->sessionInfo->{pwdAllowed};
|
|
|
|
}
|
|
|
|
|
|
|
|
return 'mail', \%tplPrm;
|
|
|
|
}
|
|
|
|
|
|
|
|
1;
|