Merge branch 'feature-new-location-2325' into v2.0

This commit is contained in:
Maxime Besson 2021-09-06 17:42:12 +02:00
commit a6bcf1b51b
43 changed files with 779 additions and 40 deletions

1
debian/control vendored
View File

@ -282,6 +282,7 @@ Depends: ${misc:Depends},
liblemonldap-ng-handler-perl (= ${binary:Version}),
libtext-unidecode-perl,
libregexp-assemble-perl,
liblist-moreutils-perl,
libemail-date-format-perl
Recommends: gsfonts,
libcrypt-openssl-bignum-perl,

View File

@ -5,4 +5,5 @@ Attacks and Protection
:maxdepth: 1
bruteforceprotection
newlocationwarning
safejail

View File

@ -0,0 +1,55 @@
|image0|
New Location Warning Plugin
===========================
Presentation
------------
This plugin allows LL::NG to send a warning message to the user's email
address when their account connects from a new location.
By default, the location is the IP address. Meaning that any connection from a
different IP address will send a warning. If this is not what you want, you can
change the way location is computed (see below).
Following steps are performed when the user logs in
#. Extract the location from session info (by default, the IP address is used)
#. Compare the current location to the previous locations saved in history
#. If it is a new location, send an email to warn the user
#. On the next login, the location will no longer be considered as new
The very first time a user logs in (empty login history), no email is sent.
Configuration
-------------
Just enable it in the Manager (section ``General Parameters`` > ``Advanced parameters`` > ``Security`` > ``New location warning``:
- **Activation**: Enable this plugin *(default: disabled)*
- **Session attribute containing location**: Indicate the session attribute you are using to store the location. You can use `ipAddr`, or a custom macro.
- **Session attribute to display**: By default, the raw value of the location session attribute is displayed in the warning email. If you want to use a different session attribute in the warning email, you can specify it here.
- **Maximum number of locations to consider**: By default, all previous value of the location are checked
- **Session mail attribute**: Session key containing mail address *(default: mail)*
- **Warning mail subject**: Subject of the email containing the warning
- **Warning mail content**: Content of the email containing the warning
.. warning::
If you use a macro instead of ``ipAddr`` as the location value, be sure to add the name of this macro to
General Parameters » Plugins » Login History » Session data to store
Otherwise, the value of the macro will not be remembered across logins
Email body variables
~~~~~~~~~~~~~~~~~~~~
Following variables are available in the Warning email body:
* ``$location``: the location value, from **Session attribute to display**
* ``$date``: the date of login
* ``$ua``: the full user agent string
.. |image0| image:: /documentation/beta.png
:width: 100px

View File

@ -285,12 +285,13 @@ Name Description
:doc:`Grant Sessions<grantsession>` Rules to apply before allowing a user to open a session
:doc:`Impersonation<impersonation>` [11]_\ |new| Allow users to use another identity
:doc:`Find user<finduser>` [12]_\ |new| Search for user account
:doc:`Notifications system<notifications>` DIsplay a message during log in process
:doc:`NewLocationWarning<newlocationwarning>` [13]_\ |beta| Send an email when user sign in from a new location
:doc:`Notifications system<notifications>` Display a message during log in process
:doc:`Portal Status<status>` Experimental portal status page
:doc:`Public pages<public_pages>` Enable public pages system
:doc:`Refresh session API<refreshsessionapi>` [13]_ Plugin that provides an API to refresh a user session
:doc:`Refresh session API<refreshsessionapi>` [14]_ Plugin that provides an API to refresh a user session
:doc:`Reset password by mail<resetpassword>` Send a mail to reset its password
:doc:`Reset certificate by mail<resetcertificate>` [14]_\ |new| Allow users to reset their certificate
:doc:`Reset certificate by mail<resetcertificate>` [15]_\ |new| Allow users to reset their certificate
:doc:`REST services<restservices>` |new| REST server for :doc:`Proxy<authproxy>`
:doc:`SOAP services<soapservices>` |deprecated| SOAP server for :doc:`Proxy<authproxy>`
:doc:`Stay connected<stayconnected>` |new| Enable persistent connection on same browser
@ -308,12 +309,12 @@ Handlers are software control agents to be installed on your web servers
==================================================================== ========== ============================================================= =========================================== ================================================================================== =============================================== ======================================================================================================================
Handler type Apache LLNG FastCGI/uWSGI server (Nginx, or :doc:`SSOaaS<ssoaas>`) `Plack servers <https://plackperl.org>`__ Node.js ( `express apps <http://expressjs.com/>`__\ or :doc:`SSOaaS<ssoaas>`) :doc:`Self protected apps<selfmadeapplication>` Comment
==================================================================== ========== ============================================================= =========================================== ================================================================================== =============================================== ======================================================================================================================
Main *(default handler)* ✔ ✔ ✔ :doc:`Partial<nodehandler>` ** [15]_ ** ✔
Main *(default handler)* ✔ ✔ ✔ :doc:`Partial<nodehandler>` ** [16]_ ** ✔
:doc:`AuthBasic<handlerauthbasic>` ✔ ✔ ✔ ✔ Designed for some server-to-server applications
:doc:`CDA<cda>` ✔ ✔ ✔ ✔ For Cross Domain Authentication
:doc:`DevOps<devopshandler>` (:doc:`SSOaaS<ssoaas>`) |new| ✔ ✔ ✔ ✔ Allows application developers to define their own rules and headers inside their applications
:doc:`DevOpsST<devopssthandler>` (:doc:`SSOaaS<ssoaas>`) |new| ✔ ✔ ✔ ✔ Enables both :doc:`DevOps<devopshandler>` and :doc:`Service Token<servertoserver>`
:doc:`OAuth2<oauth2handler>` [16]_\ |new| ✔ ✔ ✔ ✔ Uses OpenID Connect/OAuth2 access token to check authentication and authorization, can be used to protect Web Services
:doc:`OAuth2<oauth2handler>` [17]_\ |new| ✔ ✔ ✔ ✔ Uses OpenID Connect/OAuth2 access token to check authentication and authorization, can be used to protect Web Services
:doc:`Secure Token<securetoken>` ✔ ✔ ✔ Designed to secure exchanges between a LLNG reverse-proxy and a remote app
:doc:`Service Token<servertoserver>` |new| *(Server-to-Server)* ✔ ✔ ✔ ✔ ✔ Designed to permit underlying requests *(API-Based Infrastructure)*
:doc:`Zimbra PreAuth<applications/zimbra>` ✔ ✔ ✔
@ -598,18 +599,22 @@ by your language code):
2.0.11
.. [13]
:doc:`NewLocationWarning<newlocationwarning>` is available
with LLNG ≥ 2.0.14
.. [14]
:doc:`Refresh session API plugin<refreshsessionapi>` is available
with LLNG ≥ 2.0.7
.. [14]
.. [15]
:doc:`Reset certificate by mail plugin<resetcertificate>` is
available with LLNG ≥ 2.0.7
.. [15]
.. [16]
:doc:`Node.js handler<nodehandler>` has not yet reached the same
level of functionalities
.. [16]
.. [17]
:doc:`OAuth2 Handler<oauth2handler>` is available with LLNG ≥ 2.0.4
.. |image0| image:: /icons/kthememgr.png

View File

@ -1,4 +1,4 @@
.\" Automatically generated by Pod::Man 4.11 (Pod::Simple 3.35)
.\" Automatically generated by Pod::Man 4.14 (Pod::Simple 3.40)
.\"
.\" Standard preamble:
.\" ========================================================================
@ -133,7 +133,7 @@
.\" ========================================================================
.\"
.IX Title "llng-fastcgi-server 8"
.TH llng-fastcgi-server 8 "2021-07-03" "perl v5.30.0" "User Contributed Perl Documentation"
.TH llng-fastcgi-server 8 "2021-08-10" "perl v5.32.1" "User Contributed Perl Documentation"
.\" For nroff, turn off justification. Always turn off hyphenation; it makes
.\" way too many mistakes in technical documents.
.if n .ad l

View File

