[LEMONLDAP-217] add a captcha feature to the portal

This commit is contained in:
Sandro Cazzaniga 2012-07-04 12:33:03 +00:00
parent 208b9f0f07
commit 5264202231
10 changed files with 116 additions and 8 deletions

View File

@ -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)

View File

@ -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',
}; };
} }

View File

@ -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',

View File

@ -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 ) {

View File

@ -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">

View File

@ -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 (

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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'
]; ];
} }