[LEMONLDAP-217] add a captcha feature to the portal
This commit is contained in:
parent
208b9f0f07
commit
5264202231
3
Makefile
3
Makefile
|
@ -317,6 +317,9 @@ install_manager_site: install_conf_dir
|
||||||
|
|
||||||
install_portal_site: install_conf_dir
|
install_portal_site: install_conf_dir
|
||||||
# Portal install
|
# Portal install
|
||||||
|
@mkdir -p $(PORTALDIR)/captcha_output/
|
||||||
|
@mkdir -p $(DATADIR)/captcha/data
|
||||||
|
@chmod -R 777 $(DATADIR)/captcha $(PORTALDIR)/captcha_output/
|
||||||
@install -v -d $(RPORTALDIR) $(RPORTALSKINSDIR) \
|
@install -v -d $(RPORTALDIR) $(RPORTALSKINSDIR) \
|
||||||
$(RPORTALDIR)/skins/ \
|
$(RPORTALDIR)/skins/ \
|
||||||
$(RCRONDIR) $(RCONFDIR)
|
$(RCRONDIR) $(RCONFDIR)
|
||||||
|
|
|
@ -93,6 +93,8 @@ sub portalTab {
|
||||||
73 => 'PORTAL_RADIUSCONNECTFAILED',
|
73 => 'PORTAL_RADIUSCONNECTFAILED',
|
||||||
74 => 'PORTAL_MUST_SUPPLY_OLD_PASSWORD',
|
74 => 'PORTAL_MUST_SUPPLY_OLD_PASSWORD',
|
||||||
75 => 'PORTAL_FORBIDDENIP',
|
75 => 'PORTAL_FORBIDDENIP',
|
||||||
|
76 => 'PORTAL_CAPTCHAERROR',
|
||||||
|
77 => 'PORTAL_CAPTCHAEMPTY',
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -25,7 +25,8 @@ WriteMakefile(
|
||||||
},
|
},
|
||||||
PREREQ_PM => {
|
PREREQ_PM => {
|
||||||
'Apache::Session' => 0,
|
'Apache::Session' => 0,
|
||||||
'CGI' => 3.08,
|
'Authen::Captcha' => 0,
|
||||||
|
'CGI' => 3.08,
|
||||||
'File::Basename' => 0,
|
'File::Basename' => 0,
|
||||||
'HTML::Template' => 0,
|
'HTML::Template' => 0,
|
||||||
'Lemonldap::NG::Common' => '1.2.0',
|
'Lemonldap::NG::Common' => '1.2.0',
|
||||||
|
|
|
@ -65,7 +65,16 @@ if (
|
||||||
DISPLAY_MAILSENT => 0,
|
DISPLAY_MAILSENT => 0,
|
||||||
DISPLAY_PASSWORD_FORM => 0,
|
DISPLAY_PASSWORD_FORM => 0,
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
# Display captcha if it's enabled
|
||||||
|
if ( $portal->{captcha_enabled} ) {
|
||||||
|
$template->param(
|
||||||
|
CAPTCHA_IMG => $portal->{captcha_img},
|
||||||
|
CAPTCHA_CODE => $portal->{captcha_img},
|
||||||
|
CAPTCHA_SIZE => $portal->{captcha_size}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
# Display mail confirmation resent form
|
# Display mail confirmation resent form
|
||||||
if ( $portal->{error} == PE_MAILCONFIRMATION_ALREADY_SENT ) {
|
if ( $portal->{error} == PE_MAILCONFIRMATION_ALREADY_SENT ) {
|
||||||
|
|
|
@ -20,6 +20,12 @@
|
||||||
<tr><th><lang en="Mail" fr="Adresse mail"/></th>
|
<tr><th><lang en="Mail" fr="Adresse mail"/></th>
|
||||||
<td><input name="mail" type="text" value="<TMPL_VAR NAME="MAIL">"/></td>
|
<td><input name="mail" type="text" value="<TMPL_VAR NAME="MAIL">"/></td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
|
<img src="<TMPL_VAR NAME=CAPTCHA_IMG>" />
|
||||||
|
<lang en="Enter the captcha: " fr="Entrez le captcha: " />
|
||||||
|
<input type="text" NAME="captcha_user_code" size="<TMPL_VAR NAME=CAPTCHA_SIZE>" />
|
||||||
|
<input type="hidden" NAME="captcha_code" value="<TMPL_VAR NAME=CAPTCHA_CODE>" />
|
||||||
|
|
||||||
<tr><td colspan="2">
|
<tr><td colspan="2">
|
||||||
<div class="buttons">
|
<div class="buttons">
|
||||||
<button type="submit" class="positive">
|
<button type="submit" class="positive">
|
||||||
|
|
|
@ -203,6 +203,16 @@ sub display {
|
||||||
LOGIN_INFO => $self->loginInfo(),
|
LOGIN_INFO => $self->loginInfo(),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
# Display captcha if it's enabled
|
||||||
|
if ( $self->{captcha_enabled} ) {
|
||||||
|
%templateParams = (
|
||||||
|
%templateParams,
|
||||||
|
CAPTCHA_IMG => $self->{captcha_img},
|
||||||
|
CAPTCHA_CODE => $self->{captcha_code},
|
||||||
|
CAPTCHA_SIZE => $self->{captcha_size}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
# Show password form if password policy error
|
# Show password form if password policy error
|
||||||
if (
|
if (
|
||||||
|
|
||||||
|
|
|
@ -29,6 +29,8 @@ use POSIX;
|
||||||
# - extractMailInfo
|
# - extractMailInfo
|
||||||
# - getMailUser
|
# - getMailUser
|
||||||
# - storeMailSession
|
# - storeMailSession
|
||||||
|
# - initCaptcha
|
||||||
|
# - checkCaptcha
|
||||||
# - sendConfirmationMail
|
# - sendConfirmationMail
|
||||||
# - changePassword
|
# - changePassword
|
||||||
# - sendPasswordMail
|
# - sendPasswordMail
|
||||||
|
@ -50,15 +52,17 @@ sub process {
|
||||||
|
|
||||||
$self->{error} = $self->_subProcess(
|
$self->{error} = $self->_subProcess(
|
||||||
qw(smtpInit userDBInit passwordDBInit extractMailInfo
|
qw(smtpInit userDBInit passwordDBInit extractMailInfo
|
||||||
getMailUser setSessionInfo setMacros setGroups
|
getMailUser initCaptcha checkCaptcha setSessionInfo
|
||||||
setPersistentSessionInfo setLocalGroups storeMailSession
|
setMacros setGroups setPersistentSessionInfo setLocalGroups
|
||||||
sendConfirmationMail changePassword sendPasswordMail)
|
storeMailSession sendConfirmationMail changePassword sendPasswordMail)
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
(
|
(
|
||||||
$self->{error} <= 0
|
$self->{error} <= 0
|
||||||
or $self->{error} == PE_PASSWORD_OK
|
or $self->{error} == PE_PASSWORD_OK
|
||||||
|
or $self->{error} == PE_CAPTCHAERROR
|
||||||
|
or $self->{error} == PE_CAPTCHAEMPTY
|
||||||
or $self->{error} == PE_MAILCONFIRMOK
|
or $self->{error} == PE_MAILCONFIRMOK
|
||||||
or $self->{error} == PE_MAILOK
|
or $self->{error} == PE_MAILOK
|
||||||
) ? 0 : 1
|
) ? 0 : 1
|
||||||
|
|
|
@ -12,6 +12,7 @@ use Exporter 'import';
|
||||||
|
|
||||||
use warnings;
|
use warnings;
|
||||||
use MIME::Base64;
|
use MIME::Base64;
|
||||||
|
use Authen::Captcha;
|
||||||
use Lemonldap::NG::Common::CGI;
|
use Lemonldap::NG::Common::CGI;
|
||||||
use CGI::Cookie;
|
use CGI::Cookie;
|
||||||
require POSIX;
|
require POSIX;
|
||||||
|
@ -145,6 +146,8 @@ use constant {
|
||||||
PE_RADIUSCONNECTFAILED => 73,
|
PE_RADIUSCONNECTFAILED => 73,
|
||||||
PE_MUST_SUPPLY_OLD_PASSWORD => 74,
|
PE_MUST_SUPPLY_OLD_PASSWORD => 74,
|
||||||
PE_FORBIDDENIP => 75,
|
PE_FORBIDDENIP => 75,
|
||||||
|
PE_CAPTCHAERROR => 76,
|
||||||
|
PE_CAPTCHAEMPTY => 77,
|
||||||
|
|
||||||
# Portal messages
|
# Portal messages
|
||||||
PM_USER => 0,
|
PM_USER => 0,
|
||||||
|
@ -193,7 +196,7 @@ our @EXPORT = qw( PE_IMG_NOK PE_IMG_OK PE_INFO PE_REDIRECT PE_DONE PE_OK
|
||||||
PE_MISSINGREQATTR PE_BADPARTNER PE_MAILCONFIRMATION_ALREADY_SENT
|
PE_MISSINGREQATTR PE_BADPARTNER PE_MAILCONFIRMATION_ALREADY_SENT
|
||||||
PE_PASSWORDFORMEMPTY PE_CAS_SERVICE_NOT_ALLOWED PE_MAILFIRSTACCESS
|
PE_PASSWORDFORMEMPTY PE_CAS_SERVICE_NOT_ALLOWED PE_MAILFIRSTACCESS
|
||||||
PE_MAILNOTFOUND PE_PASSWORDFIRSTACCESS PE_MAILCONFIRMOK
|
PE_MAILNOTFOUND PE_PASSWORDFIRSTACCESS PE_MAILCONFIRMOK
|
||||||
PE_MUST_SUPPLY_OLD_PASSWORD PE_FORBIDDENIP
|
PE_MUST_SUPPLY_OLD_PASSWORD PE_FORBIDDENIP PE_CAPTCHAERROR PE_CAPTCHAEMPTY
|
||||||
PM_USER PM_DATE PM_IP PM_SESSIONS_DELETED PM_OTHER_SESSIONS
|
PM_USER PM_DATE PM_IP PM_SESSIONS_DELETED PM_OTHER_SESSIONS
|
||||||
PM_REMOVE_OTHER_SESSIONS PM_PP_GRACE PM_PP_EXP_WARNING
|
PM_REMOVE_OTHER_SESSIONS PM_PP_GRACE PM_PP_EXP_WARNING
|
||||||
PM_SAML_IDPSELECT PM_SAML_IDPCHOOSEN PM_REMEMBERCHOICE PM_SAML_SPLOGOUT
|
PM_SAML_IDPSELECT PM_SAML_IDPCHOOSEN PM_REMEMBERCHOICE PM_SAML_SPLOGOUT
|
||||||
|
@ -459,7 +462,13 @@ sub new {
|
||||||
'(' . join( '|', split( /\s+/, $self->{trustedDomains} ) ) . ')';
|
'(' . join( '|', split( /\s+/, $self->{trustedDomains} ) ) . ')';
|
||||||
$self->{trustedDomains} =~ s/\./\\./g;
|
$self->{trustedDomains} =~ s/\./\\./g;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# init the captcha feature if it's enabled
|
||||||
|
if ( $self->{captcha_enabled} ) {
|
||||||
|
eval $self->initCaptcha();
|
||||||
|
$self->{captcha_initialized} = 1 unless $@ ;
|
||||||
|
}
|
||||||
|
|
||||||
return $self;
|
return $self;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -636,6 +645,12 @@ sub setDefaultValues {
|
||||||
$self->{ldapPasswordResetAttribute} ||= "pwdReset";
|
$self->{ldapPasswordResetAttribute} ||= "pwdReset";
|
||||||
$self->{ldapPasswordResetAttributeValue} ||= "TRUE";
|
$self->{ldapPasswordResetAttributeValue} ||= "TRUE";
|
||||||
$self->{mailOnPasswordChange} ||= 0;
|
$self->{mailOnPasswordChange} ||= 0;
|
||||||
|
|
||||||
|
# Captcha parameters
|
||||||
|
$self->{captcha_enabled} = 0;
|
||||||
|
$self->{captcha_size} = 6;
|
||||||
|
$self->{captcha_output} = '/usr/local/lemonldap-ng/htdocs/portal/captcha_output/';
|
||||||
|
$self->{captcha_data} = '/usr/local/lemonldap-ng/data/captcha/data/';
|
||||||
|
|
||||||
# Notification
|
# Notification
|
||||||
$self->{notificationWildcard} ||= "allusers";
|
$self->{notificationWildcard} ||= "allusers";
|
||||||
|
@ -745,6 +760,22 @@ sub buildHiddenForm {
|
||||||
return $val;
|
return $val;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
## @method void initCaptcha()
|
||||||
|
# init captcha module and generate captcha
|
||||||
|
sub initCaptcha {
|
||||||
|
my $self = shift;
|
||||||
|
opendir(OUTPUT, $self->{captcha_output}) or $self->lmLog("Can't open captcha output dir", "error");
|
||||||
|
opendir(DATA, $self->{captcha_data}) or $self->lmLog("Can't open captcha data dir", "error");
|
||||||
|
foreach(readdir(OUTPUT)) {
|
||||||
|
system("rm -f $_ &>/dev/null")
|
||||||
|
or $self->lmLog("Can't clean captcha output dir!", "warn");
|
||||||
|
}
|
||||||
|
$self->{captcha} = Authen::Captcha->new(data_folder => $self->{captcha_data}, output_folder => $self->{captcha_output});
|
||||||
|
$self->{captcha_code} = $self->{captcha}->generate_code($self->{captcha_size});
|
||||||
|
$self->{captcha_img} = "/captcha_output/" . $self->{captcha_code} . ".png";
|
||||||
|
closedir(DATA) and closedir(OUTPUT);
|
||||||
|
}
|
||||||
|
|
||||||
## @method boolean isTrustedUrl(string url)
|
## @method boolean isTrustedUrl(string url)
|
||||||
# Check if an URL's domain name is declared in LL::NG config or is declared as trusted domain
|
# Check if an URL's domain name is declared in LL::NG config or is declared as trusted domain
|
||||||
# @param url Parameter url
|
# @param url Parameter url
|
||||||
|
|
|
@ -47,6 +47,29 @@ sub extractFormInfo {
|
||||||
&& ( $self->{confirmpassword} = $self->param('confirmpassword') ) );
|
&& ( $self->{confirmpassword} = $self->param('confirmpassword') ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ( $self->{captcha_enabled} ) {
|
||||||
|
my $captcha_user_code;
|
||||||
|
if ( $self->param('captcha_user_code') && $self->param('captcha_code') ) {
|
||||||
|
$captcha_user_code = $self->param('captcha_user_code');
|
||||||
|
$self->{captcha_code} = $self->param('captcha_code');
|
||||||
|
}
|
||||||
|
$self->{captcha_result} = $self->checkCaptcha($captcha_user_code, $self->{captcha_code});
|
||||||
|
}
|
||||||
|
if ( $self->{captcha_result} != 1 ) {
|
||||||
|
if ( $self->{captcha_result} == -3 or $self->{captcha_result} == -2 ) {
|
||||||
|
$self->lmLog("Captcha failed: wrong code", "error");
|
||||||
|
return PE_CAPTCHAERROR;
|
||||||
|
}
|
||||||
|
elsif ( $self->{captcha_result} == 0 ) {
|
||||||
|
$self->lmLog("Captcha failed: code not checked (file error)", "error");
|
||||||
|
return PE_CAPTCHAERROR;
|
||||||
|
}
|
||||||
|
elsif ( $self->{captcha_result} == -1 ) {
|
||||||
|
$self->lmLog("Captcha failed: code has expired", "error");
|
||||||
|
return PE_CAPTCHAERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
# Other parameters
|
# Other parameters
|
||||||
$self->{timezone} = $self->param('timezone');
|
$self->{timezone} = $self->param('timezone');
|
||||||
$self->{userControl} ||= '^[\w\.\-@]+$';
|
$self->{userControl} ||= '^[\w\.\-@]+$';
|
||||||
|
@ -88,4 +111,18 @@ sub setAuthSessionInfo {
|
||||||
PE_OK;
|
PE_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
## @method int checkCaptcha(code, ccode)
|
||||||
|
# Check captcha auth
|
||||||
|
# @return a constant
|
||||||
|
# @param code that user enter in the form
|
||||||
|
# @param captcha code generated by Authen::Captcha
|
||||||
|
sub checkCaptcha {
|
||||||
|
my ($self, $code, $ccode) = splice @_ ;
|
||||||
|
opendir(OUTPUT, $self->{captcha_output}) or $self->lmLog("Can't open captcha output dir", "error");
|
||||||
|
opendir(DATA, $self->{captcha_data}) or $self->lmLog("Can't open captcha data dir", "error");
|
||||||
|
$self->{captcha_result} = $self->{captcha}->check_code($code, $ccode);
|
||||||
|
closedir(OUTPUT) && closedir(DATA);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
1;
|
1;
|
||||||
|
|
|
@ -206,6 +206,8 @@ sub error_fr {
|
||||||
'La connexion au serveur Radius a échoué',
|
'La connexion au serveur Radius a échoué',
|
||||||
"L'ancien mot de passe est obligatoire",
|
"L'ancien mot de passe est obligatoire",
|
||||||
'Vous venez d\'une adresse IP qui n\'est pas accréditée',
|
'Vous venez d\'une adresse IP qui n\'est pas accréditée',
|
||||||
|
'Mauvais code',
|
||||||
|
'Vous devez entrez le captcha'
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -290,6 +292,8 @@ sub error_en {
|
||||||
'Radius connection has failed',
|
'Radius connection has failed',
|
||||||
'Old password is required',
|
'Old password is required',
|
||||||
'You came from an unaccredited IP address',
|
'You came from an unaccredited IP address',
|
||||||
|
'Wrong code',
|
||||||
|
'You have to tape the captcha'
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -374,7 +378,8 @@ sub error_ro {
|
||||||
'Un e-mail a fost trimis',
|
'Un e-mail a fost trimis',
|
||||||
'Radius connection has failed',
|
'Radius connection has failed',
|
||||||
'Old password is required',
|
'Old password is required',
|
||||||
'You came from an unaccredited IP address',
|
'You came from an unaccredited IP address','Bad cod',
|
||||||
|
'trebuie să introduceţi CAPTCHA'
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user