@ -31,7 +31,7 @@ use constant DEFAULTCONFBACKENDOPTIONS => (
);
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 $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)|f(?:RemovedUseNotif|OnlyUpgrade)|kip(?:Upgrade|Renew)Confirmation|oap(?:Session|Config)Server|t(?:ayConnecte|orePasswor)d|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)|o(?:ntextSwitching(?:Allowed2fModifications|StopWithLogout)|mpactConf|rsEnabled)|heck(?:DevOps(?:D(?:isplayNormalizedHeaders|ownload))?|State|User|XSS)|rowdsec|da)|p(?:ortal(?:Display(?:Re(?:freshMyRights|setPassword|gister)|CertificateResetByMail|GeneratePassword|PasswordPolicy)|E(?:rrorOn(?:ExpiredSession|MailNotFound)|nablePasswordDisplay)|(?:CheckLogin|Statu)s|OpenLinkInNewWindow|ForceAuthn|AntiFrame)|roxyUseSoap)|l(?:dap(?:(?:G(?:roup(?:DecodeSearchedValu|Recursiv)|etUserBeforePasswordChang)|UsePasswordResetAttribut)e|(?:AllowResetExpired|Set)Password|ChangePasswordAsUser|PpolicyControl|ITDS)|oginHistoryEnabled)|no(?:tif(?:ication(?:Server(?:(?:POS|GE)T|DELETE)?|sExplorer)?|y(?:Deleted|Other))|AjaxHook)|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)|(?:mai(?:lOnPasswordChang|ntenanc)|vhostMaintenanc)e|to(?:tp2fUserCanRemoveKey|kenUseGlobalStorage)|g(?:roupsBeforeMacros|lobalLogoutTimer)|a(?:voidAssignment|ctiveTimer)|h(?:ideOldPassword|ttpOnly)|yubikey2fUserCanRemoveKey|krb(?:RemoveDomain|ByJs)|(?:wsdlServ|findUs)er)$/;
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)|f(?:RemovedUseNotif|OnlyUpgrade)|kip(?:Upgrade|Renew)Confirmation|oap(?:Session|Config)Server|t(?:ayConnecte|orePasswor)d|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)|o(?:ntextSwitching(?:Allowed2fModifications|StopWithLogout)|mpactConf|rsEnabled)|heck(?:DevOps(?:D(?:isplayNormalizedHeaders|ownload))?|State|User|XSS)|rowdsec|da)|p(?:ortal(?:Display(?:Re(?:freshMyRights|setPassword|gister)|CertificateResetByMail|GeneratePassword|PasswordPolicy)|E(?:rrorOn(?:ExpiredSession|MailNotFound)|nablePasswordDisplay)|(?:CheckLogin|Statu)s|OpenLinkInNewWindow|ForceAuthn|AntiFrame)|roxyUseSoap)|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)|(?:mai(?:lOnPasswordChang|ntenanc)|vhostMaintenanc)e|to(?:tp2fUserCanRemoveKey|kenUseGlobalStorage)|g(?:roupsBeforeMacros|lobalLogoutTimer)|a(?:voidAssignment|ctiveTimer)|h(?:ideOldPassword|ttpOnly)|yubikey2fUserCanRemoveKey|krb(?:RemoveDomain|ByJs)|(?:wsdlServ|findUs)er)$/;
our @sessionTypes = ( 'remoteGlobal', 'global', 'localSession', 'persistent', 'saml', 'oidc', 'cas' );

View File

@ -186,8 +186,11 @@ sub defaultValues {
'multiValuesSeparator' => '; ',
'mySessionAuthorizedRWKeys' =>
[ '_appsListOrder', '_oidcConnectedRP', '_oidcConsents' ],
'notificationDefaultCond' => '',
'notificationServerPOST' => 1,
'newLocationWarningLocationAttribute' => 'ipAddr',
'newLocationWarningLocationDisplayAttribute' => '',
'newLocationWarningMaxValues' => '0',
'notificationDefaultCond' => '',
'notificationServerPOST' => 1,
'notificationServerSentAttributes' =>
'uid reference date title subtitle text check',
'notificationsMaxRetrieve' => 3,

View File

@ -2067,6 +2067,31 @@ m[^(?:ldapi://[^/]*/?|\w[\w\-\.]*(?::\d{1,5})?|ldap(?:s|\+tls)?://\w[\w\-\.]*(?:
[ '_appsListOrder', '_oidcConnectedRP', '_oidcConsents' ],
'type' => 'array'
},
'newLocationWarning' => {
'default' => 0,
'type' => 'bool'
},
'newLocationWarningLocationAttribute' => {
'default' => 'ipAddr',
'type' => 'text'
},
'newLocationWarningLocationDisplayAttribute' => {
'default' => '',
'type' => 'text'
},
'newLocationWarningMailAttribute' => {
'type' => 'text'
},
'newLocationWarningMailBody' => {
'type' => 'longtext'
},
'newLocationWarningMailSubject' => {
'type' => 'text'
},
'newLocationWarningMaxValues' => {
'default' => '0',
'type' => 'int'
},
'nginxCustomHandlers' => {
'keyTest' => qr/^\w+$/,
'msgFail' => '__badPerlPackageName__',

View File

@ -579,6 +579,38 @@ sub attributes {
default => '^[*\w]+$',
documentation => 'Regular expression to validate parameters',
},
newLocationWarning => {
default => 0,
type => 'bool',
documentation => 'Enable New Location Warning',
},
newLocationWarningLocationAttribute => {
type => 'text',
default => 'ipAddr',
documentation => 'New location session attribute',
},
newLocationWarningLocationDisplayAttribute => {
type => 'text',
default => '',
documentation => 'New location session attribute for user display',
},
newLocationWarningMaxValues => {
type => 'int',
default => '0',
documentation => 'How many previous locations should be compared',
},
newLocationWarningMailAttribute => {
type => 'text',
documentation => 'New location warning mail session attribute',
},
newLocationWarningMailBody => {
type => 'longtext',
documentation => 'Mail body for new location warning',
},
newLocationWarningMailSubject=> {
type => 'text',
documentation => 'Mail subject for new location warning',
},
globalLogoutRule => {
type => 'boolOrExpr',
default => 0,

View File

@ -1038,11 +1038,26 @@ sub tree {
{
title => 'CrowdSecPlugin',
help => 'crowdsec.html',
form => 'simpleInputContainer',
nodes => [
'crowdsec', 'crowdsecAction',
'crowdsecUrl', 'crowdsecKey',
],
},
{
title => 'newLocationWarnings',
help => 'newlocationwarning.html',
form => 'simpleInputContainer',
nodes => [
'newLocationWarning',
'newLocationWarningLocationAttribute',
'newLocationWarningLocationDisplayAttribute',
'newLocationWarningMaxValues',
'newLocationWarningMailAttribute',
'newLocationWarningMailSubject',
'newLocationWarningMailBody'
]
},
{
title => 'bruteForceAttackProtection',
help => 'bruteforceprotection.html',

View File

@ -180,8 +180,8 @@
"cfgLog":"Summary",
"cfgVersion":"عملية ضبط الإصدارات",
"checkDevOps":"تفعيل",
"checkDevOpsDownload":"Download file",
"checkDevOpsDisplayNormalizedHeaders":"Display normalized headers",
"checkDevOpsDownload":"Download file",
"checkState":"تفعيل",
"checkStateSecret":"سر مشترك",
"checkUser":"تفعيل",
@ -568,6 +568,14 @@
"newEntry":"أنتري جديد",
"newGrantRule":"قاعدة منح جديدة",
"newHost":"خادم جديد",
"newLocationWarning":"Activation",
"newLocationWarningLocationAttribute":"Session attribute containing location",
"newLocationWarningLocationDisplayAttribute":"Session attribute to display",
"newLocationWarningMailAttribute":"Session mail attribute",
"newLocationWarningMailBody":"Warning mail content",
"newLocationWarningMailSubject":"Warning mail subject",
"newLocationWarningMaxValues":"Maximum number of locations to consider",
"newLocationWarnings":"New location warning",
"newPost":"استمارة وظيفة replay جديدة",
"newPostVar":"متغير جديد",
"newRSAKey":"مفاتيح جديدة",
@ -1210,4 +1218,4 @@
"yubikey2fUrl":"خدمة أل يو أر ل",
"yubikey2fUserCanRemoveKey":"Allow user to remove Yubikey",
"zeroConfExplanations":"لا يحتوي الخادم على إعدادات. استخدام قالب لحفظ الأول"
}
}

View File

@ -180,8 +180,8 @@
"cfgLog":"Summary",
"cfgVersion":"Configuration version",
"checkDevOps":"Activation",
"checkDevOpsDownload":"Download file",
"checkDevOpsDisplayNormalizedHeaders":"Display normalized headers",
"checkDevOpsDownload":"Download file",
"checkState":"Activation",
"checkStateSecret":"Shared secret",
"checkUser":"Activation",
@ -568,6 +568,14 @@
"newEntry":"New entry",
"newGrantRule":"New grant rule",
"newHost":"New host",
"newLocationWarning":"Activation",
"newLocationWarningLocationAttribute":"Session attribute containing location",
"newLocationWarningLocationDisplayAttribute":"Session attribute to display",
"newLocationWarningMailAttribute":"Session mail attribute",
"newLocationWarningMailBody":"Warning mail content",
"newLocationWarningMailSubject":"Warning mail subject",
"newLocationWarningMaxValues":"Maximum number of locations to consider",
"newLocationWarnings":"New location warning",
"newPost":"New form replay",
"newPostVar":"New variable",
"newRSAKey":"New keys",
@ -1210,4 +1218,4 @@
"yubikey2fUrl":"Service URL",
"yubikey2fUserCanRemoveKey":"Allow user to remove Yubikey",
"zeroConfExplanations":"Server has no configuration. Use template to save the first."
}
}

View File

@ -180,8 +180,8 @@
"cfgLog":"Summary",
"cfgVersion":"Configuration version",
"checkDevOps":"Activation",
"checkDevOpsDownload":"Download file",
"checkDevOpsDisplayNormalizedHeaders":"Display normalized headers",
"checkDevOpsDownload":"Download file",
"checkState":"Activation",
"checkStateSecret":"Shared secret",
"checkUser":"Activation",
@ -568,6 +568,14 @@
"newEntry":"New entry",
"newGrantRule":"New grant rule",
"newHost":"New host",
"newLocationWarning":"Activation",
"newLocationWarningLocationAttribute":"Session attribute containing location",
"newLocationWarningLocationDisplayAttribute":"Session attribute to display",
"newLocationWarningMailAttribute":"Session mail attribute",
"newLocationWarningMailBody":"Warning mail content",
"newLocationWarningMailSubject":"Warning mail subject",
"newLocationWarningMaxValues":"Maximum number of locations to consider",
"newLocationWarnings":"New location warning",
"newPost":"New form replay",
"newPostVar":"New variable",
"newRSAKey":"New keys",

View File

@ -180,8 +180,8 @@
"cfgLog":"Sumario",
"cfgVersion":"Configuration version",
"checkDevOps":"Activación",
"checkDevOpsDownload":"Download file",
"checkDevOpsDisplayNormalizedHeaders":"Display normalized headers",
"checkDevOpsDownload":"Download file",
"checkState":"Activación",
"checkStateSecret":"Secreto compartido",
"checkUser":"Activación",
@ -568,6 +568,14 @@
"newEntry":"Nueva entrada",
"newGrantRule":"Nueva regla de admisión",
"newHost":"Nuevo host",
"newLocationWarning":"Activation",
"newLocationWarningLocationAttribute":"Session attribute containing location",
"newLocationWarningLocationDisplayAttribute":"Session attribute to display",
"newLocationWarningMailAttribute":"Session mail attribute",
"newLocationWarningMailBody":"Warning mail content",
"newLocationWarningMailSubject":"Warning mail subject",
"newLocationWarningMaxValues":"Maximum number of locations to consider",
"newLocationWarnings":"New location warning",
"newPost":"New form replay",
"newPostVar":"Nueva variable",
"newRSAKey":"Nuevas claves",
@ -1210,4 +1218,4 @@
"yubikey2fUrl":"Service URL",
"yubikey2fUserCanRemoveKey":"Allow user to remove Yubikey",
"zeroConfExplanations":"Server has no configuration. Use template to save the first."
}
}

