package LIMS::Controller::User;
use strict;
use base 'LIMS::Base';
use Moose;
with (
'LIMS::Controller::Roles::User', # generate_new_password
);
use LIMS::Local::Utils;
#-------------------------------------------------------------------------------
# called by 'Forgotten password' link on login page
sub password_change : Runmode {
my $self = shift; $self->_debug_path($self->get_current_runmode);
# get email address from query or return to login:
my $email = $self->query->param('email_address')
|| $self->redirect( $self->query->url . '/login' ); # $self->debug($email);
# args for User::get_user_details method:
my %args_to_get = ( col => 'email', value => $email );
# get user details from users table using email address from query:
my $user = $self->stash->{user_details}
= $self->model('User')->get_user_details(\%args_to_get); # $self->debug($user);
# if email address not recognised, set message in flash & redirect to login:
if (! $user) {
$self->flash( email_address => $email, 'login' ); # use 3rd arg to 'scope'
return $self->redirect( $self->query->url );
}
# create new 10-digit randomised letter/number password:
my $new_password = $self->stash->{new_password}
= $self->generate_new_password; # $self->debug($new_password);
# encrypt new_password using SHA1 (or MD5):
my $sha1 = LIMS::Local::Utils::sha1_digest($new_password); # $self->debug('sha1:'.$digest);
# args for User::update_password() method:
my %args_to_update = (
id => $user->id,
pwd => $sha1,
);
# execute update_password method:
my $rtn = $self->model('User')->update_password(\%args_to_update);
return $self->error($rtn)if $rtn;
# send new password details to registered email address:
$self->_email_password;
return $self->tt_process;
}
#-------------------------------------------------------------------------------
# Resources / Change password, or redirect from password_reset using forgotten password email link:
sub change_password : Runmode {
my $self = shift; $self->_debug_path($self->get_current_runmode);
# get js validation foo_onsubmit & foo_dfv_js vars into tt_params:
$self->js_validation_profile('js_change_pwd');
return $self->tt_process;
}
#-------------------------------------------------------------------------------
sub delete_message : Runmode {
my $self = shift; $self->_debug_path($self->get_current_runmode);
my $msg_id = $self->param('id');
if ($msg_id) {
my $rtn = $self->model('User')->delete_user_message($msg_id);
if ($rtn) {
$self->flash( error => $rtn );
}
else {
$self->flash( info => $self->messages('user')->{msg_deleted} );
}
}
return $self->redirect( $self->query->url . '/resources/user_messages' );
}
#-------------------------------------------------------------------------------
sub acknowledge_messages : Runmode {
my $self = shift;
my @message_ids = $self->query->param('msg_id');
my $rtn = $self->model('User')->acknowledge_messages(\@message_ids);
return $rtn
? $self->error($rtn)
: $self->redirect( $self->query->url . '/resources/user_messages' );
}
#-------------------------------------------------------------------------------
# submission of change password form
sub do_change_password : Runmode {
my $self = shift; $self->_debug_path($self->get_current_runmode);
my $profile = $self->validate('change_password'); # $self->debug($profile);
my $result = $self->form_validator($profile);
return $self->forward('change_password') if $result->has_error;
my $vars = $result->valid; # $self->debug($vars);
my $session_data = $self->session->dataref->{UserProfile};
# get current pwd from profile:
my $profile_pwd = $session_data->{password}; # $self->debug($profile_pwd);
# SHA1 digest param old_password:
my $param_password = LIMS::Local::Utils::sha1_digest($vars->{old_password});
# $self->debug($param_password);
# check param old_password matches users' profile pwd:
unless ( $param_password eq $profile_pwd ) {
$self->flash( error => $self->messages('user')->{old_pwd_mismatch} );
return $self->redirect( $self->query->url . '/user/change_password' );
}
my $new_pwd = LIMS::Local::Utils::sha1_digest($vars->{new_password});
# args for User::update_password() method:
my %args_to_update = (
id => $session_data->{id},
pwd => $new_pwd,
);
# execute update_password method:
my $rtn = $self->model('User')->update_password(\%args_to_update);
return $self->error($rtn) if $rtn;
# clear logged-in status to force re-login:
$self->authen->logout;
$self->flash( info => $self->messages('user')->{pwd_change_success} );
return $self->redirect( $self->query->url );
}
#-------------------------------------------------------------------------------
# from hello page:
sub change_email : Runmode {
my $self = shift; $self->_debug_path($self->get_current_runmode);
my $errs = shift;
# get js validation foo_onsubmit & foo_dfv_js vars into tt_params:
$self->js_validation_profile('change_user_email');
return $self->tt_process($errs);
}
#-------------------------------------------------------------------------------
sub do_change_email : Runmode {
my $self = shift; $self->_debug_path($self->get_current_runmode);
# retrieve user_id or complain:
my $user_id = $self->param('id')
or return $self->error( 'no id passed to ' . $self->get_current_runmode );
my $profile = $self->validate('change_user_email');
my $dfv = $self->check_rm( 'change_email', $profile )
|| return $self->dfv_error_page;
my $data = $dfv->valid;
my $user = $self->model('User')->get_user_profile($user_id);
$data->{user_profile} = $user; # add to $data for model
{ # check pwd OK:
my $encrypted_pwd = LIMS::Local::Utils::sha1_digest($data->{password});
unless ( $user->password eq $encrypted_pwd ) {
$self->flash( error => $self->messages('user')->{pwd_incorrect} );
return $self->forward('change_email');
}
}
my $rtn = $self->model('User')->update_email($data); # empty if success
if ($rtn) { return $self->error($rtn) }
# have to manually update email param in session:
my $user_profile = $self->session->param('UserProfile');
$user_profile->{email} = $data->{email};
# re-save it:
$self->session->param('UserProfile', $user_profile);
$self->flash( info => $self->messages('action')->{edit_success} );
return $self->redirect( $self->query->url . '/user/change_email' );
}
#-------------------------------------------------------------------------------
sub change_location : Runmode {
my $self = shift; $self->_debug_path($self->get_current_runmode);
my $errs = shift;
my $user_locations
= $self->model('User')->get_user_locations({sort_by => 'location_name'});
$self->tt_params( user_locations => $user_locations );
# create locations map for template:
my %locations_map = map { $_->id => $_->location_name } @$user_locations;
$self->tt_params( locations_map => \%locations_map );
$self->js_validation_profile('change_user_location');
return $self->tt_process($errs);
}
#-------------------------------------------------------------------------------
sub do_change_location : Runmode {
my $self = shift; $self->_debug_path($self->get_current_runmode);
# retrieve user_id or complain:
my $user_id = $self->param('id')
or return $self->error( 'no id passed to ' . $self->get_current_runmode );
my $profile = $self->validate('change_user_location');
my $dfv = $self->check_rm( 'change_location', $profile )
|| return $self->dfv_error_page;
my $data = $dfv->valid;
my $user = $self->model('User')->get_user_profile($user_id);
$data->{user_profile} = $user; # add to $data for model
{ # check pwd OK:
my $encrypted_pwd = LIMS::Local::Utils::sha1_digest($data->{password});
unless ( $user->password eq $encrypted_pwd ) {
$self->flash( error => $self->messages('user')->{pwd_incorrect} );
return $self->forward('change_location');
}
}
my $rtn = $self->model('User')->update_location($data); # empty if success
if ($rtn) { return $self->error($rtn) }
# have to manually update user_location_id param in session:
my $user_profile = $self->session->param('UserProfile');
$user_profile->{user_location_id} = $data->{location_id};
# re-save it:
$self->session->param('UserProfile', $user_profile);
$self->flash( info => $self->messages('action')->{edit_success} );
return $self->redirect( $self->query->url . '/user/change_location' );
}
#-------------------------------------------------------------------------------
# called by forgotten password email link
sub password_reset : Runmode {
my $self = shift; $self->_debug_path($self->get_current_runmode);
# from email-supplied user/password_reset/foo:
my $new_pwd_id = $self->param('id')
or die 'no id passed to password_reset'; # $self->debug($new_pwd_id);
# retrieve new_passwd_data param from session:
my $new_passwd_data = $self->session->param('new_passwd_data'); # $self->debug($new_passwd_data);
my @params = qw( user passwd destination );
# make sure unique id supplied in email matches identifier in session param:
# probably doesn't matter unless more than one pwd reset in a session
unless ($new_pwd_id eq $new_passwd_data->{id}) {
$self->flash( info => $self->messages('login')->{session_id_not_found} );
# redirect to login to display flash message:
return $self->redirect( $self->query->url );
}
# create a redirect url from session new_passwd_data params:
my $url =
sprintf $self->query->url . '/login?authen_username=%s;authen_password=%s;destination=%s',
@{$new_passwd_data}{@params};
# redirect to automatic login, destination = change_passwd:
return $self->redirect( $url );
}
#-------------------------------------------------------------------------------
sub _email_password {
my $self = shift; $self->_debug_path();
# get user & pwd from stashed password_change():
my $user = $self->stash->{user_details};
my $pwd = $self->stash->{new_password};
# create a unique identifier for email; later check for match in session data:
my $unique_identifier = unpack "H*", pack "NS", time, $$;
# hash for session param 'new_passwd_data':
my %data = (
id => $unique_identifier,
destination => $self->query->url . '/user/change_password/' . $pwd,
user => $user->username,
passwd => $pwd,
);
# save %data to session using new_passwd_data param:
$self->session->param( new_passwd_data => \%data );
# link for e-mail to take user to User::password_reset method - contains
# session unique identifier so username, new passwd & destination can be
# retrieved from session storage:
my $link = $self->query->url
. '/user/password_reset/'
. $unique_identifier;
my $message = qq!\tUSERNAME: $data{user}\n\tPASSWORD: $data{passwd}\n\nLink: $link!;
my %mail_data = (
recipient => $user->email,
config => $self->cfg('settings'),
subject => 'new password',
message => $message,
); # $self->debug(\%data);
my $rtn = $self->model('Email')->send_message(\%mail_data);
# TODO: handle smtp timeout to not die
return $self->error($rtn) if $rtn;
}
1;