lemonldap-ng/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Plugins/Register.pm

503 lines
16 KiB
Perl
Raw Normal View History

2017-01-03 23:06:14 +01:00
package Lemonldap::NG::Portal::Plugins::Register;
use strict;
2017-01-14 20:31:48 +01:00
use Encode;
2017-02-17 21:47:01 +01:00
use HTML::Template;
2017-01-03 23:06:14 +01:00
use Mouse;
2017-01-14 20:31:48 +01:00
use POSIX qw(strftime);
2017-01-03 23:06:14 +01:00
use Lemonldap::NG::Portal::Main::Constants qw(
PE_BADMAILTOKEN
PE_CAPTCHAEMPTY
PE_CAPTCHAERROR
PE_MALFORMEDUSER
2017-01-14 09:17:45 +01:00
PE_MAILCONFIRMATION_ALREADY_SENT
PE_MAILCONFIRMOK
PE_MAILERROR
PE_MAILOK
2017-01-26 22:42:42 +01:00
PE_NOTOKEN
2017-01-03 23:06:14 +01:00
PE_OK
PE_REGISTERALREADYEXISTS
PE_REGISTERFIRSTACCESS
PE_REGISTERFORMEMPTY
2017-01-26 22:42:42 +01:00
PE_TOKENEXPIRED
2017-01-03 23:06:14 +01:00
);
our $VERSION = '2.0.0';
extends 'Lemonldap::NG::Portal::Main::Plugin',
'Lemonldap::NG::Portal::Lib::SMTP';
2017-01-26 22:42:40 +01:00
# PROPERTIES
2017-01-26 22:42:42 +01:00
# Sub module (Demo, LDAP,...)
2017-01-26 22:42:40 +01:00
has registerModule => ( is => 'rw' );
2017-01-26 22:42:42 +01:00
# Register url to set in the mail
2017-01-26 22:42:40 +01:00
has registerUrl => (
is => 'rw',
default => sub {
my $p = $_[0]->conf->{portal};
$p =~ s#/*$##;
return "$p/register";
}
);
2017-01-26 22:42:42 +01:00
# Mail timeout token generator
has mailott => (
2017-01-23 12:28:13 +01:00
is => 'rw',
default => sub {
my $ott =
$_[0]->{p}->loadModule('Lemonldap::NG::Portal::Lib::OneTimeToken');
2017-01-28 13:58:22 +01:00
$ott->timeout( $_[0]->conf->{registerTimeout}
|| $_[0]->conf->{timeout} );
return $ott;
2017-01-23 12:28:13 +01:00
}
);
2017-01-26 22:42:42 +01:00
# Form timout token generator (used if requireToken is set)
has ott => ( is => 'rw' );
# Captcha generator
2017-01-26 22:42:40 +01:00
has captcha => ( is => 'rw' );
2017-01-03 23:06:14 +01:00
# INITIALIZATION
sub init {
my ($self) = @_;
2017-01-26 22:42:40 +01:00
# Declare REST route
2017-01-14 09:17:45 +01:00
$self->addUnauthRoute( register => 'register', [ 'POST', 'GET' ] );
2017-01-26 22:42:40 +01:00
# Initialize Captcha if needed
2017-01-03 23:06:14 +01:00
if ( $self->conf->{captcha_register_enabled} ) {
2017-01-26 22:42:40 +01:00
$self->captcha( $self->p->loadModule('::Lib::Captcha') ) or return 0;
2017-01-26 22:42:42 +01:00
}
2017-01-03 23:06:14 +01:00
2017-01-26 22:42:42 +01:00
# Initialize form token if needed (captcha provides also a token)
elsif ( $self->conf->{requireToken} ) {
$_[0]->ott(
$_[0]->p->loadModule('Lemonldap::NG::Portal::Lib::OneTimeToken') )
or return 0;
$_[0]->ott->timeout( $_[0]->conf->{formTimeout} );
2017-01-03 23:06:14 +01:00
}
2017-01-26 22:42:40 +01:00
2017-01-26 22:42:42 +01:00
# Load registered module
2017-01-14 09:17:45 +01:00
$self->registerModule(
$self->p->loadPlugin( '::Register::' . $self->conf->{registerDB} ) )
or return 0;
2017-01-03 23:06:14 +01:00
return 1;
}
# RUNNIG METHODS
2017-01-27 06:51:19 +01:00
# Handle register requests
2017-01-03 23:06:14 +01:00
sub register {
my ( $self, $req ) = @_;
2017-01-26 22:42:42 +01:00
# Check parameters
2017-01-03 23:06:14 +01:00
$req->error( $self->_register($req) );
2017-01-26 22:42:42 +01:00
# Display form
2017-01-03 23:06:14 +01:00
my ( $tpl, $prms ) = $self->display($req);
return $self->p->sendHtml( $req, $tpl, params => $prms );
}
2017-01-26 22:42:42 +01:00
# Parameters check
2017-01-03 23:06:14 +01:00
sub _register {
my ( $self, $req ) = @_;
2017-01-26 22:42:42 +01:00
# Check if it's a first access
2017-01-03 23:06:14 +01:00
unless ( $req->param('mail') || $req->param('register_token') ) {
2017-01-26 22:42:42 +01:00
# Set captcha or token
$self->setSecurity($req);
2017-02-15 07:41:50 +01:00
$self->logger->debug('First access to register form');
2017-01-28 13:58:22 +01:00
return PE_REGISTERFIRSTACCESS if ( $req->method eq 'GET' );
2017-01-03 23:06:14 +01:00
return PE_REGISTERFORMEMPTY;
}
2017-01-26 22:42:42 +01:00
# Get register token (mail link)
2017-01-03 23:06:14 +01:00
$req->datas->{register_token} = $req->param('register_token');
# If a register token is present, find the corresponding info
if ( $req->datas->{register_token} ) {
2017-02-15 07:41:50 +01:00
$self->logger->debug(
"Token given for register: " . $req->datas->{register_token} );
2017-01-03 23:06:14 +01:00
# Get the corresponding session
2017-01-26 22:42:42 +01:00
if ( my $datas =
$self->mailott->getToken( $req->datas->{register_token} ) )
2017-01-23 12:28:13 +01:00
{
2017-01-03 23:06:14 +01:00
foreach (qw(mail firstname lastname ipAddr)) {
2017-01-23 12:28:13 +01:00
$req->datas->{registerInfo}->{$_} = $datas->{$_};
2017-01-03 23:06:14 +01:00
}
2017-02-15 07:41:50 +01:00
$self->logger->debug( "User associated to token: "
. $req->datas->{registerInfo}->{mail} );
2017-01-03 23:06:14 +01:00
}
2017-01-23 12:28:13 +01:00
else {
return PE_BADMAILTOKEN;
}
2017-01-03 23:06:14 +01:00
}
2017-01-26 22:42:42 +01:00
# Case else: user tries to register
2017-01-03 23:06:14 +01:00
else {
# Use submitted value
$req->datas->{registerInfo}->{mail} = $req->param('mail');
$req->datas->{registerInfo}->{firstname} = $req->param('firstname');
$req->datas->{registerInfo}->{lastname} = $req->param('lastname');
$req->datas->{registerInfo}->{ipAddr} = $req->address;
2017-01-03 23:06:14 +01:00
2017-01-26 22:42:42 +01:00
# Check captcha/token only if register session does not already exist
if ( $req->datas->{registerInfo}->{mail}
and
!$self->getRegisterSession( $req->datas->{registerInfo}->{mail} ) )
2017-01-03 23:06:14 +01:00
{
2017-01-26 22:42:42 +01:00
# Check if token exists
my $token;
if ( $self->ott or $self->captcha ) {
$token = $req->param('token');
unless ($token) {
$self->setSecurity($req);
$self->userLogger->warn('Register try without token');
2017-01-26 22:42:42 +01:00
return PE_NOTOKEN;
}
2017-01-03 23:06:14 +01:00
}
2017-01-26 22:42:42 +01:00
# Captcha for register form
if ( $self->captcha ) {
my $captcha = $req->param('captcha');
unless ($captcha) {
$self->userLogger->warn(
2017-01-26 22:42:42 +01:00
'Register try with captcha not filled');
# Set captcha or token
$self->setSecurity($req);
return PE_CAPTCHAEMPTY;
}
# Check captcha
2017-01-27 06:51:19 +01:00
unless ( $self->captcha->validateCaptcha( $token, $captcha ) ) {
$self->userLogger->info('Captcha failed: wrong code');
2017-01-26 22:42:42 +01:00
# Set captcha or token
$self->setSecurity($req);
return PE_CAPTCHAERROR;
}
2017-02-15 07:41:50 +01:00
$self->logger->debug("Captcha code verified");
2017-01-26 22:42:42 +01:00
}
elsif ( $self->ott ) {
unless ( $self->ott->getToken($token) ) {
$self->setSecurity($req);
2017-02-17 21:47:01 +01:00
$self->userLogger->notice(
'Register try with expired/bad token');
2017-01-26 22:42:42 +01:00
return PE_TOKENEXPIRED;
}
2017-01-03 23:06:14 +01:00
}
}
}
# Check mail
return PE_MALFORMEDUSER
unless ( $req->datas->{registerInfo}->{mail} =~
2017-01-26 22:42:42 +01:00
m/$self->{conf}->{userControl}/o );
2017-01-03 23:06:14 +01:00
# Search for user using UserDB module
# If the user already exists, register is forbidden
$req->datas->{mail} = $req->{registerInfo}->{mail};
if ( $self->p->_userDB->getUser($req) == PE_OK ) {
2017-02-15 07:41:50 +01:00
$self->userLogger->error(
"Register: refuse mail $req->{mail} because already exists in UserDB"
2017-01-03 23:06:14 +01:00
);
return PE_REGISTERALREADYEXISTS;
}
2017-01-14 20:31:48 +01:00
my $register_session =
$self->getRegisterSession( $req->datas->{registerInfo}->{mail} );
$req->datas->{mail_already_sent} =
( $register_session and !$req->id ) ? 1 : 0;
2017-01-03 23:06:14 +01:00
# Skip this step if confirmation was already sent
2017-01-14 20:31:48 +01:00
unless ( $req->datas->{register_token} or $register_session ) {
2017-01-03 23:06:14 +01:00
2017-01-26 22:42:42 +01:00
# Create mail token
$register_session = $self->mailott->createToken(
2017-01-23 12:28:13 +01:00
{
mail => $req->datas->{registerInfo}->{mail},
firstname => $req->datas->{registerInfo}->{firstname},
lastname => $req->datas->{registerInfo}->{lastname},
ipAddr => $req->datas->{registerInfo}->{ipAddr},
_type => 'register',
}
);
2017-01-03 23:06:14 +01:00
}
# Send confirmation mail
# Skip this step if user clicked on the confirmation link
unless ( $req->datas->{register_token} ) {
# Check if confirmation mail has already been sent
2017-02-15 07:41:50 +01:00
$self->logger->debug('No register_token');
2017-01-03 23:06:14 +01:00
# Read session to get creation and expiration dates
$req->id($register_session) unless $req->id;
2017-02-15 07:41:50 +01:00
$self->logger->debug("Register session found: $register_session");
2017-01-03 23:06:14 +01:00
2017-01-24 06:10:57 +01:00
my $registerSessionObj = $self->p->getApacheSession($register_session);
2017-01-03 23:06:14 +01:00
# Mail session expiration date
my $expTimestamp =
2017-01-23 12:28:13 +01:00
$self->{conf}->{registerTimeout} || $self->conf->{timeout} + time;
2017-01-03 23:06:14 +01:00
2017-02-15 07:41:50 +01:00
$self->logger->debug("Register expiration timestamp: $expTimestamp");
2017-01-03 23:06:14 +01:00
$req->datas->{expMailDate} =
strftime( "%d/%m/%Y", localtime $expTimestamp );
$req->datas->{expMailTime} =
strftime( "%H:%M", localtime $expTimestamp );
# Mail session start date
2017-01-23 12:28:13 +01:00
my $startTimestamp = time;
2017-01-03 23:06:14 +01:00
2017-02-15 07:41:50 +01:00
$self->logger->debug("Register start timestamp: $startTimestamp");
2017-01-03 23:06:14 +01:00
$req->datas->{startMailDate} =
strftime( "%d/%m/%Y", localtime $startTimestamp );
$req->datas->{startMailTime} =
strftime( "%H:%M", localtime $startTimestamp );
# Ask if user want another confirmation email
if ( $req->datas->{mail_already_sent}
2017-01-14 20:31:48 +01:00
and !$req->param('resendconfirmation') )
2017-01-03 23:06:14 +01:00
{
return PE_MAILCONFIRMATION_ALREADY_SENT;
}
# Build confirmation url
2017-01-14 09:17:45 +01:00
my $url = $self->registerUrl . "?register_token=" . $req->{id};
2017-01-14 20:31:48 +01:00
$url .= '&skin=' . $self->p->getSkin($req);
2017-01-03 23:06:14 +01:00
$url .= '&'
. $self->conf->{authChoiceParam} . '='
. $req->datas->{_authChoice}
if ( $req->datas->{_authChoice} );
# Build mail content
my $subject = $self->conf->{registerConfirmSubject};
my $body;
my $html = 1;
# Use HTML template
my $tplfile =
$self->conf->{templateDir} . '/'
. $self->conf->{portalSkin}
. '/mail_register_confirm.tpl';
$tplfile =
$self->conf->{templateDir} . '/common/mail_register_confirm.tpl'
unless ( -e $tplfile );
2017-02-17 21:47:01 +01:00
my $template = HTML::Template->new(
filename => $tplfile,
filter => $self->translate($req),
);
2017-01-03 23:06:14 +01:00
$body = $template->output();
# Replace variables in body
$body =~ s/\$expMailDate/$req->datas->{expMailDate}/g;
$body =~ s/\$expMailTime/$req->datas->{expMailTime}/g;
$body =~ s/\$url/$url/g;
$body =~ s/\$(\w+)/decode("utf8",$req->datas->{registerInfo}->{$1})/ge;
# Send mail
return PE_MAILERROR
unless $self->send_mail( $req->datas->{registerInfo}->{mail},
$subject, $body, $html );
2017-02-15 07:41:50 +01:00
$self->logger->debug('Register message sent');
2017-01-14 09:17:45 +01:00
return PE_MAILCONFIRMOK;
2017-01-03 23:06:14 +01:00
}
# Generate a complex password
my $password = $self->gen_password( $self->conf->{randomPasswordRegexp} );
2017-02-15 07:41:50 +01:00
$self->logger->debug( "Generated password: " . $password );
2017-01-03 23:06:14 +01:00
$req->datas->{registerInfo}->{password} = $password;
$req->datas->{forceReset} = 1;
# Find a login
2017-01-14 09:17:45 +01:00
my $result = $self->registerModule->computeLogin($req);
2017-01-03 23:06:14 +01:00
unless ( $result == PE_OK ) {
2017-02-15 07:41:50 +01:00
$self->logger->error( "Could not compute login for "
. $req->datas->{registerInfo}->{mail} );
2017-01-03 23:06:14 +01:00
return $result;
}
# Create user
2017-02-15 07:41:50 +01:00
$self->logger->debug(
'Create new user ' . $req->datas->{registerInfo}->{login} );
2017-01-03 23:06:14 +01:00
$result = $self->registerModule->createUser($req);
unless ( $result == PE_OK ) {
2017-02-15 07:41:50 +01:00
$self->logger->error(
"Could not create user " . $req->datas->{registerInfo}->{login} );
2017-01-03 23:06:14 +01:00
return $result;
}
my $subject = $self->conf->{registerDoneSubject};
my $body;
my $html = 1;
# Use HTML template
my $tplfile =
$self->conf->{templateDir} . '/'
. $self->conf->{portalSkin}
. "/mail_register_done.tpl";
$tplfile = $self->conf->{templateDir} . "/common/mail_register_done.tpl"
unless ( -e $tplfile );
2017-02-17 21:47:01 +01:00
my $template = HTML::Template->new(
filename => $tplfile,
filter => $self->translate($req),
);
2017-01-03 23:06:14 +01:00
$body = $template->output();
# Replace variables in body
2017-01-14 20:31:48 +01:00
$body =~ s/\$(\w+)/decode("utf8",$req->datas->{registerInfo}->{$1})/ge;
2017-01-03 23:06:14 +01:00
# Send mail
return PE_MAILERROR
2017-01-14 20:31:48 +01:00
unless $self->send_mail( $req->datas->{registerInfo}->{mail},
$subject, $body, $html );
2017-01-03 23:06:14 +01:00
return PE_MAILOK;
}
sub display {
my ( $self, $req ) = @_;
my %templateParams = (
SKIN_PATH => '/static',
SKIN => $self->conf->{portalSkin},
SKIN_BG => $self->conf->{portalSkinBackground},
AUTH_ERROR => $req->error,
AUTH_ERROR_TYPE => $req->error_type,
CHOICE_PARAM => $self->conf->{authChoiceParam},
CHOICE_VALUE => $req->datas->{_authChoice},
EXPMAILDATE => $req->datas->{expMailDate},
EXPMAILTIME => $req->datas->{expMailTime},
STARTMAILDATE => $req->datas->{startMailDate},
STARTMAILTIME => $req->datas->{startMailTime},
MAILALREADYSENT => $req->datas->{mail_already_sent},
MAIL => $self->p->checkXSSAttack( 'mail',
$req->datas->{registerInfo}->{mail} ) ? ""
: $req->datas->{registerInfo}->{mail},
FIRSTNAME => $self->p->checkXSSAttack( 'firstname',
$req->datas->{registerInfo}->{firstname} ) ? ""
: $req->datas->{registerInfo}->{firstname},
2017-01-14 20:31:48 +01:00
LASTNAME => $self->p->checkXSSAttack( 'lastname',
2017-01-03 23:06:14 +01:00
$req->datas->{registerInfo}->{lastname} ) ? ""
: $req->datas->{registerInfo}->{lastname},
2017-01-14 20:31:48 +01:00
REGISTER_TOKEN => $self->p->checkXSSAttack( 'register_token',
2017-01-03 23:06:14 +01:00
$req->datas->{register_token} ) ? ""
: $req->datas->{register_token},
);
# Display form the first time
if (
(
$req->error == PE_REGISTERFORMEMPTY
or $req->error == PE_REGISTERFIRSTACCESS
or $req->error == PE_REGISTERALREADYEXISTS
or $req->error == PE_CAPTCHAERROR
or $req->error == PE_CAPTCHAEMPTY
2017-01-26 22:42:42 +01:00
or $req->error == PE_NOTOKEN
or $req->error == PE_TOKENEXPIRED
2017-01-03 23:06:14 +01:00
)
and !$req->param('mail_token')
)
{
%templateParams = (
%templateParams,
DISPLAY_FORM => 1,
DISPLAY_RESEND_FORM => 0,
DISPLAY_CONFIRMMAILSENT => 0,
DISPLAY_MAILSENT => 0,
DISPLAY_PASSWORD_FORM => 0,
);
}
# Display captcha if it's enabled
2017-01-26 22:42:42 +01:00
if ( $req->captcha ) {
$templateParams{CAPTCHA_SRC} = $req->captcha;
$templateParams{CAPTCHA_SIZE} = $self->conf->{captcha_size} || 6;
}
if ( $req->token ) {
$templateParams{TOKEN} = $req->token;
2017-01-03 23:06:14 +01:00
}
# Display mail confirmation resent form
if ( $req->{error} == PE_MAILCONFIRMATION_ALREADY_SENT ) {
%templateParams = (
%templateParams,
DISPLAY_FORM => 0,
DISPLAY_RESEND_FORM => 1,
DISPLAY_CONFIRMMAILSENT => 0,
DISPLAY_MAILSENT => 0,
DISPLAY_PASSWORD_FORM => 0,
);
}
# Display confirmation mail sent
if ( $req->{error} == PE_MAILCONFIRMOK ) {
%templateParams = (
%templateParams,
DISPLAY_FORM => 0,
DISPLAY_RESEND_FORM => 0,
DISPLAY_CONFIRMMAILSENT => 1,
DISPLAY_MAILSENT => 0,
DISPLAY_PASSWORD_FORM => 0,
);
}
# Display mail sent
if ( $req->{error} == PE_MAILOK ) {
%templateParams = (
%templateParams,
DISPLAY_FORM => 0,
DISPLAY_RESEND_FORM => 0,
DISPLAY_CONFIRMMAILSENT => 0,
DISPLAY_MAILSENT => 1,
DISPLAY_PASSWORD_FORM => 0,
);
}
# Display password change form
if ( $req->param('mail_token')
and $req->{error} != PE_MAILERROR
and $req->{error} != PE_BADMAILTOKEN
and $req->{error} != PE_MAILOK )
{
%templateParams = (
%templateParams,
DISPLAY_FORM => 0,
DISPLAY_RESEND_FORM => 0,
DISPLAY_CONFIRMMAILSENT => 0,
DISPLAY_MAILSENT => 0,
DISPLAY_PASSWORD_FORM => 1,
);
}
return ( 'register', \%templateParams );
}
2017-01-26 22:42:42 +01:00
sub setSecurity {
my ( $self, $req ) = @_;
if ( $self->captcha ) {
$self->captcha->setCaptcha($req);
}
elsif ( $self->ott ) {
$self->ott->setToken($req);
}
}
2017-01-03 23:06:14 +01:00
1;