View File

@ -180,8 +180,8 @@
"cfgLog":"Résumé",
"cfgVersion":"Version de la configuration",
"checkDevOps":"Activation",
"checkDevOpsDownload":"Télécharger un fichier",
"checkDevOpsDisplayNormalizedHeaders":"Afficher les entêtes normalisés",
"checkDevOpsDownload":"Télécharger un fichier",
"checkState":"Activation",
"checkStateSecret":"Secret partagé",
"checkUser":"Activation",
@ -568,6 +568,14 @@
"newEntry":"Nouvelle entrée",
"newGrantRule":"Nouvelle règle d'accès",
"newHost":"Nouvel hôte",
"newLocationWarning":"Activation",
"newLocationWarningLocationAttribute":"Attribut de session contenant la localisation",
"newLocationWarningLocationDisplayAttribute":"Attribut de session à afficher",
"newLocationWarningMailAttribute":"Attribut utilisateur contenant le mail ",
"newLocationWarningMailBody":"Contenu du mail d'avertissement",
"newLocationWarningMailSubject":"Sujet du mail d'avertissement",
"newLocationWarningMaxValues":"Nombre maximum de localisations à mémoriser",
"newLocationWarnings":"Avertissement de nouvelle connexion",
"newPost":"Nouveau rejeu de formulaire",
"newPostVar":"Nouvelle variable",
"newRSAKey":"Nouvelles clefs",

View File

@ -180,8 +180,8 @@
"cfgLog":"Summary",
"cfgVersion":"Versione configurazione",
"checkDevOps":"Activation",
"checkDevOpsDownload":"Download file",
"checkDevOpsDisplayNormalizedHeaders":"Display normalized headers",
"checkDevOpsDownload":"Download file",
"checkState":"Attivazione",
"checkStateSecret":"Segreto condiviso",
"checkUser":"Attivazione",
@ -568,6 +568,14 @@
"newEntry":"Nuova entrata",
"newGrantRule":"Nuova regola di autorizzazione",
"newHost":"Nuovo host",
"newLocationWarning":"Activation",
"newLocationWarningLocationAttribute":"Session attribute containing location",
"newLocationWarningLocationDisplayAttribute":"Session attribute to display",
"newLocationWarningMailAttribute":"Session mail attribute",
"newLocationWarningMailBody":"Warning mail content",
"newLocationWarningMailSubject":"Warning mail subject",
"newLocationWarningMaxValues":"Maximum number of locations to consider",
"newLocationWarnings":"New location warning",
"newPost":"Nuovo formulario di risposta",
"newPostVar":"Nuova variabile",
"newRSAKey":"Nuove chiavi",
@ -1210,4 +1218,4 @@
"yubikey2fUrl":"URL del servizio",
"yubikey2fUserCanRemoveKey":"Autorizza l'utente a rimuovere la Yubikey",
"zeroConfExplanations":"Il server non ha alcuna configurazione. Utilizza il modello per salvare il primo."
}
}

View File

@ -180,8 +180,8 @@
"cfgLog":"Podsumowanie",
"cfgVersion":"Wersja konfiguracji",
"checkDevOps":"Aktywacja",
"checkDevOpsDownload":"Download file",
"checkDevOpsDisplayNormalizedHeaders":"Display normalized headers",
"checkDevOpsDownload":"Download file",
"checkState":"Aktywacja",
"checkStateSecret":"Współdzielony sekret",
"checkUser":"Aktywacja",
@ -568,6 +568,14 @@
"newEntry":"Nowy wpis",
"newGrantRule":"Nowa reguła przyznawania",
"newHost":"Nowy host",
"newLocationWarning":"Activation",
"newLocationWarningLocationAttribute":"Session attribute containing location",
"newLocationWarningLocationDisplayAttribute":"Session attribute to display",
"newLocationWarningMailAttribute":"Session mail attribute",
"newLocationWarningMailBody":"Warning mail content",
"newLocationWarningMailSubject":"Warning mail subject",
"newLocationWarningMaxValues":"Maximum number of locations to consider",
"newLocationWarnings":"New location warning",
"newPost":"Nowy formularz powtórzenia",
"newPostVar":"Nowa zmienna",
"newRSAKey":"Nowe klucze",
@ -1210,4 +1218,4 @@
"yubikey2fUrl":"URL usługi",
"yubikey2fUserCanRemoveKey":"Pozwól użytkownikowi usunąć Yubikey",
"zeroConfExplanations":"Serwer nie ma konfiguracji. Użyj szablonu, aby zapisać pierwszy."
}
}

View File

@ -180,8 +180,8 @@
"cfgLog":"Özet",
"cfgVersion":"Yapılandırma sürümü",
"checkDevOps":"Aktivasyon",
"checkDevOpsDownload":"Dosyayı indir",
"checkDevOpsDisplayNormalizedHeaders":"Display normalized headers",
"checkDevOpsDownload":"Dosyayı indir",
"checkState":"Aktivasyon",
"checkStateSecret":"Paylaşılan sır",
"checkUser":"Aktivasyon",
@ -568,6 +568,14 @@
"newEntry":"Yeni kayıt",
"newGrantRule":"Yeni imtiyaz kuralı",
"newHost":"Yeni konak",
"newLocationWarning":"Activation",
"newLocationWarningLocationAttribute":"Session attribute containing location",
"newLocationWarningLocationDisplayAttribute":"Session attribute to display",
"newLocationWarningMailAttribute":"Session mail attribute",
"newLocationWarningMailBody":"Warning mail content",
"newLocationWarningMailSubject":"Warning mail subject",
"newLocationWarningMaxValues":"Maximum number of locations to consider",
"newLocationWarnings":"New location warning",
"newPost":"Yeni form tekrarı",
"newPostVar":"Yeni değişken",
"newRSAKey":"Yeni anahtarlar",
@ -1210,4 +1218,4 @@
"yubikey2fUrl":"Servis URL'si",
"yubikey2fUserCanRemoveKey":"Yubikey'i kaldırmak için kullanıcıya izin ver",
"zeroConfExplanations":"Sunucunun yapılandırması yok. Şimdi bir tane kaydetmek için şablonu kullanın."
}
}

View File

