Merge branch 'fix-captcha-api-2692' into 'v2.0'
New Captcha API See merge request lemonldap-ng/lemonldap-ng!262
This commit is contained in:
commit
77557c246d
|
@ -31,3 +31,18 @@ Go in ``General parameters`` > ``Portal`` > ``Captcha``:
|
|||
- **Activation in register form**: set to 1 to display captcha in
|
||||
register form
|
||||
- **Size**: length of captcha
|
||||
- **Captcha module**: allows you to use a custom Captcha module, see
|
||||
:ref:`below <customcaptcha>`. Leave it blank to use the default Captcha
|
||||
implementation
|
||||
- **Captcha module options**: options for the custom Captcha module
|
||||
|
||||
.. _customcaptcha:
|
||||
|
||||
Custom Captcha modules
|
||||
----------------------
|
||||
|
||||
.. versionadded:: 2.0.15
|
||||
|
||||
If the default Captcha does not meet your requirements, you can replace it with
|
||||
a different implementation. See the ``Lemonldap::NG::Portal::Captcha`` manual
|
||||
page for details on how to implement a Captcha module.
|
||||
|
|
|
@ -26,6 +26,26 @@ Known regressions in the latest released version
|
|||
|
||||
None
|
||||
|
||||
2.0.15
|
||||
------
|
||||
|
||||
New Captcha API
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
It is now possible to create your own Captcha modules to replace the one provided by default.
|
||||
|
||||
In order for custom Captcha modules to work, you need to modify your custom ``standardform.tpl``, ``mail.tpl`` and ``register.tpl`` template files:
|
||||
|
||||
.. code:: diff
|
||||
|
||||
- <TMPL_IF NAME=CAPTCHA_SRC>
|
||||
- <TMPL_INCLUDE NAME="captcha.tpl">
|
||||
+ <TMPL_IF NAME=CAPTCHA_HTML>
|
||||
+ <TMPL_VAR NAME=CAPTCHA_HTML>
|
||||
</TMPL_IF>
|
||||
|
||||
If you are using the default templates from the ``bootstrap`` theme, you don't need to change anything.
|
||||
|
||||
2.0.14
|
||||
------
|
||||
|
||||
|
|
|
@ -29,7 +29,7 @@ use constant DEFAULTCONFBACKEND => "File";
|
|||
use constant DEFAULTCONFBACKENDOPTIONS => (
|
||||
dirName => '/usr/local/lemonldap-ng/data/conf',
|
||||
);
|
||||
our $hashParameters = qr/^(?:(?:l(?:o(?:ca(?:lSessionStorageOption|tionRule)|goutService)|dapExportedVar|wp(?:Ssl)?Opt)|(?:(?:d(?:emo|bi)|webID)ExportedVa|exported(?:Heade|Va)|issuerDBGetParamete)r|f(?:indUser(?:Exclud|Search)ingAttribute|acebookExportedVar)|re(?:moteGlobalStorageOption|st2f(?:Verify|Init)Arg|loadUrl)|g(?:r(?:antSessionRule|oup)|lobalStorageOption)|n(?:otificationStorageOption|ginxCustomHandler)|macro)s|o(?:idc(?:S(?:ervice(?:DynamicRegistrationEx(?:portedVar|traClaim)s|MetaDataAuthnContext)|torageOptions)|RPMetaData(?:(?:Option(?:sExtraClaim)?|ExportedVar|ScopeRule|Macro)s|Node)|OPMetaData(?:(?:ExportedVar|Option)s|J(?:SON|WKS)|Node))|penIdExportedVars)|c(?:as(?:A(?:ppMetaData(?:(?:ExportedVar|Option|Macro)s|Node)|ttributes)|S(?:rvMetaData(?:(?:ExportedVar|Option)s|Node)|torageOptions))|(?:ustom(?:Plugins|Add)Param|heckUserHiddenHeader|ombModule)s)|s(?:aml(?:S(?:PMetaData(?:(?:ExportedAttribute|Option|Macro)s|Node|XML)|torageOptions)|IDPMetaData(?:(?:ExportedAttribute|Option)s|Node|XML))|essionDataToRemember|laveExportedVars|fExtra)|a(?:(?:daptativeAuthenticationLevelR|ut(?:hChoiceMod|oSigninR))ules|pplicationList)|p(?:ersistentStorageOptions|o(?:rtalSkinRules|st))|v(?:hostOptions|irtualHost)|S(?:MTPTLSOpts|SLVarIf))$/;
|
||||
our $hashParameters = qr/^(?:(?:l(?:o(?:ca(?:lSessionStorageOption|tionRule)|goutService)|dapExportedVar|wp(?:Ssl)?Opt)|(?:(?:d(?:emo|bi)|webID)ExportedVa|exported(?:Heade|Va)|issuerDBGetParamete)r|f(?:indUser(?:Exclud|Search)ingAttribute|acebookExportedVar)|re(?:moteGlobalStorageOption|st2f(?:Verify|Init)Arg|loadUrl)|g(?:r(?:antSessionRule|oup)|lobalStorageOption)|n(?:otificationStorageOption|ginxCustomHandler)|macro)s|o(?:idc(?:S(?:ervice(?:DynamicRegistrationEx(?:portedVar|traClaim)s|MetaDataAuthnContext)|torageOptions)|RPMetaData(?:(?:Option(?:sExtraClaim)?|ExportedVar|ScopeRule|Macro)s|Node)|OPMetaData(?:(?:ExportedVar|Option)s|J(?:SON|WKS)|Node))|penIdExportedVars)|c(?:a(?:s(?:A(?:ppMetaData(?:(?:ExportedVar|Option|Macro)s|Node)|ttributes)|S(?:rvMetaData(?:(?:ExportedVar|Option)s|Node)|torageOptions))|ptchaOptions)|(?:ustom(?:Plugins|Add)Param|heckUserHiddenHeader|ombModule)s)|s(?:aml(?:S(?:PMetaData(?:(?:ExportedAttribute|Option|Macro)s|Node|XML)|torageOptions)|IDPMetaData(?:(?:ExportedAttribute|Option)s|Node|XML))|essionDataToRemember|laveExportedVars|fExtra)|a(?:(?:daptativeAuthenticationLevelR|ut(?:hChoiceMod|oSigninR))ules|pplicationList)|p(?:ersistentStorageOptions|o(?:rtalSkinRules|st))|v(?:hostOptions|irtualHost)|S(?:MTPTLSOpts|SLVarIf))$/;
|
||||
our $arrayParameters = qr/^mySessionAuthorizedRWKeys$/;
|
||||
our $boolKeys = qr/^(?:s(?:aml(?:IDP(?:MetaDataOptions(?:(?:Check(?:S[LS]OMessageSignatur|Audienc|Tim)|IsPassiv)e|A(?:llow(?:LoginFromIDP|ProxiedAuthn)|daptSessionUtime)|Force(?:Authn|UTF8)|StoreSAMLToken|RelayStateURL)|SSODescriptorWantAuthnRequestsSigned)|S(?:P(?:MetaDataOptions(?:(?:CheckS[LS]OMessageSignatur|OneTimeUs)e|EnableIDPInitiatedURL|ForceUTF8)|SSODescriptor(?:WantAssertion|AuthnRequest)sSigned)|erviceUseCertificateInResponse)|DiscoveryProtocol(?:Activation|IsPassive)|CommonDomainCookieActivation|UseQueryStringSpecific|MetadataForceUTF8)|t(?:ayConnectedBypassFG|orePassword)|f(?:RemovedUseNotif|OnlyUpgrade)|kip(?:Upgrade|Renew)Confirmation|oap(?:Session|Config)Server|laveDisplayLogo|howLanguages|slByAjax)|o(?:idc(?:RPMetaDataOptions(?:A(?:llow(?:(?:ClientCredentials|Password)Grant|Offline)|ccessToken(?:Claims|JWT))|Re(?:freshToken|quirePKCE)|LogoutSessionRequired|IDTokenForceClaims|BypassConsent|Public)|ServiceAllow(?:(?:AuthorizationCode|Implicit|Hybrid)Flow|DynamicRegistration|OnlyDeclaredScopes)|OPMetaDataOptions(?:(?:CheckJWTSignatur|UseNonc)e|StoreIDToken))|ldNotifFormat)|c(?:a(?:sS(?:rvMetaDataOptions(?:Gateway|Renew)|trictMatching)|ptcha_(?:register|login|mail)_enabled)|heck(?:DevOps(?:D(?:isplayNormalizedHeaders|ownload)|CheckSessionAttributes)?|State|User|XSS)|o(?:ntextSwitching(?:Allowed2fModifications|StopWithLogout)|mpactConf|rsEnabled)|rowdsec|da)|p(?:ortal(?:Display(?:Re(?:freshMyRights|setPassword|gister)|CertificateResetByMail|GeneratePassword|PasswordPolicy)|E(?:rrorOn(?:ExpiredSession|MailNotFound)|nablePasswordDisplay)|(?:CheckLogin|Statu)s|OpenLinkInNewWindow|ForceAuthn|AntiFrame)|roxy(?:AuthServiceImpersonation|UseSoap))|l(?:dap(?:(?:G(?:roup(?:DecodeSearchedValu|Recursiv)|etUserBeforePasswordChang)|UsePasswordResetAttribut)e|(?:AllowResetExpired|Set)Password|ChangePasswordAsUser|PpolicyControl|ITDS)|oginHistoryEnabled)|n(?:o(?:tif(?:ication(?:Server(?:(?:POS|GE)T|DELETE)?|sExplorer)?|y(?:Deleted|Other))|AjaxHook)|ewLocationWarning)|i(?:ssuerDB(?:OpenID(?:Connect)?|SAML|CAS|Get)Activation|mpersonationSkipEmptyValues)|u(?:se(?:RedirectOn(?:Forbidden|Error)|SafeJail)|2fUserCanRemoveKey|pgradeSession)|re(?:st(?:(?:Password|Session|Config|Auth)Server|ExportSecretKeys)|freshSessions)|br(?:uteForceProtection(?:IncrementalTempo)?|owsersDontStorePassword)|d(?:is(?:ablePersistentStorage|playSessionId)|biDynamicHashEnabled)|to(?:tp2f(?:UserCanRemoveKey|EncryptSecret)|kenUseGlobalStorage)|(?:mai(?:lOnPasswordChang|ntenanc)|vhostMaintenanc)e|w(?:ebauthn2fUserCanRemoveKey|sdlServer)|g(?:roupsBeforeMacros|lobalLogoutTimer)|a(?:voidAssignment|ctiveTimer)|h(?:ideOldPassword|ttpOnly)|yubikey2fUserCanRemoveKey|krb(?:RemoveDomain|ByJs)|findUser)$/;
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@ our $specialNodeHash = {
|
|||
};
|
||||
|
||||
our $doubleHashKeys = 'issuerDBGetParameters';
|
||||
our $simpleHashKeys = '(?:(?:c(?:as(?:StorageOption|Attribute)|ustom(?:Plugins|Add)Param|heckUserHiddenHeader|ombModule)|l(?:o(?:calSessionStorageOption|goutService)|dapExportedVar|wp(?:Ssl)?Opt)|f(?:indUser(?:Exclud|Search)ingAttribute|acebookExportedVar)|re(?:moteGlobalStorageOption|st2f(?:Verify|Init)Arg|loadUrl)|g(?:r(?:antSessionRule|oup)|lobalStorageOption)|n(?:otificationStorageOption|ginxCustomHandler)|p(?:ersistentStorageOption|ortalSkinRule)|(?:(?:d(?:emo|bi)|webID)E|e)xportedVar|macro)s|o(?:idc(?:S(?:ervice(?:DynamicRegistrationEx(?:portedVar|traClaim)s|MetaDataAuthnContext)|torageOptions)|OPMetaDataJ(?:SON|WKS))|penIdExportedVars)|a(?:(?:daptativeAuthenticationLevelR|ut(?:hChoiceMod|oSigninR))ules|pplicationList)|s(?:(?:amlStorageOption|laveExportedVar)s|essionDataToRemember|fExtra)|S(?:MTPTLSOpts|SLVarIf))';
|
||||
our $simpleHashKeys = '(?:(?:c(?:a(?:s(?:StorageOption|Attribute)|ptchaOption)|ustom(?:Plugins|Add)Param|heckUserHiddenHeader|ombModule)|l(?:o(?:calSessionStorageOption|goutService)|dapExportedVar|wp(?:Ssl)?Opt)|f(?:indUser(?:Exclud|Search)ingAttribute|acebookExportedVar)|re(?:moteGlobalStorageOption|st2f(?:Verify|Init)Arg|loadUrl)|g(?:r(?:antSessionRule|oup)|lobalStorageOption)|n(?:otificationStorageOption|ginxCustomHandler)|p(?:ersistentStorageOption|ortalSkinRule)|(?:(?:d(?:emo|bi)|webID)E|e)xportedVar|macro)s|o(?:idc(?:S(?:ervice(?:DynamicRegistrationEx(?:portedVar|traClaim)s|MetaDataAuthnContext)|torageOptions)|OPMetaDataJ(?:SON|WKS))|penIdExportedVars)|a(?:(?:daptativeAuthenticationLevelR|ut(?:hChoiceMod|oSigninR))ules|pplicationList)|s(?:(?:amlStorageOption|laveExportedVar)s|essionDataToRemember|fExtra)|S(?:MTPTLSOpts|SLVarIf))';
|
||||
our $specialNodeKeys = '(?:(?:(?:saml(?:ID|S)|oidc[OR])P|cas(?:App|Srv))MetaDataNode|virtualHost)s';
|
||||
our $casAppMetaDataNodeKeys = 'casAppMetaData(?:Options(?:(?:UserAttribut|Servic|Rul)e|AuthnLevel)|(?:ExportedVar|Macro)s)';
|
||||
our $casSrvMetaDataNodeKeys = 'casSrvMetaData(?:Options(?:Re(?:solutionRule|new)|ProxiedServices|DisplayName|SortNumber|Gateway|Icon|Url)|ExportedVars)';
|
||||
|
|
|
@ -681,6 +681,9 @@ sub attributes {
|
|||
'default' => 30,
|
||||
'type' => 'int'
|
||||
},
|
||||
'captcha' => {
|
||||
'type' => 'PerlModule'
|
||||
},
|
||||
'captcha_login_enabled' => {
|
||||
'default' => 0,
|
||||
'type' => 'bool'
|
||||
|
@ -697,6 +700,9 @@ sub attributes {
|
|||
'default' => 6,
|
||||
'type' => 'int'
|
||||
},
|
||||
'captchaOptions' => {
|
||||
'type' => 'keyTextContainer'
|
||||
},
|
||||
'casAccessControlPolicy' => {
|
||||
'default' => 'none',
|
||||
'select' => [ {
|
||||
|
|
|
@ -1479,6 +1479,16 @@ sub attributes {
|
|||
default => 6,
|
||||
documentation => 'Captcha size',
|
||||
},
|
||||
captcha => {
|
||||
type => 'PerlModule',
|
||||
documentation => 'Captcha backend module',
|
||||
flags => 'hp',
|
||||
},
|
||||
captchaOptions => {
|
||||
type => 'keyTextContainer',
|
||||
documentation => 'Captcha module options',
|
||||
flags => 'hp',
|
||||
},
|
||||
|
||||
# Variables
|
||||
exportedVars => {
|
||||
|
|
|
@ -118,13 +118,20 @@ sub tree {
|
|||
},
|
||||
{
|
||||
title => 'portalCaptcha',
|
||||
help => 'captcha.html',
|
||||
form => 'simpleInputContainer',
|
||||
help => 'captcha.html#configuration',
|
||||
nodes => [
|
||||
'captcha_login_enabled',
|
||||
'captcha_mail_enabled',
|
||||
'captcha_register_enabled',
|
||||
'captcha_size',
|
||||
{
|
||||
title => 'captchaCustom',
|
||||
help => 'captcha.html#configuration',
|
||||
nodes => [
|
||||
'captcha',
|
||||
'captchaOptions',
|
||||
]
|
||||
},
|
||||
]
|
||||
}
|
||||
]
|
||||
|
@ -132,7 +139,7 @@ sub tree {
|
|||
{
|
||||
title => 'authParams',
|
||||
help =>
|
||||
'start.html#authentication-users-and-password-databases',
|
||||
'start.html#authentication-users-and-password-databases',
|
||||
form => 'authParams',
|
||||
nodes => [
|
||||
'authentication', 'userDB', 'passwordDB', 'registerDB'
|
||||
|
@ -217,8 +224,8 @@ sub tree {
|
|||
nodes => [
|
||||
'dbiDynamicHashEnabled',
|
||||
'dbiDynamicHashValidSchemes',
|
||||
'dbiDynamicHashValidSaltedSchemes',
|
||||
'dbiDynamicHashNewPasswordScheme'
|
||||
'dbiDynamicHashValidSaltedSchemes',
|
||||
'dbiDynamicHashNewPasswordScheme'
|
||||
]
|
||||
}
|
||||
]
|
||||
|
@ -556,7 +563,7 @@ sub tree {
|
|||
help => 'logs.html',
|
||||
form => 'simpleInputContainer',
|
||||
nodes =>
|
||||
[ 'whatToTrace', 'customToTrace', 'hiddenAttributes' ]
|
||||
[ 'whatToTrace', 'customToTrace', 'hiddenAttributes' ]
|
||||
},
|
||||
{
|
||||
title => 'cookieParams',
|
||||
|
@ -653,7 +660,7 @@ sub tree {
|
|||
{
|
||||
title => 'soapServices',
|
||||
help =>
|
||||
'portalservers.html#SOAP_(deprecated)',
|
||||
'portalservers.html#SOAP_(deprecated)',
|
||||
form => 'simpleInputContainer',
|
||||
nodes => [
|
||||
'soapSessionServer',
|
||||
|
@ -687,14 +694,14 @@ sub tree {
|
|||
{
|
||||
title => 'serverNotification',
|
||||
help =>
|
||||
'notifications.html#notification-server',
|
||||
'notifications.html#notification-server',
|
||||
nodes => [
|
||||
'notificationServer',
|
||||
'notificationDefaultCond',
|
||||
'notificationServerSentAttributes',
|
||||
{
|
||||
title =>
|
||||
'notificationServerMethods',
|
||||
'notificationServerMethods',
|
||||
form => 'simpleInputContainer',
|
||||
nodes => [
|
||||
'notificationServerPOST',
|
||||
|
|
|
@ -131,6 +131,9 @@
|
|||
"bruteForceProtectionMaxLockTime":"Maximum lock time",
|
||||
"bruteForceProtectionTempo":"Lock time",
|
||||
"cancel":"إلغاء",
|
||||
"captcha":"Captcha module",
|
||||
"captchaCustom":"Custom Captcha module",
|
||||
"captchaOptions":"Captcha module options",
|
||||
"captcha_login_enabled":"التفعيل في استمارة تسجيل الدخول",
|
||||
"captcha_mail_enabled":"التفعيل في إعادة تعيين كلمة المرور بواسطة استمارة البريد",
|
||||
"captcha_register_enabled":"التفعيل في استمارة التسجيل",
|
||||
|
|
|
@ -131,6 +131,9 @@
|
|||
"bruteForceProtectionMaxLockTime":"Maximum lock time",
|
||||
"bruteForceProtectionTempo":"Lock time",
|
||||
"cancel":"Cancel",
|
||||
"captcha":"Captcha module",
|
||||
"captchaCustom":"Custom Captcha module",
|
||||
"captchaOptions":"Captcha module options",
|
||||
"captcha_login_enabled":"Activation in login form",
|
||||
"captcha_mail_enabled":"Activation in password reset by mail form",
|
||||
"captcha_register_enabled":"Activation in register form",
|
||||
|
|
|
@ -131,6 +131,9 @@
|
|||
"bruteForceProtectionMaxLockTime":"Maximum lock time",
|
||||
"bruteForceProtectionTempo":"Lock time",
|
||||
"cancel":"Cancelar",
|
||||
"captcha":"Captcha module",
|
||||
"captchaCustom":"Custom Captcha module",
|
||||
"captchaOptions":"Captcha module options",
|
||||
"captcha_login_enabled":"Activación en formulario de acceso",
|
||||
"captcha_mail_enabled":"Activación en formulario de restauración por correo",
|
||||
"captcha_register_enabled":"Activación en formulario de registro",
|
||||
|
|
|
@ -131,6 +131,9 @@
|
|||
"bruteForceProtectionMaxLockTime":"Temps maximum de verrouillage",
|
||||
"bruteForceProtectionTempo":"Temps de verrouillage",
|
||||
"cancel":"Annuler",
|
||||
"captcha":"Module Captcha",
|
||||
"captchaCustom":"Module Captcha personnalisé",
|
||||
"captchaOptions":"Options du module Captcha",
|
||||
"captcha_login_enabled":"Activation dans le formulaire d'authentification",
|
||||
"captcha_mail_enabled":"Activation dans le formulaire de réinitialisation par mail",
|
||||
"captcha_register_enabled":"Activation dans le formulaire de création de compte",
|
||||
|
|
|
@ -131,6 +131,9 @@
|
|||
"bruteForceProtectionMaxLockTime":"זמן הנעילה המרבי",
|
||||
"bruteForceProtectionTempo":"זמן נעילה",
|
||||
"cancel":"ביטול",
|
||||
"captcha":"Captcha module",
|
||||
"captchaCustom":"Custom Captcha module",
|
||||
"captchaOptions":"Captcha module options",
|
||||
"captcha_login_enabled":"הפעלה בטופס הכניסה",
|
||||
"captcha_mail_enabled":"הפעלה באיפוס סיסמה בטופס בדוא״ל",
|
||||
"captcha_register_enabled":"הפעלה בטופס הרשמה",
|
||||
|
|
|
@ -131,6 +131,9 @@
|
|||
"bruteForceProtectionMaxLockTime":"Maximum lock time",
|
||||
"bruteForceProtectionTempo":"Lock time",
|
||||
"cancel":"Cancella",
|
||||
"captcha":"Captcha module",
|
||||
"captchaCustom":"Custom Captcha module",
|
||||
"captchaOptions":"Captcha module options",
|
||||
"captcha_login_enabled":"Attivazione nel modulo di login",
|
||||
"captcha_mail_enabled":"Attivazione della reimpostazione della password tramite modulo di posta",
|
||||
"captcha_register_enabled":"Attivazione nel formulario di registro",
|
||||
|
|
|
@ -131,6 +131,9 @@
|
|||
"bruteForceProtectionMaxLockTime":"Maximum lock time",
|
||||
"bruteForceProtectionTempo":"Czas blokady",
|
||||
"cancel":"Anuluj",
|
||||
"captcha":"Captcha module",
|
||||
"captchaCustom":"Custom Captcha module",
|
||||
"captchaOptions":"Captcha module options",
|
||||
"captcha_login_enabled":"Aktywacja w formularzu logowania",
|
||||
"captcha_mail_enabled":"Aktywacja przy resetowaniu hasła za pomocą formularza pocztowego",
|
||||
"captcha_register_enabled":"Aktywacja w formularzu rejestracji",
|
||||
|
|
|
@ -131,6 +131,9 @@
|
|||
"bruteForceProtectionMaxLockTime":"Maksimum kilit süresi",
|
||||
"bruteForceProtectionTempo":"Kilit süresi",
|
||||
"cancel":"İptal Et",
|
||||
"captcha":"Captcha module",
|
||||
"captchaCustom":"Custom Captcha module",
|
||||
"captchaOptions":"Captcha module options",
|
||||
"captcha_login_enabled":"Giriş formunda aktivasyon",
|
||||
"captcha_mail_enabled":"E-posta formu tarafından parola sıfırlamada aktivasyon",
|
||||
"captcha_register_enabled":"Kayıt formunda aktivasyon",
|
||||
|
|
|
@ -131,6 +131,9 @@
|
|||
"bruteForceProtectionMaxLockTime":"Maximum lock time",
|
||||
"bruteForceProtectionTempo":"Lock time",
|
||||
"cancel":"Hủy",
|
||||
"captcha":"Captcha module",
|
||||
"captchaCustom":"Custom Captcha module",
|
||||
"captchaOptions":"Captcha module options",
|
||||
"captcha_login_enabled":"Kích hoạt ở dạng đăng nhập",
|
||||
"captcha_mail_enabled":"Kích hoạt đặt lại mật khẩu bằng biểu mẫu thư",
|
||||
"captcha_register_enabled":"Kích hoạt trong biểu mẫu đăng ký",
|
||||
|
|
|
@ -131,6 +131,9 @@
|
|||
"bruteForceProtectionMaxLockTime":"Maximum lock time",
|
||||
"bruteForceProtectionTempo":"鎖時間",
|
||||
"cancel":"取消",
|
||||
"captcha":"Captcha module",
|
||||
"captchaCustom":"Custom Captcha module",
|
||||
"captchaOptions":"Captcha module options",
|
||||
"captcha_login_enabled":" 登录激活",
|
||||
"captcha_mail_enabled":"通过邮件进行密码重置 激活",
|
||||
"captcha_register_enabled":"注册 激活",
|
||||
|
|
|
@ -131,6 +131,9 @@
|
|||
"bruteForceProtectionMaxLockTime":"Maximum lock time",
|
||||
"bruteForceProtectionTempo":"鎖時間",
|
||||
"cancel":"取消",
|
||||
"captcha":"Captcha module",
|
||||
"captchaCustom":"Custom Captcha module",
|
||||
"captchaOptions":"Captcha module options",
|
||||
"captcha_login_enabled":"在登入表單中啟用",
|
||||
"captcha_mail_enabled":"透過郵件表單啟用密碼重設",
|
||||
"captcha_register_enabled":"在註冊表單中啟用",
|
||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -48,6 +48,8 @@ lib/Lemonldap/NG/Portal/Auth/Slave.pm
|
|||
lib/Lemonldap/NG/Portal/Auth/SSL.pm
|
||||
lib/Lemonldap/NG/Portal/Auth/Twitter.pm
|
||||
lib/Lemonldap/NG/Portal/Auth/WebID.pm
|
||||
lib/Lemonldap/NG/Portal/Captcha.pod
|
||||
lib/Lemonldap/NG/Portal/Captcha/SecurityImage.pm
|
||||
lib/Lemonldap/NG/Portal/CDC.pm
|
||||
lib/Lemonldap/NG/Portal/CertificateResetByMail/Custom.pm
|
||||
lib/Lemonldap/NG/Portal/CertificateResetByMail/Demo.pm
|
||||
|
|
|
@ -44,7 +44,7 @@ sub init {
|
|||
my $self = shift;
|
||||
|
||||
if ( $self->{conf}->{captcha_login_enabled} ) {
|
||||
$self->captcha( $self->p->loadModule('::Lib::Captcha') ) or return 0;
|
||||
$self->captcha(1);
|
||||
}
|
||||
else {
|
||||
$self->ott( $self->p->loadModule('::Lib::OneTimeToken') ) or return 0;
|
||||
|
@ -117,18 +117,15 @@ sub extractFormInfo {
|
|||
}
|
||||
|
||||
if ( $self->captcha ) {
|
||||
my $code = $req->param('captcha');
|
||||
unless ($code) {
|
||||
$self->captcha->setCaptcha($req);
|
||||
return PE_CAPTCHAEMPTY;
|
||||
my $result = $self->p->_captcha->check_captcha($req);
|
||||
if ($result) {
|
||||
$self->logger->debug("Captcha code verified");
|
||||
}
|
||||
unless ( $self->captcha->validateCaptcha( $token, $code ) ) {
|
||||
$self->captcha->setCaptcha($req);
|
||||
$self->userLogger->warn(
|
||||
"Captcha failed: wrong or expired code");
|
||||
else {
|
||||
$self->p->_captcha->init_captcha($req);
|
||||
$self->userLogger->warn("Captcha failed");
|
||||
return PE_CAPTCHAERROR;
|
||||
}
|
||||
$self->logger->debug("Captcha code verified");
|
||||
}
|
||||
elsif ( $self->ottRule->( $req, {} ) ) {
|
||||
unless ( $req->data->{tokenVerified}
|
||||
|
@ -179,7 +176,7 @@ sub setSecurity {
|
|||
|
||||
# If captcha is enable, prepare it
|
||||
if ( $self->captcha ) {
|
||||
$self->captcha->setCaptcha($req);
|
||||
$self->p->_captcha->init_captcha($req);
|
||||
}
|
||||
|
||||
# Else get token
|
||||
|
|
|
@ -0,0 +1,153 @@
|
|||
=pod
|
||||
|
||||
=encoding utf8
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Lemonldap:NG::Portal::Captcha - Writing CAPTCHA modules for LemonLDAP::NG.
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
package Lemonldap::NG::Portal::Captcha::My;
|
||||
|
||||
use strict;
|
||||
use Mouse;
|
||||
# Add constants used by this module
|
||||
|
||||
our $VERSION = '0.1';
|
||||
|
||||
extends 'Lemonldap::NG::Portal::Main::Plugin';
|
||||
|
||||
sub init {
|
||||
my $self = shift;
|
||||
return 1;
|
||||
}
|
||||
|
||||
sub init_captcha {
|
||||
my ( $self, $req ) = @_;
|
||||
|
||||
# Read option from the manager configuration
|
||||
my $option = $self->conf->{captchaOptions}->{option1};
|
||||
|
||||
# This can be used to inject custom JS code at the beginning
|
||||
# of the page
|
||||
my $script = <your html code>;
|
||||
$req->data->{customScript} .= $script;
|
||||
|
||||
# This will add your custom HTML code to the protected form
|
||||
my $html = <your html code>;
|
||||
$req->captchaHtml($html);
|
||||
}
|
||||
|
||||
sub check_captcha {
|
||||
my ( $self, $req ) = @_;
|
||||
|
||||
my $captcha_input = $req->param('some_post_param');
|
||||
my $is_captcha_valid = <your code here>;
|
||||
|
||||
if($is_captcha_valid) {
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
Captcha modules only need to implement two methods: one for initializing the
|
||||
challenge, before the form is displayed, and the other to verify that the
|
||||
submitted response is correct.
|
||||
|
||||
=head1 METHODS
|
||||
|
||||
=head2 Accessors and methods provided by Lemonldap::NG::Portal::Main::Plugin
|
||||
|
||||
=over
|
||||
|
||||
=item p: portal object
|
||||
|
||||
=item conf: configuration hash (as reference)
|
||||
|
||||
=item logger alias for p->logger accessor
|
||||
|
||||
=item userLogger alias for p->userLogger accessor
|
||||
|
||||
=item error: alias for p->error method
|
||||
|
||||
=back
|
||||
|
||||
=head2 "Routes" management
|
||||
|
||||
Like each module that inherits from Lemonldap::NG::Portal::Plugin,
|
||||
you can define dedicated routes in a Captcha plugin.
|
||||
|
||||
=over
|
||||
|
||||
=item addAuthRoute: wrapper to L<Lemonldap::NG::Handler::PSGI::Try>
|
||||
addAuthRoute() method
|
||||
|
||||
=item addUnauthRoute: wrapper to L<Lemonldap::NG::Handler::PSGI::Try>
|
||||
addUnauthRoute() method
|
||||
|
||||
=back
|
||||
|
||||
=head2 Methods that must be provided by a Captcha module
|
||||
|
||||
=head3 init_captcha($req)
|
||||
|
||||
This method is called when the protected form is built by LemonLDAP::NG.
|
||||
Your responsibility is to do any preparatory step, and provide LemonLDAP::NG
|
||||
with the HTML code that it has to display in the form to enable the Captcha.
|
||||
|
||||
This is done by setting C<$req-E<gt>captchaHtml>
|
||||
|
||||
=head3 check_captcha($req)
|
||||
|
||||
This method is called after the user submitted the protected form. Your
|
||||
responibility is to check the user's response (usually provided as a POST
|
||||
field), and return 0 if the response is incorrect, 1 if the response is
|
||||
correct.
|
||||
|
||||
=head1 LOGGING
|
||||
|
||||
Logging is provided by C<$self-E<gt>logger> and C<$self-E<gt>userLogger>. See
|
||||
L<Lemonldap::NG::Portal::Main::Plugin> for a detailed description of logging levels.
|
||||
|
||||
=head1 AUTHORS
|
||||
|
||||
=over
|
||||
|
||||
=item LemonLDAP::NG team L<http://lemonldap-ng.org/team>
|
||||
|
||||
=back
|
||||
|
||||
=head1 BUG REPORT
|
||||
|
||||
Use OW2 system to report bug or ask for features:
|
||||
L<https://gitlab.ow2.org/lemonldap-ng/lemonldap-ng/issues>
|
||||
|
||||
=head1 DOWNLOAD
|
||||
|
||||
Lemonldap::NG is available at
|
||||
L<https://lemonldap-ng.org/download>
|
||||
|
||||
=head1 COPYRIGHT AND LICENSE
|
||||
|
||||
See COPYING file for details.
|
||||
|
||||
This library is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2, or (at your option)
|
||||
any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see L<http://www.gnu.org/licenses/>.
|
||||
|
||||
=cut
|
|
@ -0,0 +1,189 @@
|
|||
package Lemonldap::NG::Portal::Captcha::SecurityImage;
|
||||
|
||||
use strict;
|
||||
use Mouse;
|
||||
use MIME::Base64;
|
||||
use GD::SecurityImage use_magick => 1;
|
||||
|
||||
our $VERSION = '2.0.12';
|
||||
|
||||
extends 'Lemonldap::NG::Portal::Main::Plugin';
|
||||
|
||||
has width => (
|
||||
is => 'rw',
|
||||
lazy => 1,
|
||||
default => sub { $_[0]->{conf}->{captchaWidth} || 220 }
|
||||
);
|
||||
has height => (
|
||||
is => 'rw',
|
||||
lazy => 1,
|
||||
default => sub { $_[0]->{conf}->{captchaHeight} || 40 }
|
||||
);
|
||||
has lines => (
|
||||
is => 'rw',
|
||||
lazy => 1,
|
||||
default => sub { $_[0]->{conf}->{captchaLines} || 5 }
|
||||
);
|
||||
has scramble => (
|
||||
is => 'rw',
|
||||
lazy => 1,
|
||||
default => sub { $_[0]->{conf}->{captchaScramble} || 1 }
|
||||
);
|
||||
has fgColor => (
|
||||
is => 'rw',
|
||||
lazy => 1,
|
||||
default => sub { $_[0]->{conf}->{captchaFg} || '#403030' }
|
||||
);
|
||||
has bgColor => (
|
||||
is => 'rw',
|
||||
lazy => 1,
|
||||
default => sub { $_[0]->{conf}->{captchaBg} || '#FF644B' }
|
||||
);
|
||||
has rndmax => (
|
||||
is => 'rw',
|
||||
lazy => 1,
|
||||
default => sub { $_[0]->{conf}->{captcha_size} || 6 }
|
||||
);
|
||||
has timeout => (
|
||||
is => 'rw',
|
||||
lazy => 1,
|
||||
default => sub { $_[0]->{conf}->{formTimeout} }
|
||||
);
|
||||
|
||||
has ott => (
|
||||
is => 'rw',
|
||||
lazy => 1,
|
||||
default => sub {
|
||||
my $ott = $_[0]->{p}->loadModule('::Lib::OneTimeToken');
|
||||
$ott->timeout( $_[0]->timeout );
|
||||
return $ott;
|
||||
}
|
||||
);
|
||||
|
||||
sub init {
|
||||
my ($self) = @_;
|
||||
if ( $self->conf->{captcha_mail_enabled}
|
||||
|| $self->conf->{captcha_login_enabled}
|
||||
|| $self->conf->{captcha_register_enabled} )
|
||||
{
|
||||
$self->addUnauthRoute( renewcaptcha => '_sendCaptcha', ['GET'] );
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
# Internal methods
|
||||
sub _getCaptcha {
|
||||
my ($self) = @_;
|
||||
my $image = GD::SecurityImage->new(
|
||||
width => $self->width,
|
||||
height => $self->height,
|
||||
lines => $self->lines,
|
||||
gd_font => 'Giant',
|
||||
scramble => $self->scramble,
|
||||
rndmax => $self->rndmax,
|
||||
);
|
||||
$image->random;
|
||||
$image->create( 'normal', 'default', $self->fgColor, $self->bgColor );
|
||||
my ( $imageData, $mimeType, $rdm ) = $image->out( force => 'png' );
|
||||
my $img = 'data:image/png;base64,' . encode_base64( $imageData, '' );
|
||||
my $token = $self->ott->createToken( { captcha => $rdm } );
|
||||
return ( $token, $img );
|
||||
}
|
||||
|
||||
sub _sendCaptcha {
|
||||
my ( $self, $req ) = @_;
|
||||
$self->logger->info("User request for captcha renew");
|
||||
my ( $token, $image ) = $self->_getCaptcha($req);
|
||||
|
||||
return $self->p->sendJSONresponse( $req,
|
||||
{ newtoken => $token, newimage => $image } );
|
||||
}
|
||||
|
||||
sub _validate_captcha_token {
|
||||
my ( $self, $token, $value ) = @_;
|
||||
my $s = $self->ott->getToken($token);
|
||||
unless ($s) {
|
||||
$self->logger->warn("Captcha token $token isn't valid");
|
||||
return 0;
|
||||
}
|
||||
unless ( $s->{captcha} eq $value ) {
|
||||
$self->logger->notice('Bad captcha response');
|
||||
return 0;
|
||||
}
|
||||
$self->logger->debug('Good captcha response');
|
||||
return 1;
|
||||
}
|
||||
|
||||
sub _get_captcha_html {
|
||||
my ( $self, $req, $src ) = @_;
|
||||
|
||||
my $sp = $self->p->staticPrefix;
|
||||
$sp =~ s/\/*$/\//;
|
||||
|
||||
return $self->loadTemplate(
|
||||
$req,
|
||||
'captcha',
|
||||
params => {
|
||||
STATIC_PREFIX => $sp,
|
||||
CAPTCHA_SRC => $src,
|
||||
CAPTCHA_SIZE => $self->rndmax,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
# New API
|
||||
sub check_captcha {
|
||||
my ( $self, $req ) = @_;
|
||||
my $token = $req->param('token');
|
||||
unless ($token) {
|
||||
$self->logger->warn("No token provided for Captcha::SecurityImage");
|
||||
return 0;
|
||||
}
|
||||
|
||||
my $value = $req->param('captcha');
|
||||
unless ($value) {
|
||||
$self->logger->warn("No response provided for Captcha::SecurityImage");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return $self->_validate_captcha_token( $token, $value );
|
||||
}
|
||||
|
||||
sub init_captcha {
|
||||
my ( $self, $req ) = @_;
|
||||
|
||||
my ( $token, $image ) = $self->_getCaptcha;
|
||||
$self->logger->debug('Prepare captcha');
|
||||
$req->token($token);
|
||||
$req->captchaHtml( $self->_get_captcha_html( $req, $image ) );
|
||||
|
||||
# DEPRECATED: Compatibility with old templates
|
||||
$req->captcha($image);
|
||||
}
|
||||
|
||||
# #######
|
||||
# Old API
|
||||
# TODO: Remove this in 3.0
|
||||
# #######
|
||||
|
||||
sub validateCaptcha {
|
||||
my ( $self, $token, $value ) = @_;
|
||||
return $self->_validate_captcha_token( $token, $value );
|
||||
}
|
||||
|
||||
sub setCaptcha {
|
||||
my ( $self, $req ) = @_;
|
||||
$self->init_captcha($req);
|
||||
}
|
||||
|
||||
sub sendCaptcha {
|
||||
my ( $self, $req ) = @_;
|
||||
return $self->_sendCaptcha($req);
|
||||
}
|
||||
|
||||
sub getCaptcha {
|
||||
my ( $self, $req ) = @_;
|
||||
return $self->_getCaptcha($req);
|
||||
}
|
||||
|
||||
1;
|
|
@ -1,109 +1,40 @@
|
|||
package Lemonldap::NG::Portal::Lib::Captcha;
|
||||
|
||||
# Old Captcha API, this is only a wrapper around Captcha::SecurityImage
|
||||
|
||||
use strict;
|
||||
use Mouse;
|
||||
use MIME::Base64;
|
||||
use GD::SecurityImage use_magick => 1;
|
||||
|
||||
our $VERSION = '2.0.12';
|
||||
|
||||
extends 'Lemonldap::NG::Common::Module';
|
||||
|
||||
has width => (
|
||||
has module => (
|
||||
is => 'rw',
|
||||
lazy => 1,
|
||||
default => sub { $_[0]->{conf}->{captchaWidth} || 220 }
|
||||
);
|
||||
has height => (
|
||||
is => 'rw',
|
||||
lazy => 1,
|
||||
default => sub { $_[0]->{conf}->{captchaHeight} || 40 }
|
||||
);
|
||||
has lines => (
|
||||
is => 'rw',
|
||||
lazy => 1,
|
||||
default => sub { $_[0]->{conf}->{captchaLines} || 5 }
|
||||
);
|
||||
has scramble => (
|
||||
is => 'rw',
|
||||
lazy => 1,
|
||||
default => sub { $_[0]->{conf}->{captchaScramble} || 1 }
|
||||
);
|
||||
has fgColor => (
|
||||
is => 'rw',
|
||||
lazy => 1,
|
||||
default => sub { $_[0]->{conf}->{captchaFg} || '#403030' }
|
||||
);
|
||||
has bgColor => (
|
||||
is => 'rw',
|
||||
lazy => 1,
|
||||
default => sub { $_[0]->{conf}->{captchaBg} || '#FF644B' }
|
||||
);
|
||||
has rndmax => (
|
||||
is => 'rw',
|
||||
lazy => 1,
|
||||
default => sub { $_[0]->{conf}->{captcha_size} || 6 }
|
||||
);
|
||||
has timeout => (
|
||||
is => 'rw',
|
||||
lazy => 1,
|
||||
default => sub { $_[0]->{conf}->{formTimeout} }
|
||||
);
|
||||
|
||||
has ott => (
|
||||
is => 'rw',
|
||||
lazy => 1,
|
||||
default => sub {
|
||||
my $ott = $_[0]->{p}->loadModule('::Lib::OneTimeToken');
|
||||
$ott->timeout( $_[0]->timeout );
|
||||
return $ott;
|
||||
}
|
||||
handles => [
|
||||
qw(setCaptcha validateCaptcha getCaptcha ott width height lines scramble fgColor bgColor rndmax timeout )
|
||||
]
|
||||
);
|
||||
|
||||
sub init {
|
||||
return 1;
|
||||
}
|
||||
|
||||
# Returns secret + a HTML image src content
|
||||
sub getCaptcha {
|
||||
my ($self) = @_;
|
||||
my $image = GD::SecurityImage->new(
|
||||
width => $self->width,
|
||||
height => $self->height,
|
||||
lines => $self->lines,
|
||||
gd_font => 'Giant',
|
||||
scramble => $self->scramble,
|
||||
rndmax => $self->rndmax,
|
||||
);
|
||||
$image->random;
|
||||
$image->create( 'normal', 'default', $self->fgColor, $self->bgColor );
|
||||
my ( $imageData, $mimeType, $rdm ) = $image->out( force => 'png' );
|
||||
my $img = 'data:image/png;base64,' . encode_base64( $imageData, '' );
|
||||
my $token = $self->ott->createToken( { captcha => $rdm } );
|
||||
return ( $token, $img );
|
||||
}
|
||||
|
||||
sub validateCaptcha {
|
||||
my ( $self, $token, $value ) = @_;
|
||||
my $s = $self->ott->getToken($token);
|
||||
unless ($s) {
|
||||
$self->logger->warn("Captcha token $token isn't valid");
|
||||
if ( $self->conf->{captcha} ) {
|
||||
$self->logger->error( "The Lib::Captcha API is not compatible"
|
||||
. " with custom Captcha module" );
|
||||
return 0;
|
||||
}
|
||||
unless ( $s->{captcha} eq $value ) {
|
||||
$self->logger->notice('Bad captcha response');
|
||||
return 0;
|
||||
else {
|
||||
my $module = $self->p->loadModule("::Captcha::SecurityImage");
|
||||
if ($module) {
|
||||
$self->module($module);
|
||||
return 1;
|
||||
}
|
||||
else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
$self->logger->debug('Good captcha response');
|
||||
return 1;
|
||||
}
|
||||
|
||||
sub setCaptcha {
|
||||
my ( $self, $req ) = @_;
|
||||
my ( $token, $image ) = $self->getCaptcha;
|
||||
$self->logger->debug('Prepare captcha');
|
||||
$req->token($token);
|
||||
$req->captcha($image);
|
||||
}
|
||||
|
||||
1;
|
||||
|
|
|
@ -446,6 +446,15 @@ sub display {
|
|||
}
|
||||
|
||||
# Display captcha if it's enabled
|
||||
if ( $req->captchaHtml ) {
|
||||
%templateParams =
|
||||
( %templateParams, CAPTCHA_HTML => $req->captchaHtml, );
|
||||
}
|
||||
if ( $req->token ) {
|
||||
%templateParams = ( %templateParams, TOKEN => $req->token, );
|
||||
}
|
||||
|
||||
# DEPRECATED: This is only used for compatibility with existing templates
|
||||
if ( $req->captcha ) {
|
||||
%templateParams = (
|
||||
%templateParams,
|
||||
|
@ -453,9 +462,6 @@ sub display {
|
|||
CAPTCHA_SIZE => $self->{conf}->{captcha_size} || 6
|
||||
);
|
||||
}
|
||||
if ( $req->token ) {
|
||||
%templateParams = ( %templateParams, TOKEN => $req->token, );
|
||||
}
|
||||
|
||||
# Show password form if password policy error
|
||||
if (
|
||||
|
|
|
@ -30,6 +30,7 @@ has _authentication => ( is => 'rw' );
|
|||
has _userDB => ( is => 'rw' );
|
||||
has _passwordDB => ( is => 'rw' );
|
||||
has _sfEngine => ( is => 'rw' );
|
||||
has _captcha => ( is => 'rw' );
|
||||
|
||||
has loadedModules => ( is => 'rw' );
|
||||
|
||||
|
@ -336,6 +337,14 @@ sub reloadConf {
|
|||
unless $self->{_sfEngine} =
|
||||
$self->loadPlugin( $self->conf->{'sfEngine'} );
|
||||
|
||||
# Load Captcha module
|
||||
return $self->fail
|
||||
unless $self->_captcha(
|
||||
$self->loadPlugin(
|
||||
$self->conf->{'captcha'} || '::Captcha::SecurityImage'
|
||||
)
|
||||
);
|
||||
|
||||
# Compile macros in _macros, groups in _groups
|
||||
foreach my $type (qw(macros groups)) {
|
||||
$self->{"_$type"} = {};
|
||||
|
|
|
@ -74,8 +74,10 @@ has lockTime => ( is => 'rw' );
|
|||
|
||||
# Security
|
||||
#
|
||||
# Captcha
|
||||
has captcha => ( is => 'rw' );
|
||||
# Captcha HTML code to display in forms
|
||||
has captchaHtml => ( is => 'rw' );
|
||||
# DEPRECATED: 2.0 captcha compatibility
|
||||
has captcha => ( is => 'rw' );
|
||||
|
||||
# Token
|
||||
has token => ( is => 'rw' );
|
||||
|
|
|
@ -98,7 +98,7 @@ sub init {
|
|||
|
||||
# Initialize Captcha if needed
|
||||
if ( $self->conf->{captcha_mail_enabled} ) {
|
||||
$self->captcha( $self->p->loadModule('::Lib::Captcha') ) or return 0;
|
||||
$self->captcha(1);
|
||||
}
|
||||
|
||||
# Load registered module
|
||||
|
@ -179,46 +179,32 @@ sub _certificateReset {
|
|||
# Use submitted value
|
||||
$req->{user} = $req->param('mail');
|
||||
|
||||
# Check if token exists
|
||||
my $token;
|
||||
if ( $self->ottRule->( $req, {} ) or $self->captcha ) {
|
||||
$token = $req->param('token');
|
||||
# Captcha for register form
|
||||
if ( $self->captcha ) {
|
||||
my $result = $self->p->_captcha->check_captcha($req);
|
||||
if ($result) {
|
||||
$self->logger->debug("Captcha code verified");
|
||||
}
|
||||
else {
|
||||
$self->setSecurity($req);
|
||||
$self->userLogger->warn("Captcha failed");
|
||||
return PE_CAPTCHAERROR;
|
||||
}
|
||||
}
|
||||
elsif ( $self->ottRule->( $req, {} ) ) {
|
||||
my $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) {
|
||||
$self->userLogger->notice('Reset try with captcha not filled');
|
||||
|
||||
# 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;
|
||||
}
|
||||
$self->logger->debug('Captcha code verified');
|
||||
}
|
||||
elsif ( $self->ottRule->( $req, {} ) ) {
|
||||
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;
|
||||
|
@ -581,9 +567,11 @@ sub modifyCertificate {
|
|||
|
||||
sub setSecurity {
|
||||
my ( $self, $req ) = @_;
|
||||
|
||||
if ( $self->captcha ) {
|
||||
$self->captcha->setCaptcha($req);
|
||||
$self->p->_captcha->init_captcha($req);
|
||||
}
|
||||
|
||||
elsif ( $self->ottRule->( $req, {} ) ) {
|
||||
$self->ott->setToken($req);
|
||||
}
|
||||
|
@ -608,9 +596,13 @@ sub display {
|
|||
STARTMAILDATE => $req->data->{startMailDate},
|
||||
STARTMAILTIME => $req->data->{startMailTime},
|
||||
MAILALREADYSENT => $req->data->{mailAlreadySent},
|
||||
MAIL => (
|
||||
$self->p->checkXSSAttack( 'mail', $req->{user} )
|
||||
? ''
|
||||
(
|
||||
$req->data->{customScript}
|
||||
? ( CUSTOM_SCRIPT => $req->data->{customScript} )
|
||||
: ()
|
||||
),
|
||||
MAIL => (
|
||||
$self->p->checkXSSAttack( 'mail', $req->{user} ) ? ''
|
||||
: $req->{user}
|
||||
),
|
||||
DISPLAY_FORM => 0,
|
||||
|
@ -627,14 +619,19 @@ sub display {
|
|||
}
|
||||
|
||||
# Display captcha if enabled
|
||||
if ( $req->captcha ) {
|
||||
$tplPrm{CAPTCHA_SRC} = $req->captcha;
|
||||
$tplPrm{CAPTCHA_SIZE} = $self->conf->{captcha_size};
|
||||
if ( $req->captchaHtml ) {
|
||||
$tplPrm{CAPTCHA_HTML} = $req->captchaHtml;
|
||||
}
|
||||
if ( $req->token ) {
|
||||
$tplPrm{TOKEN} = $req->token;
|
||||
}
|
||||
|
||||
# DEPRECATED: This is only used for compatibility with existing templates
|
||||
if ( $req->captcha ) {
|
||||
$tplPrm{CAPTCHA_SRC} = $req->captcha;
|
||||
$tplPrm{CAPTCHA_SIZE} = $self->conf->{captcha_size};
|
||||
}
|
||||
|
||||
# Display form the first time
|
||||
if ( (
|
||||
$req->error == PE_MAILFORMEMPTY
|
||||
|
|
|
@ -71,7 +71,7 @@ sub init {
|
|||
|
||||
# Initialize Captcha if needed
|
||||
if ( $self->conf->{captcha_mail_enabled} ) {
|
||||
$self->captcha( $self->p->loadModule('::Lib::Captcha') ) or return 0;
|
||||
$self->captcha(1);
|
||||
}
|
||||
|
||||
# Parse password policy activation rule
|
||||
|
@ -154,46 +154,32 @@ sub _reset {
|
|||
# Use submitted value
|
||||
$req->{user} = $req->param('mail');
|
||||
|
||||
# Check if token exists
|
||||
my $token;
|
||||
if ( $self->ottRule->( $req, {} ) or $self->captcha ) {
|
||||
$token = $req->param('token');
|
||||
# Captcha for register form
|
||||
if ( $self->captcha ) {
|
||||
my $result = $self->p->_captcha->check_captcha($req);
|
||||
if ($result) {
|
||||
$self->logger->debug("Captcha code verified");
|
||||
}
|
||||
else {
|
||||
$self->setSecurity($req);
|
||||
$self->userLogger->warn("Captcha failed");
|
||||
return PE_CAPTCHAERROR;
|
||||
}
|
||||
}
|
||||
elsif ( $self->ottRule->( $req, {} ) ) {
|
||||
my $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) {
|
||||
$self->userLogger->notice('Reset try with captcha not filled');
|
||||
|
||||
# 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;
|
||||
}
|
||||
$self->logger->debug('Captcha code verified');
|
||||
}
|
||||
elsif ( $self->ottRule->( $req, {} ) ) {
|
||||
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;
|
||||
|
@ -583,7 +569,7 @@ sub changePwd {
|
|||
sub setSecurity {
|
||||
my ( $self, $req ) = @_;
|
||||
if ( $self->captcha ) {
|
||||
$self->captcha->setCaptcha($req);
|
||||
$self->p->_captcha->init_captcha($req);
|
||||
}
|
||||
elsif ( $self->ottRule->( $req, {} ) ) {
|
||||
$self->ott->setToken($req);
|
||||
|
@ -623,6 +609,11 @@ sub display {
|
|||
STARTMAILDATE => $req->data->{startMailDate},
|
||||
STARTMAILTIME => $req->data->{startMailTime},
|
||||
MAILALREADYSENT => $req->data->{mailAlreadySent},
|
||||
(
|
||||
$req->data->{customScript}
|
||||
? ( CUSTOM_SCRIPT => $req->data->{customScript} )
|
||||
: ()
|
||||
),
|
||||
MAIL => (
|
||||
$self->p->checkXSSAttack( 'mail', $req->{user} )
|
||||
? ''
|
||||
|
@ -653,14 +644,19 @@ sub display {
|
|||
}
|
||||
|
||||
# Display captcha if it's enabled
|
||||
if ( $req->captcha ) {
|
||||
$tplPrm{CAPTCHA_SRC} = $req->captcha;
|
||||
$tplPrm{CAPTCHA_SIZE} = $self->conf->{captcha_size};
|
||||
if ( $req->captchaHtml ) {
|
||||
$tplPrm{CAPTCHA_HTML} = $req->captchaHtml;
|
||||
}
|
||||
if ( $req->token ) {
|
||||
$tplPrm{TOKEN} = $req->token;
|
||||
}
|
||||
|
||||
# DEPRECATED: This is only used for compatibility with existing templates
|
||||
if ( $req->captcha ) {
|
||||
$tplPrm{CAPTCHA_SRC} = $req->captcha;
|
||||
$tplPrm{CAPTCHA_SIZE} = $self->conf->{captcha_size};
|
||||
}
|
||||
|
||||
# Display form the first time
|
||||
if ( (
|
||||
$req->error == PE_MAILFORMEMPTY
|
||||
|
|
|
@ -18,7 +18,6 @@
|
|||
# * DELETE /sessions/my : ask for global logout (if GlobalLogout plugin is on)
|
||||
#
|
||||
# - Authentication
|
||||
# * GET /renewcaptcha : get token and captcha image
|
||||
# * POST /sessions/<type>/<session-id>?auth : authenticate with a fixed
|
||||
# sessionId
|
||||
# * Note that the "getCookie" method (authentification via SOAP) exists for
|
||||
|
@ -71,7 +70,6 @@ our $VERSION = '2.0.14';
|
|||
|
||||
extends qw(
|
||||
Lemonldap::NG::Portal::Main::Plugin
|
||||
Lemonldap::NG::Portal::Lib::Captcha
|
||||
);
|
||||
|
||||
has configStorage => (
|
||||
|
@ -116,7 +114,6 @@ has exportedAttr => (
|
|||
}
|
||||
}
|
||||
);
|
||||
has captcha => ( is => 'rw' );
|
||||
has ott => (
|
||||
is => 'rw',
|
||||
lazy => 1,
|
||||
|
@ -134,13 +131,6 @@ sub init {
|
|||
my ($self) = @_;
|
||||
my @parents = ('Lemonldap::NG::Portal::Main::Plugin');
|
||||
my $add = 0;
|
||||
if ( $self->conf->{captcha_mail_enabled}
|
||||
|| $self->conf->{captcha_login_enabled}
|
||||
|| $self->conf->{captcha_register_enabled} )
|
||||
{
|
||||
$self->captcha( $self->p->loadModule('::Lib::Captcha') ) or return 0;
|
||||
$self->addUnauthRoute( renewcaptcha => 'sendCaptcha', ['GET'] );
|
||||
}
|
||||
if ( $self->conf->{restConfigServer} ) {
|
||||
push @parents, 'Lemonldap::NG::Common::Conf::RESTServer';
|
||||
$add++;
|
||||
|
@ -641,15 +631,6 @@ sub removeSessions {
|
|||
return $self->p->sendJSONresponse( $req, { result => $nbr } );
|
||||
}
|
||||
|
||||
sub sendCaptcha {
|
||||
my ( $self, $req ) = @_;
|
||||
$self->logger->info("User request for captcha renew");
|
||||
my ( $token, $image ) = $self->captcha->getCaptcha($req);
|
||||
|
||||
return $self->p->sendJSONresponse( $req,
|
||||
{ newtoken => $token, newimage => $image } );
|
||||
}
|
||||
|
||||
sub pwdReset {
|
||||
my ( $self, $req ) = @_;
|
||||
|
||||
|
|
|
@ -76,7 +76,7 @@ sub init {
|
|||
|
||||
# Initialize Captcha if needed
|
||||
if ( $self->conf->{captcha_register_enabled} ) {
|
||||
$self->captcha( $self->p->loadModule('::Lib::Captcha') ) or return 0;
|
||||
$self->captcha(1);
|
||||
}
|
||||
|
||||
# Initialize form token if needed (captcha provides also a token)
|
||||
|
@ -168,44 +168,28 @@ sub _register {
|
|||
!$self->getRegisterSession( $req->data->{registerInfo}->{mail} ) )
|
||||
{
|
||||
|
||||
# Check if token exists
|
||||
my $token;
|
||||
if ( $self->ottRule->( $req, {} ) or $self->captcha ) {
|
||||
$token = $req->param('token');
|
||||
# Captcha for register form
|
||||
if ( $self->captcha ) {
|
||||
my $result = $self->p->_captcha->check_captcha($req);
|
||||
if ($result) {
|
||||
$self->logger->debug("Captcha code verified");
|
||||
}
|
||||
else {
|
||||
$self->setSecurity($req);
|
||||
$self->userLogger->warn("Captcha failed");
|
||||
return PE_CAPTCHAERROR;
|
||||
}
|
||||
}
|
||||
elsif ( $self->ottRule->( $req, {} ) ) {
|
||||
my $token = $req->param('token');
|
||||
unless ($token) {
|
||||
$self->setSecurity($req);
|
||||
$self->userLogger->warn('Register try without token');
|
||||
return PE_NOTOKEN;
|
||||
}
|
||||
}
|
||||
|
||||
# Captcha for register form
|
||||
if ( $self->captcha ) {
|
||||
my $captcha = $req->param('captcha');
|
||||
|
||||
unless ($captcha) {
|
||||
$self->userLogger->warn(
|
||||
'Register try with captcha not filled');
|
||||
|
||||
# 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;
|
||||
}
|
||||
$self->logger->debug("Captcha code verified");
|
||||
}
|
||||
elsif ( $self->ottRule->( $req, {} ) ) {
|
||||
unless ( $self->ott->getToken($token) ) {
|
||||
$self->setSecurity($req);
|
||||
$self->userLogger->notice(
|
||||
$self->userLogger->warn(
|
||||
'Register try with expired/bad token');
|
||||
return PE_TOKENEXPIRED;
|
||||
}
|
||||
|
@ -445,7 +429,12 @@ sub display {
|
|||
STARTMAILDATE => $req->data->{startMailDate},
|
||||
STARTMAILTIME => $req->data->{startMailTime},
|
||||
MAILALREADYSENT => $req->data->{mail_already_sent},
|
||||
MAIL => $self->p->checkXSSAttack(
|
||||
(
|
||||
$req->data->{customScript}
|
||||
? ( CUSTOM_SCRIPT => $req->data->{customScript} )
|
||||
: ()
|
||||
),
|
||||
MAIL => $self->p->checkXSSAttack(
|
||||
'mail', $req->data->{registerInfo}->{mail}
|
||||
) ? ""
|
||||
: $req->data->{registerInfo}->{mail},
|
||||
|
@ -483,14 +472,19 @@ sub display {
|
|||
}
|
||||
|
||||
# Display captcha if it's enabled
|
||||
if ( $req->captcha ) {
|
||||
$templateParams{CAPTCHA_SRC} = $req->captcha;
|
||||
$templateParams{CAPTCHA_SIZE} = $self->conf->{captcha_size} || 6;
|
||||
if ( $req->captchaHtml ) {
|
||||
$templateParams{CAPTCHA_HTML} = $req->captchaHtml;
|
||||
}
|
||||
if ( $req->token ) {
|
||||
$templateParams{TOKEN} = $req->token;
|
||||
}
|
||||
|
||||
# DEPRECATED: This is only used for compatibility with existing templates
|
||||
if ( $req->captcha ) {
|
||||
$templateParams{CAPTCHA_SRC} = $req->captcha;
|
||||
$templateParams{CAPTCHA_SIZE} = $self->conf->{captcha_size};
|
||||
}
|
||||
|
||||
if ( $req->error == PE_REGISTERALREADYEXISTS ) {
|
||||
%templateParams = (
|
||||
%templateParams,
|
||||
|
@ -560,7 +554,7 @@ sub display {
|
|||
sub setSecurity {
|
||||
my ( $self, $req ) = @_;
|
||||
if ( $self->captcha ) {
|
||||
$self->captcha->setCaptcha($req);
|
||||
$self->p->_captcha->init_captcha($req);
|
||||
}
|
||||
elsif ( $self->ottRule->( $req, {} ) ) {
|
||||
$self->ott->setToken($req);
|
||||
|
|
|
@ -28,16 +28,8 @@
|
|||
<input id="mailfield" name="mail" type="text" value="<TMPL_VAR NAME="MAIL">" class="form-control" trplaceholder="mail" required />
|
||||
</div>
|
||||
|
||||
<TMPL_IF NAME=CAPTCHA_SRC>
|
||||
<div class="form-group">
|
||||
<img src="<TMPL_VAR NAME=CAPTCHA_SRC>" class="img-thumbnail" />
|
||||
</div>
|
||||
<div class="input-group mb-3">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><label for="captchafield" class="mb-0"><i class="fa fa-eye"></i></label></span>
|
||||
</div>
|
||||
<input id="captchafield" type="text" name="captcha" size="<TMPL_VAR NAME=CAPTCHA_SIZE>" class="form-control" trplaceholder="captcha" required autocomplete="off" />
|
||||
</div>
|
||||
<TMPL_IF NAME=CAPTCHA_HTML>
|
||||
<TMPL_VAR NAME=CAPTCHA_HTML>
|
||||
</TMPL_IF>
|
||||
<TMPL_IF NAME="TOKEN">
|
||||
<input type="hidden" name="token" value="<TMPL_VAR NAME="TOKEN">" />
|
||||
|
|
|
@ -15,8 +15,8 @@
|
|||
<textarea id="passwordfield" name="password" class="form-control" trplaceholder="Signed text" required aria-required="true"></textarea>
|
||||
</div>
|
||||
|
||||
<TMPL_IF NAME=CAPTCHA_SRC>
|
||||
<TMPL_INCLUDE NAME="captcha.tpl">
|
||||
<TMPL_IF NAME=CAPTCHA_HTML>
|
||||
<TMPL_VAR NAME=CAPTCHA_HTML>
|
||||
</TMPL_IF>
|
||||
<input id="token" type="hidden" name="token" value="<TMPL_VAR NAME="TOKEN">" />
|
||||
|
||||
|
|
|
@ -71,8 +71,8 @@
|
|||
<span trspan="resentConfirm">Do you want the confirmation mail to be resent?</span>
|
||||
</p>
|
||||
|
||||
<TMPL_IF NAME=CAPTCHA_SRC>
|
||||
<TMPL_INCLUDE NAME="captcha.tpl">
|
||||
<TMPL_IF NAME=CAPTCHA_HTML>
|
||||
<TMPL_VAR NAME=CAPTCHA_HTML>
|
||||
</TMPL_IF>
|
||||
|
||||
<div class="input-group mb-3">
|
||||
|
|
|
@ -40,8 +40,8 @@
|
|||
<input id="mailfield" name="mail" type="text" value="<TMPL_VAR NAME="MAIL">" class="form-control" trplaceholder="mail" required aria-required="true" autocomplete="email" />
|
||||
</div>
|
||||
|
||||
<TMPL_IF NAME=CAPTCHA_SRC>
|
||||
<TMPL_INCLUDE NAME="captcha.tpl">
|
||||
<TMPL_IF NAME=CAPTCHA_HTML>
|
||||
<TMPL_VAR NAME=CAPTCHA_HTML>
|
||||
</TMPL_IF>
|
||||
<TMPL_IF NAME="TOKEN">
|
||||
<input id="token" type="hidden" name="token" value="<TMPL_VAR NAME="TOKEN">" />
|
||||
|
|
|
@ -33,8 +33,8 @@
|
|||
</TMPL_IF>
|
||||
</div>
|
||||
|
||||
<TMPL_IF NAME=CAPTCHA_SRC>
|
||||
<TMPL_INCLUDE NAME="captcha.tpl">
|
||||
<TMPL_IF NAME=CAPTCHA_HTML>
|
||||
<TMPL_VAR NAME=CAPTCHA_HTML>
|
||||
</TMPL_IF>
|
||||
|
||||
<TMPL_INCLUDE NAME="impersonation.tpl">
|
||||
|
|
|
@ -0,0 +1,114 @@
|
|||
use Test::More;
|
||||
use strict;
|
||||
use IO::String;
|
||||
use JSON;
|
||||
use Lemonldap::NG::Portal::Main::Constants 'PE_CAPTCHAEMPTY';
|
||||
|
||||
require 't/test-lib.pm';
|
||||
|
||||
my $res;
|
||||
|
||||
my $maintests = 0;
|
||||
SKIP: {
|
||||
eval 'use GD::SecurityImage; use Image::Magick;';
|
||||
if ($@) {
|
||||
skip 'Image::Magick not found', $maintests;
|
||||
}
|
||||
|
||||
my $client = LLNG::Manager::Test->new( {
|
||||
ini => {
|
||||
logLevel => 'error',
|
||||
portalMainLogo => 'common/logos/logo_llng_old.png',
|
||||
customPlugins => 't::CaptchaOldApi',
|
||||
}
|
||||
}
|
||||
);
|
||||
my ( $data, $json );
|
||||
|
||||
# check setCaptcha
|
||||
$data = '';
|
||||
$json = expectJSON(
|
||||
$client->_post(
|
||||
'/setCaptcha',
|
||||
IO::String->new($data),
|
||||
length => length($data),
|
||||
)
|
||||
);
|
||||
like( $json->{token}, qr/.+/ );
|
||||
like( $json->{img}, qr#^data:image/png;base64,.{10}# );
|
||||
like( $json->{answer}, qr#^\d{6}$# );
|
||||
count(3);
|
||||
|
||||
# check getCaptcha
|
||||
$data = '';
|
||||
$json = expectJSON(
|
||||
$client->_post(
|
||||
'/getCaptcha',
|
||||
IO::String->new($data),
|
||||
length => length($data),
|
||||
)
|
||||
);
|
||||
like( $json->{token}, qr/.+/ );
|
||||
like( $json->{img}, qr#^data:image/png;base64,.{10}# );
|
||||
like( $json->{answer}, qr#^\d{6}$# );
|
||||
count(3);
|
||||
|
||||
my $token = $json->{token};
|
||||
my $answer = $json->{answer};
|
||||
|
||||
# validate: wrong token
|
||||
$data = buildForm( { token => 111, answer => $answer } );
|
||||
$json = expectJSON(
|
||||
$client->_post(
|
||||
'/validateCaptcha', IO::String->new($data),
|
||||
length => length($data),
|
||||
)
|
||||
);
|
||||
is( $json->{result}, 0, 'Wrong token failed' );
|
||||
count(1);
|
||||
|
||||
# validate: wrong answer
|
||||
$data = buildForm( { token => $token, answer => 999 } );
|
||||
$json = expectJSON(
|
||||
$client->_post(
|
||||
'/validateCaptcha', IO::String->new($data),
|
||||
length => length($data),
|
||||
)
|
||||
);
|
||||
is( $json->{result}, 0, 'Wrong captcha failed' );
|
||||
count(1);
|
||||
|
||||
# Get Fresh token/answer pair
|
||||
$data = '';
|
||||
$json = expectJSON(
|
||||
$client->_post(
|
||||
'/getCaptcha',
|
||||
IO::String->new($data),
|
||||
length => length($data),
|
||||
)
|
||||
);
|
||||
like( $json->{token}, qr/.+/ );
|
||||
like( $json->{img}, qr#^data:image/png;base64,.{10}# );
|
||||
like( $json->{answer}, qr#^\d{6}$# );
|
||||
count(3);
|
||||
|
||||
$token = $json->{token};
|
||||
$answer = $json->{answer};
|
||||
|
||||
# validate: correct values
|
||||
$data = buildForm( { token => $token, answer => $answer } );
|
||||
$json = expectJSON(
|
||||
$client->_post(
|
||||
'/validateCaptcha', IO::String->new($data),
|
||||
length => length($data),
|
||||
)
|
||||
);
|
||||
is( $json->{result}, 1, 'Captcha successfully verified' );
|
||||
count(1);
|
||||
|
||||
}
|
||||
count($maintests);
|
||||
|
||||
clean_sessions();
|
||||
|
||||
done_testing( count() );
|
|
@ -2,7 +2,7 @@ use Test::More;
|
|||
use strict;
|
||||
use IO::String;
|
||||
use JSON;
|
||||
use Lemonldap::NG::Portal::Main::Constants 'PE_CAPTCHAEMPTY';
|
||||
use Lemonldap::NG::Portal::Main::Constants 'PE_CAPTCHAERROR';
|
||||
|
||||
require 't/test-lib.pm';
|
||||
|
||||
|
@ -42,8 +42,8 @@ SKIP: {
|
|||
my $json;
|
||||
ok( $json = eval { from_json( $res->[2]->[0] ) }, 'Response is JSON' )
|
||||
or print STDERR "$@\n" . Dumper($res);
|
||||
ok( $json->{error} == PE_CAPTCHAEMPTY, 'Response is PE_CAPTCHAEMPTY' )
|
||||
or explain( $json, "error => 77" );
|
||||
ok( $json->{error} == PE_CAPTCHAERROR, 'Response is PE_CAPTCHAERROR' )
|
||||
or explain( $json, "error => 76" );
|
||||
|
||||
# Test normal first access
|
||||
# ------------------------
|
||||
|
|
|
@ -0,0 +1,57 @@
|
|||
package t::CaptchaOldApi;
|
||||
|
||||
use Mouse;
|
||||
use Lemonldap::NG::Portal::Main::Constants;
|
||||
extends 'Lemonldap::NG::Portal::Main::Plugin';
|
||||
|
||||
has 'captcha' => ( is => 'rw' );
|
||||
|
||||
sub init {
|
||||
my $self = shift;
|
||||
|
||||
$self->addUnauthRoute( validateCaptcha => 'validateCaptcha', ['POST'] );
|
||||
$self->addUnauthRoute( setCaptcha => 'setCaptcha', ['POST'] );
|
||||
$self->addUnauthRoute( getCaptcha => 'getCaptcha', ['POST'] );
|
||||
$self->captcha( $self->p->loadModule('::Lib::Captcha') );
|
||||
return 1;
|
||||
}
|
||||
|
||||
sub setCaptcha {
|
||||
my ( $self, $req ) = @_;
|
||||
|
||||
$self->captcha->setCaptcha($req);
|
||||
|
||||
my $info = $self->captcha->ott->getToken( $req->token, 1 );
|
||||
|
||||
return $self->sendJSONresponse(
|
||||
$req,
|
||||
{
|
||||
token => $req->token,
|
||||
img => $req->captcha,
|
||||
answer => $info->{captcha}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
sub getCaptcha {
|
||||
my ( $self, $req ) = @_;
|
||||
|
||||
my ( $token, $image ) = $self->captcha->getCaptcha;
|
||||
my $info = $self->captcha->ott->getToken( $token, 1 );
|
||||
|
||||
return $self->sendJSONresponse( $req,
|
||||
{ token => $token, img => $image, answer => $info->{captcha} } );
|
||||
}
|
||||
|
||||
sub validateCaptcha {
|
||||
my ( $self, $req ) = @_;
|
||||
my $token = $req->param('token');
|
||||
my $answer = $req->param('answer');
|
||||
|
||||
my $result = $self->captcha->validateCaptcha( $token, $answer );
|
||||
|
||||
return $self->sendJSONresponse( $req, { result => $result } );
|
||||
|
||||
}
|
||||
|
||||
1;
|
Loading…
Reference in New Issue