@ -180,8 +180,8 @@
"cfgLog":"Summary",
"cfgVersion":"Phiên bản cấu hình",
"checkDevOps":"Activation",
"checkDevOpsDownload":"Download file",
"checkDevOpsDisplayNormalizedHeaders":"Display normalized headers",
"checkDevOpsDownload":"Download file",
"checkState":"Kích hoạt",
"checkStateSecret":"Chia sẻ bí mật",
"checkUser":"Kích hoạt",
@ -568,6 +568,14 @@
"newEntry":"Mục nhập mới",
"newGrantRule":"Quy tắc cấp mới",
"newHost":"Máy chủ mới",
"newLocationWarning":"Activation",
"newLocationWarningLocationAttribute":"Session attribute containing location",
"newLocationWarningLocationDisplayAttribute":"Session attribute to display",
"newLocationWarningMailAttribute":"Session mail attribute",
"newLocationWarningMailBody":"Warning mail content",
"newLocationWarningMailSubject":"Warning mail subject",
"newLocationWarningMaxValues":"Maximum number of locations to consider",
"newLocationWarnings":"New location warning",
"newPost":"Phát lại mẫu mới",
"newPostVar":"Biến mới",
"newRSAKey":"Khóa mới",
@ -1210,4 +1218,4 @@
"yubikey2fUrl":"Dịch vụ URL",
"yubikey2fUserCanRemoveKey":"Allow user to remove Yubikey",
"zeroConfExplanations":"Máy chủ không có cấu hình. Sử dụng mẫu để lưu đầu tiên. "
}
}

View File

@ -180,8 +180,8 @@
"cfgLog":"Summary",
"cfgVersion":"配置信息",
"checkDevOps":"Activation",
"checkDevOpsDownload":"Download file",
"checkDevOpsDisplayNormalizedHeaders":"Display normalized headers",
"checkDevOpsDownload":"Download file",
"checkState":"激活",
"checkStateSecret":"Shared secret",
"checkUser":"激活",
@ -568,6 +568,14 @@
"newEntry":"New entry",
"newGrantRule":"New grant rule",
"newHost":"New host",
"newLocationWarning":"Activation",
"newLocationWarningLocationAttribute":"Session attribute containing location",
"newLocationWarningLocationDisplayAttribute":"Session attribute to display",
"newLocationWarningMailAttribute":"Session mail attribute",
"newLocationWarningMailBody":"Warning mail content",
"newLocationWarningMailSubject":"Warning mail subject",
"newLocationWarningMaxValues":"Maximum number of locations to consider",
"newLocationWarnings":"New location warning",
"newPost":"New form replay",
"newPostVar":"New variable",
"newRSAKey":"New keys",
@ -1210,4 +1218,4 @@
"yubikey2fUrl":"Service URL",
"yubikey2fUserCanRemoveKey":"Allow user to remove Yubikey",
"zeroConfExplanations":"Server has no configuration. Use template to save the first."
}
}

View File

@ -180,8 +180,8 @@
"cfgLog":"概要",
"cfgVersion":"設定版本",
"checkDevOps":"啟用",
"checkDevOpsDownload":"Download file",
"checkDevOpsDisplayNormalizedHeaders":"Display normalized headers",
"checkDevOpsDownload":"Download file",
"checkState":"啟用",
"checkStateSecret":"已分享的祕密",
"checkUser":"啟用",
@ -568,6 +568,14 @@
"newEntry":"新項目",
"newGrantRule":"新授權規則",
"newHost":"新主機",
"newLocationWarning":"Activation",
"newLocationWarningLocationAttribute":"Session attribute containing location",
"newLocationWarningLocationDisplayAttribute":"Session attribute to display",
"newLocationWarningMailAttribute":"Session mail attribute",
"newLocationWarningMailBody":"Warning mail content",
"newLocationWarningMailSubject":"Warning mail subject",
"newLocationWarningMaxValues":"Maximum number of locations to consider",
"newLocationWarnings":"New location warning",
"newPost":"新表單重新進行",
"newPostVar":"新變數",
"newRSAKey":"新金鑰",
@ -1210,4 +1218,4 @@
"yubikey2fUrl":"服務 URL",
"yubikey2fUserCanRemoveKey":"允許使用者移除 Yubikey",
"zeroConfExplanations":"伺服器未設定。使用飯本來儲存第一個。"
}
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -120,6 +120,7 @@ lib/Lemonldap/NG/Portal/Plugins/GrantSession.pm
lib/Lemonldap/NG/Portal/Plugins/History.pm
lib/Lemonldap/NG/Portal/Plugins/Impersonation.pm
lib/Lemonldap/NG/Portal/Plugins/MailPasswordReset.pm
lib/Lemonldap/NG/Portal/Plugins/NewLocationWarning.pm
lib/Lemonldap/NG/Portal/Plugins/Notifications.pm
lib/Lemonldap/NG/Portal/Plugins/PublicPages.pm
lib/Lemonldap/NG/Portal/Plugins/Refresh.pm
@ -470,6 +471,7 @@ site/templates/common/mail_certificateReset.tpl
site/templates/common/mail_confirm.tpl
site/templates/common/mail_footer.tpl
site/templates/common/mail_header.tpl
site/templates/common/mail_new_location_warning.tpl
site/templates/common/mail_password.tpl
site/templates/common/mail_register_confirm.tpl
site/templates/common/mail_register_done.tpl
@ -672,6 +674,8 @@ t/61-CrowdSec-warn.t
t/61-CrowdSec.t
t/61-ForceAuthn.t
t/61-GrantSession.t
t/61-NewLocationWarning-Custom.t
t/61-NewLocationWarning.t
t/61-Session-ActivityTimeout.t
t/61-Session-Timeout.t
t/62-Refresh-plugin.t

View File

@ -33,7 +33,8 @@ our @pList = (
contextSwitchingRule => '::Plugins::ContextSwitching',
decryptValueRule => '::Plugins::DecryptValue',
findUser => '::Plugins::FindUser',
adaptativeAuthenticationLevelRules =>
newLocationWarning => '::Plugins::NewLocationWarning',
adaptativeAuthenticationLevelRules =>
'::Plugins::AdaptativeAuthenticationLevel',
globalLogoutRule => '::Plugins::GlobalLogout',
refreshSessions => '::Plugins::Refresh',

View File

@ -0,0 +1,170 @@
package Lemonldap::NG::Portal::Plugins::NewLocationWarning;
use strict;
use Mouse;
use POSIX qw(strftime);
use Lemonldap::NG::Portal::Main::Constants qw(PE_OK);
use List::MoreUtils qw/uniq/;
our $VERSION = '2.0.14';
has locationAttribute => ( is => 'rw' );
has locationDisplayAttribute => ( is => 'rw' );
has locationMaxValues => ( is => 'rw' );
has mailSessionKey => (
is => 'rw',
lazy => 1,
default => sub {
return
$_[0]->{conf}->{newLocationWarningMailAttribute}
|| $_[0]->{conf}->{mailSessionKey}
|| 'mail';
}
);
extends qw(
Lemonldap::NG::Portal::Lib::SMTP
Lemonldap::NG::Portal::Main::Plugin
);
# Entrypoint
use constant afterSub => { setLocalGroups => 'checkNewLocation' };
use constant endAuth => 'sendWarningEmail';
sub init {
my ($self) = @_;
if ( $self->conf->{disablePersistentStorage} ) {
$self->logger->error(
'"NewLocationWarning" plugin enabled WITHOUT persistent session storage"'
);
return 0;
}
unless ( $self->conf->{loginHistoryEnabled} ) {
$self->logger->error(
'"NewLocationWarning" plugin enabled WITHOUT "History" plugin');
return 0;
}
$self->locationAttribute( $self->conf->{newLocationWarningLocationAttribute}
|| 'ipAddr' );
$self->locationDisplayAttribute(
$self->conf->{newLocationWarningLocationDisplayAttribute}
|| $self->locationAttribute );
$self->locationMaxValues( $self->conf->{newLocationWarningMaxValues} || 0 );
return 1;
}
sub checkNewLocation {
my ( $self, $req ) = @_;
my $successLogin = $req->sessionInfo->{_loginHistory}->{successLogin} || [];
my $location = $req->sessionInfo->{ $self->locationAttribute };
unless ($location) {
$self->logger->debug( "Could not find location of user " . $req->user );
}
# Get all non-empty, unique values of location attribute through list of
# successful logins
my @envHistory =
grep { $_ // "" }
uniq( map { $_->{ $self->locationAttribute } // "" } @{$successLogin} );
# Only consider some of the past unique locations
my $maxLocations = $self->locationMaxValues;
splice @envHistory, $maxLocations
if ( $maxLocations and ( scalar @envHistory > $maxLocations ) );
if ( grep { $_ eq $location } @envHistory ) {
$self->userLogger->debug(
"User " . $req->user . " logged in from known location $location" );
}
else {
# Not the first location in history, warn if new location
if (@envHistory) {
$self->userLogger->info( "User "
. $req->user
. " logged in from unknown location $location" );
my $riskLevel = ( $req->sessionInfo->{_riskLevel} || 0 ) + 1;
$req->sessionInfo->{_riskDetails}->{newLocation} =
$req->sessionInfo->{ $self->locationDisplayAttribute };
}
else {
$self->userLogger->info( "User "
. $req->user
. " logged with empty location history from location $location"
);
}
}
return PE_OK;
}
sub sendWarningEmail {
my ( $self, $req ) = @_;
if ( $req->sessionInfo->{_riskDetails}->{newLocation} ) {
return $self->_sendMail($req);
}
return PE_OK;
}
sub _sendMail {
my ( $self, $req ) = @_;
my $date = strftime( '%F %X', localtime );
my $location = $req->sessionInfo->{_riskDetails}->{newLocation};
my $ua = $req->env->{HTTP_USER_AGENT};
my $mail = $req->sessionInfo->{ $self->mailSessionKey };
# Build mail content
my $tr = $self->translate($req);
my $subject = $self->conf->{newLocationWarningMailSubject};
unless ($subject) {
$self->logger->debug('Use default warning subject');
$subject = 'newLocationWarningMailSubject';
$tr->( \$subject );
}
my ( $body, $html );
if ( $self->conf->{newLocationWarningMailBody} ) {
# We use a specific text message, no html
$self->logger->debug('Use specific warning body message');
$body = $self->conf->{newLocationWarningMailBody};
# Replace variables in body
$body =~ s/\$ua\b/$ua/ge;
$body =~ s/\$location\b/$location/ge;
$body =~ s/\$date\b/$date/ge;
$body =~ s/\$(\w+)/$req->{sessionInfo}->{$1} || ''/ge;
}
else {
# Use HTML template
$body = $self->loadMailTemplate(
$req,
'mail_new_location_warning',
filter => $tr,
params => {
location => $location,
date => $date,
ua => $ua
},
);
$html = 1;
}
if ( $mail && $subject && $body ) {
$self->logger->warn("User $mail is signing in from a new location");
# Send mail
$self->logger->debug('Unable to send new location warning mail')
unless ( $self->send_mail( $mail, $subject, $body, $html ) );
}
else {
$self->logger->error(
'Unable to send new location warning mail: missing parameter(s)');
}
return PE_OK;
}
1;

View File

@ -1 +1 @@
(function(){var r,e,s,o,t;s=function(e,r){return $("#msg").attr("trspan",e),$("#msg").html(window.translate(e)),$("#color").removeClass("message-positive message-warning message-danger alert-success alert-warning alert-danger"),$("#color").addClass("message-"+r),"positive"===r&&(r="success"),$("#color").addClass("alert-"+r)},r=function(e,r,t){var o;if(console.log("Error",t),(o=JSON.parse(e.responseText))&&o.error)return o=o.error.replace(/.* /,""),console.log("Returned error",o),s(o,"warning")},o="",e=function(){return s("yourTotpKey","warning"),$.ajax({type:"POST",url:portal+"/2fregisters/totp/getkey",dataType:"json",error:r,success:function(e){var r,t;return e.error?(e.error.match(/totpExistingKey/)&&$("#divToHide").hide(),s(e.error,"warning")):e.portal&&e.user&&e.secret?($("#divToHide").show(),r="otpauth://totp/"+escape(e.portal)+":"+escape(e.user)+"?secret="+e.secret+"&issuer="+escape(e.portal),6!==e.digits&&(r+="&digits="+e.digits),30!==e.interval&&(r+="&period="+e.interval),new QRious({element:document.getElementById("qr"),value:r,size:150}),t=e.secret||"",$("#secret").text(t.toUpperCase().replace(/(.{4})/g,"$1 ").trim()),e.newkey?s("yourNewTotpKey","warning"):s("yourTotpKey","success"),o=e.token):s("PE24","danger")}})},t=function(){var e;return(e=$("#code").val())?$.ajax({type:"POST",url:portal+"/2fregisters/totp/verify",dataType:"json",data:{token:o,code:e,TOTPName:$("#TOTPName").val()},error:r,success:function(e){return e.error?e.error.match(/bad(Code|Name)/)?s(e.error,"warning"):s(e.error,"danger"):s("yourKeyIsRegistered","success")}}):(s("totpMissingCode","warning"),$("#code").focus())},$(document).ready(function(){return e(),$("#verify").on("click",function(){return t()})})}).call(this);
!function(){var o=function(e,r){return $("#msg").attr("trspan",e),$("#msg").html(window.translate(e)),$("#color").removeClass("message-positive message-warning message-danger alert-success alert-warning alert-danger"),$("#color").addClass("message-"+r),"positive"===r&&(r="success"),$("#color").addClass("alert-"+r)},r=function(e,r,t){if(console.log("Error",t),(e=JSON.parse(e.responseText))&&e.error)return e=e.error.replace(/.* /,""),console.log("Returned error",e),o(e,"warning")},t="",e=function(){return o("yourTotpKey","warning"),$.ajax({type:"POST",url:portal+"/2fregisters/totp/getkey",dataType:"json",error:r,success:function(e){var r;return e.error?(e.error.match(/totpExistingKey/)&&$("#divToHide").hide(),o(e.error,"warning")):e.portal&&e.user&&e.secret?($("#divToHide").show(),r="otpauth://totp/"+escape(e.portal)+":"+escape(e.user)+"?secret="+e.secret+"&issuer="+escape(e.portal),6!==e.digits&&(r+="&digits="+e.digits),30!==e.interval&&(r+="&period="+e.interval),new QRious({element:document.getElementById("qr"),value:r,size:150}),r=e.secret||"",$("#secret").text(r.toUpperCase().replace(/(.{4})/g,"$1 ").trim()),e.newkey?o("yourNewTotpKey","warning"):o("yourTotpKey","success"),t=e.token):o("PE24","danger")}})},s=function(){var e=$("#code").val();return e?$.ajax({type:"POST",url:portal+"/2fregisters/totp/verify",dataType:"json",data:{token:t,code:e,TOTPName:$("#TOTPName").val()},error:r,success:function(e){return e.error?e.error.match(/bad(Code|Name)/)?o(e.error,"warning"):o(e.error,"danger"):o("yourKeyIsRegistered","success")}}):(o("totpMissingCode","warning"),$("#code").focus())};$(document).ready(function(){return e(),$("#verify").on("click",s)})}.call(this);

View File

@ -1 +1 @@
{"version":3,"sources":["totpregistration.js"],"names":["displayError","getKey","setMsg","token","verify","msg","level","$","attr","html","window","translate","removeClass","addClass","j","status","err","res","console","log","JSON","parse","responseText","error","replace","ajax","type","url","portal","dataType","success","data","s","secret","match","hide","user","show","escape","digits","interval","QRious","element","document","getElementById","value","size","text","toUpperCase","trim","newkey","val","code","TOTPName","focus","ready","on","call","this"],"mappings":"CAMA,WACE,IAAIA,EAAcC,EAAQC,EAAQC,EAAOC,EAEzCF,EAAS,SAASG,EAAKC,GAQrB,OAPAC,EAAE,QAAQC,KAAK,SAAUH,GACzBE,EAAE,QAAQE,KAAKC,OAAOC,UAAUN,IAChCE,EAAE,UAAUK,YAAY,4FACxBL,EAAE,UAAUM,SAAS,WAAaP,GACpB,aAAVA,IACFA,EAAQ,WAEHC,EAAE,UAAUM,SAAS,SAAWP,IAGzCN,EAAe,SAASc,EAAGC,EAAQC,GACjC,IAAIC,EAGJ,GAFAC,QAAQC,IAAI,QAASH,IACrBC,EAAMG,KAAKC,MAAMP,EAAEQ,gBACRL,EAAIM,MAGb,OAFAN,EAAMA,EAAIM,MAAMC,QAAQ,MAAO,IAC/BN,QAAQC,IAAI,iBAAkBF,GACvBf,EAAOe,EAAK,YAIvBd,EAAQ,GAERF,EAAS,WAEP,OADAC,EAAO,cAAe,WACfK,EAAEkB,KAAK,CACZC,KAAM,OACNC,IAAKC,OAAS,2BACdC,SAAU,OACVN,MAAOvB,EACP8B,QAAS,SAASC,GAChB,IAAQC,EAAGC,EACX,OAAIF,EAAKR,OACHQ,EAAKR,MAAMW,MAAM,oBACnB3B,EAAE,cAAc4B,OAEXjC,EAAO6B,EAAKR,MAAO,YAEtBQ,EAAKH,QAAUG,EAAKK,MAAQL,EAAKE,QAGvC1B,EAAE,cAAc8B,OAChBL,EAAI,kBAAqBM,OAAOP,EAAKH,QAAW,IAAOU,OAAOP,EAAKK,MAAS,WAAaL,EAAKE,OAAS,WAAcK,OAAOP,EAAKH,QAC7G,IAAhBG,EAAKQ,SACPP,GAAK,WAAaD,EAAKQ,QAEH,KAAlBR,EAAKS,WACPR,GAAK,WAAaD,EAAKS,UAEpB,IAAIC,OAAO,CACdC,QAASC,SAASC,eAAe,MACjCC,MAAOb,EACPc,KAAM,MAERb,EAASF,EAAKE,QAAU,GACxB1B,EAAE,WAAWwC,KAAKd,EAAOe,cAAcxB,QAAQ,UAAW,OAAOyB,QAC7DlB,EAAKmB,OACPhD,EAAO,iBAAkB,WAEzBA,EAAO,cAAe,WAEjBC,EAAQ4B,EAAK5B,OAtBXD,EAAO,OAAQ,cA2B9BE,EAAS,WACP,IAAI+C,EAEJ,OADAA,EAAM5C,EAAE,SAAS4C,OAKR5C,EAAEkB,KAAK,CACZC,KAAM,OACNC,IAAKC,OAAS,2BACdC,SAAU,OACVE,KAAM,CACJ5B,MAAOA,EACPiD,KAAMD,EACNE,SAAU9C,EAAE,aAAa4C,OAE3B5B,MAAOvB,EACP8B,QAAS,SAASC,GAChB,OAAIA,EAAKR,MACHQ,EAAKR,MAAMW,MAAM,kBACZhC,EAAO6B,EAAKR,MAAO,WAEnBrB,EAAO6B,EAAKR,MAAO,UAGrBrB,EAAO,sBAAuB,eArB3CA,EAAO,kBAAmB,WACnBK,EAAE,SAAS+C,UA2BtB/C,EAAEoC,UAAUY,MAAM,WAEhB,OADAtD,IACOM,EAAE,WAAWiD,GAAG,QAAS,WAC9B,OAAOpD,UAIVqD,KAAKC"}
{"version":3,"sources":["totpregistration.js"],"names":["setMsg","msg","level","$","attr","html","window","translate","removeClass","addClass","displayError","j","status","err","console","log","res","JSON","parse","responseText","error","replace","token","getKey","ajax","type","url","portal","dataType","success","data","secret","match","hide","user","show","s","escape","digits","interval","QRious","element","document","getElementById","value","size","text","toUpperCase","trim","newkey","verify","val","code","TOTPName","focus","ready","on","call","this"],"mappings":"CAMA,WACE,IAEAA,EAAS,SAASC,EAAKC,GAQrB,OAPAC,EAAE,QAAQC,KAAK,SAAUH,GACzBE,EAAE,QAAQE,KAAKC,OAAOC,UAAUN,IAChCE,EAAE,UAAUK,YAAY,4FACxBL,EAAE,UAAUM,SAAS,WAAaP,GACpB,aAAVA,IACFA,EAAQ,WAEHC,EAAE,UAAUM,SAAS,SAAWP,IAGzCQ,EAAe,SAASC,EAAGC,EAAQC,GAIjC,GAFAC,QAAQC,IAAI,QAASF,IACrBG,EAAMC,KAAKC,MAAMP,EAAEQ,gBACRH,EAAII,MAGb,OAFAJ,EAAMA,EAAII,MAAMC,QAAQ,MAAO,IAC/BP,QAAQC,IAAI,iBAAkBC,GACvBhB,EAAOgB,EAAK,YAIvBM,EAAQ,GAERC,EAAS,WAEP,OADAvB,EAAO,cAAe,WACfG,EAAEqB,KAAK,CACZC,KAAM,OACNC,IAAKC,OAAS,2BACdC,SAAU,OACVR,MAAOV,EACPmB,QAAS,SAASC,GAChB,IAAWC,EACX,OAAID,EAAKV,OACHU,EAAKV,MAAMY,MAAM,oBACnB7B,EAAE,cAAc8B,OAEXjC,EAAO8B,EAAKV,MAAO,YAEtBU,EAAKH,QAAUG,EAAKI,MAAQJ,EAAKC,QAGvC5B,EAAE,cAAcgC,OAChBC,EAAI,kBAAqBC,OAAOP,EAAKH,QAAW,IAAOU,OAAOP,EAAKI,MAAS,WAAaJ,EAAKC,OAAS,WAAcM,OAAOP,EAAKH,QAC7G,IAAhBG,EAAKQ,SACPF,GAAK,WAAaN,EAAKQ,QAEH,KAAlBR,EAAKS,WACPH,GAAK,WAAaN,EAAKS,UAEpB,IAAIC,OAAO,CACdC,QAASC,SAASC,eAAe,MACjCC,MAAOR,EACPS,KAAM,MAERd,EAASD,EAAKC,QAAU,GACxB5B,EAAE,WAAW2C,KAAKf,EAAOgB,cAAc1B,QAAQ,UAAW,OAAO2B,QAC7DlB,EAAKmB,OACPjD,EAAO,iBAAkB,WAEzBA,EAAO,cAAe,WAEjBsB,EAAQQ,EAAKR,OAtBXtB,EAAO,OAAQ,cA2B9BkD,EAAS,WACP,IACAC,EAAMhD,EAAE,SAASgD,MACjB,OAAKA,EAIIhD,EAAEqB,KAAK,CACZC,KAAM,OACNC,IAAKC,OAAS,2BACdC,SAAU,OACVE,KAAM,CACJR,MAAOA,EACP8B,KAAMD,EACNE,SAAUlD,EAAE,aAAagD,OAE3B/B,MAAOV,EACPmB,QAAS,SAASC,GAChB,OAAIA,EAAKV,MACHU,EAAKV,MAAMY,MAAM,kBACZhC,EAAO8B,EAAKV,MAAO,WAEnBpB,EAAO8B,EAAKV,MAAO,UAGrBpB,EAAO,sBAAuB,eArB3CA,EAAO,kBAAmB,WACnBG,EAAE,SAASmD,UA2BtBnD,EAAEuC,UAAUa,MAAM,WAEhB,OADAhC,IACOpB,EAAE,WAAWqD,GAAG,QACdN,MAIVO,KAAKC"}

View File

@ -6,15 +6,20 @@
"click2Reset":"انقر هنا لإعادة تعيين كلمة المرور",
"goToPortal":"انتقل إلى البوابة",
"hello":"مرحبا ",
"host":"Host",
"location": "Location",
"mail2fSubject":"[LemonLDAP::NG] تسجيل الدخول الخاص بك هو ",
"mailConfirmSubject":"تأكيد إعادة تعيين كلمة المرور[LemonLDAP::NG]",
"mailSubject":"كلمة المرور الجديدة [LemonLDAP::NG]",
"newLocationWarningMailBody":"Your account was signed in to from a new location.",
"newLocationWarningMailSubject":"[LemonLDAP::NG] Sign-in from a new location",
"newPwdIs":"كلمة المرور الجديدة هي",
"pwdChanged":"تم تغيير كلمة المرور الخاصة بك",
"pwdIs":"كلمة المرور الخاصة بك هي",
"registerConfirmSubject":"تأكيد تسجيل الحساب[LemonLDAP::NG] ",
"registerDoneSubject":"حسابك الجديد[LemonLDAP::NG]",
"requestIssuedFromIP":"الطلب قد أرسل من عنوان الآي بي",
"date":"Date",
"yourLoginCodeIs":"Your login code is",
"yourLoginIs":"تسجيل الدخول الخاص بك هو"
}

View File

@ -6,15 +6,20 @@
"click2Reset":"Click here to reset your password",
"goToPortal":"Go to portal",
"hello":"Hello",
"host":"Host",
"location": "Location",
"mail2fSubject":"[LemonLDAP::NG] Your login code",
"mailConfirmSubject": "[LemonLDAP::NG] Password reset confirmation",
"mailSubject": "[LemonLDAP::NG] Your new password",
"newLocationWarningMailBody":"Your account was signed in to from a new location.",
"newLocationWarningMailSubject":"[LemonLDAP::NG] Sign-in from a new location",
"newPwdIs":"Your new password is",
"pwdChanged":"Your password was changed.",
"pwdIs":"Your password is",
"registerConfirmSubject": "[LemonLDAP::NG] Account register confirmation",
"registerDoneSubject": "[LemonLDAP::NG] Your new account",
"requestIssuedFromIP":"The request was issued from IP",
"date":"Date",
"yourLoginCodeIs":"Your login code is",
"yourLoginIs":"Your login is"
}

View File

@ -6,15 +6,20 @@
"click2Reset":"Haga clic para reiniciar su contraseña",
"goToPortal":"Ir al portal",
"hello":"Hola",
"host":"Host",
"location": "Location",
"mail2fSubject":"[LemonLDAP::NG] Su código de acceso",
"mailConfirmSubject":"[LemonLDAP::NG] Confirmación de reinicio de contraseña",
"mailSubject":"[LemonLDAP::NG] Su nueva contraseña",
"newLocationWarningMailBody":"Your account was signed in to from a new location.",
"newLocationWarningMailSubject":"[LemonLDAP::NG] Sign-in from a new location",
"newPwdIs":"Su nueva contraseña es",
"pwdChanged":"Su contraseña ha sido cambiada.",
"pwdIs":"Su contraseña es",
"registerConfirmSubject":"[LemonLDAP::NG] Confirmación de creación de cuenta",
"registerDoneSubject":"[LemonLDAP::NG] Su nueva cuenta",
"requestIssuedFromIP":"El pedido fue emitido desde la IP",
"date":"Date",
"yourLoginCodeIs":"Su código de acceso es ",
"yourLoginIs":"Su nombre de usuario es"
}

View File

@ -6,15 +6,20 @@
"click2Reset":"Klikkaa tästä nollataksesi salasanasi",
"goToPortal":"Siirry portaaliin",
"hello":"Hei",
"host":"Host",
"location":"Location",
"mail2fSubject":"[LemonLDAP::NG] Your login code",
"mailConfirmSubject":"[LemonLDAP::NG] Salasanan nollauksen vahvistus",
"mailSubject":"[LemonLDAP::NG] Uusi salasanasi",
"newLocationWarningMailBody":"Your account was signed in to from a new location.",
"newLocationWarningMailSubject":"[LemonLDAP::NG] Sign-in from a new location",
"newPwdIs":"Uusi salasanasi on",
"pwdChanged":"Salasanasi on vaihdettu.",
"pwdIs":"Sinun salasanasi on",
"registerConfirmSubject":"[LemonLDAP::NG] Tunnuksen rekisteröinnin vahvistus",
"registerDoneSubject":"[LemonLDAP::NG] Uusi käyttäjätunnuksesi",
"requestIssuedFromIP":"The request was issued from IP",
"date":"Date",
"yourLoginCodeIs":"Your login code is",
"yourLoginIs":"Your login is"
}

View File

@ -6,15 +6,20 @@
"click2Reset":"Cliquez ici pour réinitialiser votre mot de passe",
"goToPortal":"Aller au portail",
"hello":"Bonjour",
"host":"Hôte",
"location":"Localisation",
"mail2fSubject":"[LemonLDAP::NG] Votre code de connexion",
"mailConfirmSubject": "[LemonLDAP::NG] Confirmation de réinitialisation de mot de passe",
"mailSubject": "[LemonLDAP::NG] Votre nouveau mot de passe",
"newLocationWarningMailBody":"Une nouvelle connexion à votre compte a été détectée",
"newLocationWarningMailSubject":"[LemonLDAP::NG] Nouvelle connexion à votre compte",
"newPwdIs":"Votre nouveau mot de passe est",
"pwdChanged":"Votre mot de passe a été changé.",
"pwdIs":"Votre mot de passe est",
"registerConfirmSubject": "[LemonLDAP::NG] Confirmation d'enregistrement de compte",
"registerDoneSubject": "[LemonLDAP::NG] Votre nouveau compte",
"requestIssuedFromIP":"La demande provient de l'IP",
"date":"Date",
"yourLoginCodeIs":"Votre code de connexion est",
"yourLoginIs":"Votre identifiant est"
}

View File

@ -6,15 +6,20 @@
"click2Reset":"Clicca qui per reimpostare la password",
"goToPortal":"Vai al portale",
"hello":"Salve",
"host":"Host",
"location":"Location",
"mail2fSubject":"[LemonLDAP :: NG] Il tuo codice di accesso",
"mailConfirmSubject":"Conferma reimpostazione password [LemonLDAP::NG] ",
"mailSubject":"[LemonLDAP::NG] La tua nuova password",
"newLocationWarningMailBody":"Your account was signed in to from a new location.",
"newLocationWarningMailSubject":"[LemonLDAP::NG] Sign-in from a new location",
"newPwdIs":"La tua nuova password é",
"pwdChanged":"La tua password é stata cambiata",
"pwdIs":"La tua password é",
"registerConfirmSubject":"[LemonLDAP :: NG] Conferma registro account",
"registerDoneSubject":"[LemonLDAP::NG] Il tuo nuovo account",
"requestIssuedFromIP":"La richiesta è stata emessa da IP",
"date":"Date",
"yourLoginCodeIs":"Il tuo codice di accesso è",
"yourLoginIs":"Il tuo login é"
}

View File

@ -6,15 +6,20 @@
"click2Reset":"Kilik disini untuk menetapkan semula kata laluan anda",
"goToPortal":"Go to portal",
"hello":"Hello",
"host":"Host",
"location":"Location",
"mail2fSubject":"[LemonLDAP::NG] Kod login anda",
"mailConfirmSubject":"[LemonLDAP::NG] Pengesahan penetapan semula kata laluan",
"mailSubject":"[LemonLDAP::NG] Kata laluan baru anda",
"newLocationWarningMailBody":"Your account was signed in to from a new location.",
"newLocationWarningMailSubject":"[LemonLDAP::NG] Sign-in from a new location",
"newPwdIs":"Kata laluan baru anda ialah",
"pwdChanged":"Kata laluan anda telah ditukar",
"pwdIs":"Kata laluan anda ialah",
"registerConfirmSubject":"[LemonLDAP::NG] Pengesahan pendaftaran akaun",
"registerDoneSubject":"[LemonLDAP::NG] Akaun baru anda",
"requestIssuedFromIP":"Permintaan itu dari alamat IP",
"date":"Date",
"yourLoginCodeIs":"Kod login anda ialah",
"yourLoginIs":"Login anda ialah"
}

View File

@ -6,15 +6,20 @@
"click2Reset":"Parolanızı sıfırlamak için buraya tıklayın",
"goToPortal":"Portala git",
"hello":"Merhaba",
"host":"Host",
"location":"Location",
"mail2fSubject":"[LemonLDAP::NG] Giriş kodunuz",
"mailConfirmSubject":"[LemonLDAP::NG] Parola sıfırlama onayı",
"mailSubject":"[LemonLDAP::NG] Yeni parolanız",
"newLocationWarningMailBody":"Your account was signed in to from a new location.",
"newLocationWarningMailSubject":"[LemonLDAP::NG] Sign-in from a new location",
"newPwdIs":"Yeni parolanız",
"pwdChanged":"Parolanız değiştirildi.",
"pwdIs":"Parolanız",
"registerConfirmSubject":"[LemonLDAP::NG] Hesap açma onayı",
"registerDoneSubject":"[LemonLDAP::NG] Yeni hesabınız",
"requestIssuedFromIP":"İstek IP'den gönderildi",
"date":"Date",
"yourLoginCodeIs":"Giriş kodunuz",
"yourLoginIs":"Giriş bilgileriniz"
}

View File

@ -6,15 +6,20 @@
"click2Reset":"Nhấn ở đây để thiết lập lại mật khẩu của bạn",
"goToPortal":"Đi tới cổng thông tin",
"hello":"Xin chào",
"host":"Host",
"location":"Location",
"mail2fSubject":"[LemonLDAP::NG] Your login code",
"mailConfirmSubject":"[LemonLDAP::NG] Xác nhận thiết lập lại mật khẩu",
"mailSubject":"[LemonLDAP::NG] Mật khẩu mới của bạn",
"newLocationWarningMailBody":"Your account was signed in to from a new location.",
"newLocationWarningMailSubject":"[LemonLDAP::NG] Sign-in from a new location",
"newPwdIs":"Mật khẩu mới của bạn là",
"pwdChanged":"Mật khẩu của bạn đã được thay đổi.",
"pwdIs":"Mật khẩu của bạn là",
"registerConfirmSubject":"[LemonLDAP::NG] Xác nhận đăng ký tài khoản",
"registerDoneSubject":"[LemonLDAP::NG] Tài khoản mới của bạn",
"requestIssuedFromIP":"Yêu cầu được gửi đi từ địa chỉ IP",
"date":"Date",
"yourLoginCodeIs":"Your login code is",
"yourLoginIs":"Đăng nhập của bạn là"
}

View File

@ -6,15 +6,20 @@
"click2Reset":"请点击此处充值您的密码",
"goToPortal":"回到首页",
"hello":"您好",
"host":"Host",
"location":"Location",
"mail2fSubject":"[LemonLDAP::NG] Your login code",
"mailConfirmSubject":"[LemonLDAP::NG] 密码重置确认",
"mailSubject":"[LemonLDAP::NG] 您的新密码",
"newLocationWarningMailBody":"Your account was signed in to from a new location.",
"newLocationWarningMailSubject":"[LemonLDAP::NG] Sign-in from a new location",
"newPwdIs":"您的新密码是",
"pwdChanged":"您的密码已经修改",
"pwdIs":"您的密码是",
"registerConfirmSubject":"[LemonLDAP::NG] 账号注册确认",
"registerDoneSubject":"[LemonLDAP::NG] 您的新账号",
"requestIssuedFromIP":"此请求来自IP地址",
"date":"Date",
"yourLoginCodeIs":"Your login code is",
"yourLoginIs":"您登陆的账户是"
}

View File

@ -6,15 +6,20 @@
"click2Reset":"點擊此處以重設您的密碼",
"goToPortal":"回到首頁",
"hello":"您好",
"host":"Host",
"location":"Location",
"mail2fSubject":"[LemonLDAP::NG] 您的登入代碼",
"mailConfirmSubject":"[LemonLDAP::NG] 確認重設密碼",
"mailSubject":"[LemonLDAP::NG] 您的新密碼",
"newLocationWarningMailBody":"Your account was signed in to from a new location.",
"newLocationWarningMailSubject":"[LemonLDAP::NG] Sign-in from a new location",
"newPwdIs":"您的新密碼為",
"pwdChanged":"您的密碼已變更",
"pwdIs":"您的密碼為",
"registerConfirmSubject":"[LemonLDAP::NG] 確認帳號註冊",
"registerDoneSubject":"[LemonLDAP::NG] 您的新帳號",
"requestIssuedFromIP":"請求發出從 IP",
"date":"Date",
"yourLoginCodeIs":"您的登入代碼為",
"yourLoginIs":"您的登入為"
}

View File

@ -0,0 +1,12 @@
<TMPL_INCLUDE NAME="mail_header.tpl">
<p>
<span trspan="hello">Hello</span> <TMPL_VAR NAME="session_cn" ESCAPE=HTML>,<br />
<br />
<h3><span trspan="newLocationWarningMailBody">Your account was signed in to from a new location</span></h3></br>
<span trspan="location">Location</span> <b><TMPL_VAR NAME="location"></b></br>
<span trspan="date">Date</span> <b><TMPL_VAR NAME="date"></b></br>
<span trspan="UA">UA</span> <b><TMPL_VAR NAME="ua"></b></br>
</p>
<TMPL_INCLUDE NAME="mail_footer.tpl">

View File

@ -0,0 +1,79 @@
use Test::More;
use strict;
use IO::String;
use POSIX qw(locale_h);
use locale;
setlocale( LC_TIME, "C" );
BEGIN {
eval {
require 't/test-lib.pm';
require 't/smtp.pm';
};
}
my $res;
my $maintests = 5;
SKIP: {
eval 'require Email::Sender::Simple;';
if ($@) {
skip 'Missing dependencies', $maintests;
}
my $client = LLNG::Manager::Test->new( {
ini => {
logLevel => 'error',
useSafeJail => 1,
authentication => 'Demo',
userDB => 'Same',
passwordDB => 'Demo',
captcha_mail_enabled => 0,
portalMainLogo => 'common/logos/logo_llng_old.png',
newLocationWarning => 1,
loginHistoryEnabled => 1,
newLocationWarningMailSubject => 'Test new location mail',
newLocationWarningMailBody => 'Test $location $date $ua',
}
}
);
## Simple access
ok( $res = $client->_get( '/', accept => 'text/html' ), 'Get Portal', );
my ( $host, $url, $query ) =
expectForm( $res, '#', undef, 'user', 'password' );
## Authentication #1 with IP #1 (Test 1)
ok(
$res = $client->_post(
'/',
IO::String->new('user=dwho&password=dwho'),
length => 23,
accept => 'text/html',
),
'First auth query'
);
## Authentication #1 with IP #2 (Test 2)
ok(
$res = $client->_post(
'/',
IO::String->new('user=dwho&password=dwho'),
length => 23,
accept => 'text/html',
ip => '127.0.0.2',
),
'Second auth query'
);
like( subject(), qr#Test new location mail#, ' Subject found' );
like(
mail(),
qr#^Test 127\.0\.0\.2 \d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2} Mozilla/5\.0 \(VAX-4000; rv:36\.0\) Gecko/20350101 Firefox$#,
' Mail sent (IP, Date and UA found)'
);
}
count($maintests);
clean_sessions();
done_testing( count() );

View File

@ -0,0 +1,187 @@
use Test::More;
use strict;
use IO::String;
use POSIX qw(locale_h);
use locale;
setlocale( LC_TIME, "C" );
BEGIN {
eval {
require 't/test-lib.pm';
require 't/smtp.pm';
};
}
my $res;
my $maintests = 20;
SKIP: {
eval 'require Email::Sender::Simple;';
if ($@) {
skip 'Missing dependencies', $maintests;
}
my $client = LLNG::Manager::Test->new( {
ini => {
logLevel => 'error',
useSafeJail => 1,
authentication => 'Demo',
userDB => 'Same',
passwordDB => 'Demo',
captcha_mail_enabled => 0,
portalMainLogo => 'common/logos/logo_llng_old.png',
newLocationWarning => 1,
loginHistoryEnabled => 1
}
}
);
## Simple access
ok( $res = $client->_get( '/', accept => 'text/html' ), 'Get Portal', );
my ( $host, $url, $query ) =
expectForm( $res, '#', undef, 'user', 'password' );
## Authentication #1 with IP #1
clear_mail();
ok(
$res = $client->_post(
'/',
IO::String->new('user=dwho&password=dwho'),
length => 23,
accept => 'text/html',
),
'First auth query'
);
my $id = expectCookie($res);
$client->logout($id);
ok( !mail(), "First time seeing a new IP, no mail sent" );
## Authentication #2 with IP #1
clear_mail();
ok(
$res = $client->_post(
'/',
IO::String->new('user=dwho&password=dwho'),
length => 23,
accept => 'text/html',
),
'Second auth query'
);
$id = expectCookie($res);
expectRedirection( $res, 'http://auth.example.com/' );
$client->logout($id);
ok( !mail(), "Second time seeing a new IP, no mail sent" );
## Authentication #3 with IP #2
clear_mail();
ok(
$res = $client->_post(
'/',
IO::String->new('user=dwho&password=dwho'),
length => 23,
accept => 'text/html',
ip => '127.0.0.2',
),
'Third auth query'
);
$id = expectCookie($res);
expectRedirection( $res, 'http://auth.example.com/' );
$client->logout($id);
like(
mail(),
qr#<h3><span>Your account was signed in to from a new location\.</span></h3></br>
#, 'First login on a new IP, email sent'
);
## Authentication #1 with IP #3 wrong password
clear_mail();
ok(
$res = $client->_post(
'/',
IO::String->new('user=dwho&password=ohwd'),
length => 23,
accept => 'text/html',
ip => "127.0.0.3",
),
'Fourth auth query'
);
ok( $res->[2]->[0] =~ /<span trmsg="5"><\/span>/, ' Bad credential' )
or print STDERR Dumper( $res->[2]->[0] );
ok( !mail(), "Failed login with a new IP, no email sent" );
## Authentication #2 with IP #3
clear_mail();
ok(
$res = $client->_post(
'/',
IO::String->new('user=dwho&password=dwho'),
length => 23,
accept => 'text/html',
ip => '127.0.0.3',
),
'Fifth auth query'
);
$id = expectCookie($res);
expectRedirection( $res, 'http://auth.example.com/' );
like(
subject(),
qr#\[LemonLDAP::NG\] Sign-in from a new location#,
' Subject found'
);
like(
mail(),
qr#<h3><span>Your account was signed in to from a new location\.</span></h3></br>#,
' Mail sent (Wrong password)'
);
like(
mail(),
qr#<span>Location</span> <b>127.0.0.3</b>#,
' Location found in mail body'
);
like(
mail(),
qr#<span>Date</span> <b>\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}</b>#,
' Date found in mail body'
);
like(
mail(),
qr#<span>UA</span> <b>Mozilla/5\.0 \(VAX-4000; rv:36\.0\) Gecko/20350101 Firefox</b></br>#,
' UserAgent found in mail body'
);
## Authentication #3 with IP #3
clear_mail();
ok(
$res = $client->_post(
'/',
IO::String->new('user=dwho&password=dwho'),
length => 23,
accept => 'text/html',
ip => '127.0.0.3',
),
'Fifth auth query'
);
$id = expectCookie($res);
expectRedirection( $res, 'http://auth.example.com/' );
ok( !mail(), "Login on newly learned address, no email" );
## Authentication #3 with IP #1
clear_mail();
ok(
$res = $client->_post(
'/',
IO::String->new('user=dwho&password=dwho'),
length => 23,
accept => 'text/html',
),
'Fifth auth query'
);
$id = expectCookie($res);
expectRedirection( $res, 'http://auth.example.com/' );
ok( !mail(), "Login on previously learned address, no email" );
}
count($maintests);
clean_sessions();
done_testing( count() );

View File

@ -1,17 +1,23 @@
package main;
my ($mail, $mail_envelope, $mail_subject);
my ( $mail, $mail_envelope, $mail_subject );
sub mail {
return $mail;
}
sub clear_mail {
$mail = undef;
$mail_envelope = undef;
$mail_subject = undef;
}
sub envelope {
return $mail_envelope;
}
sub subject {
my $subject = ($mail_subject =~ /=\?utf-8\?B\?(.+?)\?=/)[0];
my $subject = ( $mail_subject =~ /=\?utf-8\?B\?(.+?)\?=/ )[0];
return decode_base64($subject);
}