This commit is contained in:
Clément OUDOT 2019-01-23 11:36:13 +01:00
commit eddd54383e
47 changed files with 330 additions and 204 deletions

View File

@ -412,6 +412,7 @@ prepare_test_server:
#@cp -f e2e-tests/index.* e2e-tests/conf/
@cp -f $(SRCMANAGERDIR)/site/htdocs/manager* e2e-tests/conf/manager
@cp -f $(SRCPORTALDIR)/site/htdocs/index* e2e-tests/conf/portal
@cp e2e-tests/persistent/5efe8af397fc3577e05b483aca964f1b e2e-tests/conf/persistents
@cp e2e-tests/saml-sp.xml e2e-tests/conf/site/saml-sp.xml
@cp e2e-tests/rules.json e2e-tests/conf/site/test.json
@for f in $$(find e2e-tests/conf -name '*.fcgi'); do \

3
debian/changelog vendored
View File

@ -1,3 +1,6 @@
lemonldap-ng (2.1.0) artful; urgency=medium
lemonldap-ng (2.0.1-1) unstable; urgency=medium
* New release. See changes on our website:

2
debian/control vendored
View File

@ -47,7 +47,7 @@ Build-Depends-Indep: libapache-session-perl,
libxml-libxslt-perl,
libxml-simple-perl,
perl
Standards-Version: 4.2.1
Standards-Version: 4.3.0
Vcs-Browser: https://salsa.debian.org/perl-team/modules/packages/lemonldap-ng
Vcs-Git: https://salsa.debian.org/perl-team/modules/packages/lemonldap-ng.git
Homepage: https://lemonldap-ng.org/

View File

@ -26,6 +26,8 @@ templateDir = __pwd__/lemonldap-ng-portal/site/templates
portalStatus = 1
totp2fActivation = 1
totp2fSelfRegistration = 1
captcha_mail_enabled = 0
portalDisplayResetPassword = 1
[handler]

View File

@ -164,7 +164,7 @@
"UA" : "$ENV{HTTP_USER_AGENT}",
"_whatToTrace": "$_auth eq 'SAML' ? \"$_user\\@$_idpConfKey\" : $_auth eq 'OpenIDConnect' ? \"$_user\\@$_oidcConnectedRP\" : \"$_user\""
},
"mailUrl": "http://auth.example.com:__port__/resetpwd",
"mailPwdRstUrl": "http://auth.example.com:__port__/resetpwd",
"notification": 1,
"notificationStorage": "File",
"notificationStorageOptions": {

View File

@ -0,0 +1 @@
{"_session_kind":"Persistent","_loginHistory":{"successLogin":[{"ipAddr":"127.0.0.1","_utime":1548016089}]},"_2fDevices":"[{\"type\":\"U2F\",\"_keyHandle\":\"CTPeZD3aFrNOY4yVWH4o1MKSn2aLH2OwLOWTtrQSlt_6LtUyki5nzrwBEeuxj7PRSujFZQDaMTfrEb-gr22Qfg\",\"_userKey\":\"BI1MGzKj1C9mMV8PwrYMggQXlItLBNSB19rNnFgUpLMBjAkMW8w3Sqg8s_hUGbdfdWX99duquzIzRLUtRUEvJLo\",\"name\":\"MyU2FKey\",\"epoch\":1548016193},{\"epoch\":1548016213,\"name\":\"MyYubikey\",\"_yubikey\":\"cccccchehfff\",\"type\":\"UBK\"},{\"epoch\":1548018950,\"name\":\"MyU2FKeyBlue\",\"_userKey\":\"BDEa8pQfV9agdvsX63bcwceRTXR_QvDdm5hQ5ZKQUaH4HlOi8ab4fQfl9CIACALWYm0jQcpfaRAcACiSCdwGrnI\",\"_keyHandle\":\"ZD_G6EfDv4FzttWS9RCS80SaSlRTXgtJU9r-1gInsQ4Jj1555r7nnrYhIvRfE4CTyH7NyGrt9fMnMMgByAx97Q\",\"type\":\"U2F\"}]","_session_id":"5efe8af397fc3577e05b483aca964f1b","_session_uid":"dwho","_updateTime":"20190120221550","_utime":1548016089}

View File

@ -6,17 +6,22 @@ describe('00 Lemonldap::NG', function() {
describe('Auth mechanism', function() {
it('Portal should display 11 lang flags', function() {
browser.driver.get('http://auth.example.com:' + process.env.TESTWEBSERVERPORT + '/');
browser.sleep(500);
browser.driver.findElements(by.className('langicon')).then(function(elems) {
expect(elems.length).toEqual(11);
});
browser.sleep(500);
browser.driver.findElement(by.xpath("//img[@title='en']")).click();
expect(browser.driver.findElement(by.css('[trmsg="9"]')).getText()).toEqual('Authentication required');
expect(browser.driver.findElement(by.css('[trspan="createAccount"]')).getText()).toEqual('Create an account');
expect(browser.driver.findElement(by.css('[trspan="resetPwd"]')).getText()).toEqual('Reset my password');
browser.driver.findElement(by.xpath("//img[@title='it']")).click();
expect(browser.driver.findElement(by.css('[trmsg="9"]')).getText()).toEqual('Autenticazione necessaria');
expect(browser.driver.findElement(by.css('[trspan="createAccount"]')).getText()).toEqual('Crea un account');
expect(browser.driver.findElement(by.css('[trspan="resetPwd"]')).getText()).toEqual('Reimpostare la password');
browser.driver.findElement(by.xpath("//img[@title='fr']")).click();
expect(browser.driver.findElement(by.css('[trspan="createAccount"]')).getText()).toEqual('Créer un compte');
expect(browser.driver.findElement(by.css('[trspan="resetPwd"]')).getText()).toEqual('Réinitialiser mon mot de passe');
});
it('should create an account', function() {
browser.driver.findElement(by.css('[trspan="createAccount"]')).click();
@ -29,6 +34,28 @@ describe('00 Lemonldap::NG', function() {
browser.driver.findElements(by.className('img-thumbnail')).then(function(elems) {
expect(elems.length).toEqual(1);
});
browser.driver.findElement(by.xpath("//input[@name='firstname']")).sendKeys('doctor');
browser.driver.findElement(by.xpath("//input[@name='lastname']")).sendKeys('who');
browser.driver.findElement(by.xpath("//input[@name='mail']")).sendKeys('dwho@badwolf.com');
browser.driver.findElement(by.xpath("//input[@name='captcha']")).sendKeys('1234567');
browser.driver.findElement(by.xpath("//button[@type='submit']")).click();
expect(browser.driver.findElement(by.css('[trmsg="76"]')).getText()).toEqual('Erreur dans la saisie du captcha');
browser.driver.findElement(by.css('[trspan="back2Portal"]')).click();
});
it('should reset my password', function() {
browser.driver.findElement(by.css('[trspan="resetPwd"]')).click();
expect(browser.driver.findElement(by.css('[trmsg="69"]')).getText()).toEqual('Merci de saisir votre adresse mail');
expect(browser.driver.findElement(by.css('[trspan="sendPwd"]')).getText()).toEqual('Envoyez-moi un lien');
// A one input form
browser.driver.findElements(by.className('input-group')).then(function(elems) {
expect(elems.length).toEqual(1);
});
browser.driver.findElement(by.xpath("//input[@name='mail']")).sendKeys('dwho@badwolf.com');
browser.driver.findElement(by.xpath("//button[@type='submit']")).click();
expect(browser.driver.findElement(by.css('[trmsg="72"]')).getText()).toEqual('Un mail de confirmation vous a été envoyé');
expect(browser.driver.findElement(by.css('[trspan="mailSent2"]')).getText()).toEqual('Un message a été envoyé à votre adresse mail.');
expect(browser.driver.findElement(by.css('[trspan="linkValidUntil"]')).getText()).toEqual("Ce message contient un lien pour réinitialiser votre mot de passe, ce lien est valide jusqu'au");
});
it('should authenticate with history', function() {
expect(browser.driver.findElement(by.css('[trspan="back2Portal"]')).getText()).toEqual('Retourner au portail');
@ -53,11 +80,11 @@ describe('00 Lemonldap::NG', function() {
expect(browser.driver.findElement(by.css('[trspan="info"]')).getText()).toEqual("Information");
});
it('should display history', function() {
// Two entries
// Three entries
browser.driver.findElements(by.xpath('//table/tbody/tr')).then(function(elems) {
expect(elems.length).toEqual(2);
expect(elems.length).toEqual(3);
});
// Expect history with one login and one failed login
// Expect history with two logins and one failed login
browser.driver.findElements(by.xpath('//form/div/div/h3')).then(function(elems) {
expect(elems.length).toEqual(3);
expect(elems[0].getText()).toEqual('Information');
@ -71,10 +98,11 @@ describe('00 Lemonldap::NG', function() {
expect(elems[4].getText()).toEqual('Fehlermeldung');
});
browser.driver.findElements(by.xpath('//table/tbody/tr/td')).then(function(elems) {
expect(elems.length).toEqual(5);
expect(elems.length).toEqual(7);
expect(elems[1].getText()).toEqual('127.0.0.1');
expect(elems[3].getText()).toEqual('127.0.0.1');
expect(elems[4].getText()).toEqual('Benutzername oder Passwort nicht korrekt');
expect(elems[5].getText()).toEqual('127.0.0.1');
expect(elems[6].getText()).toEqual('Benutzername oder Passwort nicht korrekt');
});
expect(browser.driver.findElement(by.css('[trspan="PE5"]')).getText()).toEqual('Benutzername oder Passwort nicht korrekt');
expect(browser.driver.findElement(by.id('timer')).getText()).toMatch(/^Du wirst in \d{2} Sekunden umgeleitet$/);

View File

@ -1,7 +1,7 @@
'use strict';
/* http://docs.angularjs.org/guide/dev_guide.e2e-testing */
describe('01 Lemonldap::NG', function() {
describe('0 Lemonldap::NG', function() {
describe('Portal should display Menu', function() {
it('Should have four buttons', function() {
browser.driver.findElement(by.xpath("//img[@title='fr']")).click();
@ -42,10 +42,11 @@ describe('01 Lemonldap::NG', function() {
expect(elems[4].getText()).toEqual("Message d'erreur");
});
browser.driver.findElements(by.xpath('//table/tbody/tr/td')).then(function(elems) {
expect(elems.length).toEqual(5);
expect(elems.length).toEqual(7);
expect(elems[1].getText()).toEqual('127.0.0.1');
expect(elems[3].getText()).toEqual('127.0.0.1');
expect(elems[4].getText()).toEqual('Mot de passe ou identifiant incorrect');
expect(elems[5].getText()).toEqual('127.0.0.1');
expect(elems[6].getText()).toEqual('Mot de passe ou identifiant incorrect');
});
browser.driver.findElement(by.xpath("//button[@type='button']")).click();
browser.sleep(1000);

View File

@ -27,7 +27,36 @@ describe('10 Lemonldap::NG', function() {
browser.sleep(1000);
});
});
it('Should submit TOTP form', function() {
it('Should display 2FA Manager', function() {
expect(browser.driver.findElement(by.css('[trspan="choose2f"]')).getText()).toEqual('Choisissez votre second facteur');
browser.driver.findElements(by.xpath('//table/thead/tr/th')).then(function(elems) {
expect(elems.length).toEqual(4);
expect(elems[0].getText()).toEqual('Type');
expect(elems[1].getText()).toEqual('Nom');
expect(elems[2].getText()).toEqual('Date');
// expect(elems[3].getText()).toEqual('Action');
});
browser.driver.findElements(by.xpath('//table/tbody/tr/td')).then(function(elems) {
expect(elems.length).toEqual(12);
expect(elems[0].getText()).toEqual('U2F');
expect(elems[1].getText()).toEqual('MyU2FKey');
expect(elems[2].getText()).toEqual('20/01/2019 à 21:29:53');
expect(elems[4].getText()).toEqual('UBK');
expect(elems[5].getText()).toEqual('MyYubikey');
expect(elems[6].getText()).toEqual('20/01/2019 à 21:30:13');
expect(elems[8].getText()).toEqual('U2F');
expect(elems[9].getText()).toEqual('MyU2FKeyBlue');
expect(elems[10].getText()).toEqual('20/01/2019 à 22:15:50');
});
expect(browser.driver.findElement(by.className('card-footer')).getText()).toEqual('TOTP2F');
browser.driver.findElements(by.className('btn-danger')).then(function(elems) {
expect(elems.length).toEqual(0);
// elems[0].click();
});
// expect(browser.driver.findElement(by.css('[trspan="choose2f"]')).getText()).toEqual("Vous n'êtes pas autorisé à faire cette requête");
browser.driver.findElement(by.xpath("//img[@title='totp2F']")).click();
});
it('Should display and submit TOTP form', function() {
browser.driver.findElements(by.css('[role="button"]')).then(function(links) {
expect(links.length).toEqual(4);
expect(links[0].getText()).toEqual('Générer une nouvelle clef');
@ -56,7 +85,7 @@ describe('10 Lemonldap::NG', function() {
// Back to Portal
links[3].click();
browser.driver.findElement(by.xpath("//button[@type='button']")).click();
browser.sleep(1000);
browser.sleep(500);
expect(browser.driver.findElement(by.css('[trspan="yourApps"]')).getText()).toEqual('Vos applications');
});
});

View File

@ -3,9 +3,17 @@
/* http://docs.angularjs.org/guide/dev_guide.e2e-testing */
describe('99 Lemonldap::NG auth mechanism', function() {
it('should allow logout', function() {
browser.driver.get('http://auth.example.com:' + process.env.TESTWEBSERVERPORT + '/?logout=1');
browser.driver.findElements(by.xpath('//li/a/span/img')).then(function(links) {
expect(links.length).toEqual(4);
links[3].click();
browser.sleep(1000);
expect(browser.driver.findElement(by.css('[trspan="areYouSure"]')).getText()).toEqual('Êtes-vous sûr ?');
browser.driver.findElement(by.css('[trspan="imSure"]')).click();
expect(browser.driver.findElement(by.css('[trmsg="47"]')).getText()).toEqual('Vous avez été déconnecté');
browser.sleep(500);
browser.driver.findElement(by.css('[trspan="goToPortal"]')).click();
expect(browser.driver.findElement(by.css('[trmsg="9"]')).getText()).toEqual('Veuillez vous authentifier');
});
});
});

View File

@ -2,11 +2,12 @@ exports.config = {
allScriptsTimeout: 300000,
// Specific test
// specs: process.env.E2E_TESTS,
specs: process.env.E2E_TESTS,
// All tests
specs: ['portal/*.js', 'handler/*.js', 'manager/*.js' ],
//specs: ['portal/*.js', 'handler/*.js', 'manager/*.js' ],
capabilities: {
//'browserName': 'firefox'
'browserName': 'chrome'
},

View File

@ -121,9 +121,9 @@ sub defaultValues {
'macros' => {},
'mailCharset' => 'utf-8',
'mailFrom' => 'noreply@example.com',
'mailPwdRstTimeout' => 0,
'mailPwdRstUrl' => 'http://auth.example.com/resetpwd',
'mailSessionKey' => 'mail',
'mailTimeout' => 0,
'managerDn' => '',
'managerPassword' => '',
'max2FDevices' => 10,

View File

@ -16,10 +16,8 @@ BEGIN { use_ok('Lemonldap::NG::Common::Conf') }
my $h;
ok(
$h = new Lemonldap::NG::Common::Conf(
{
type => 'File',
ok( $h = new Lemonldap::NG::Common::Conf(
{ type => 'File',
dirName => "t/",
}
),
@ -39,25 +37,15 @@ my @test = (
{ cfgNum => 1, test => 'éà' }
);
for ( my $i = 0 ; $i < @test ; $i++ ) {
for ( my $i = 0; $i < @test; $i++ ) {
ok( $h->store( $test[$i] ) == 1, "Test $i is stored" )
or print STDERR "$Lemonldap::NG::Common::Conf::msg $!";
or print STDERR "$Lemonldap::NG::Common::Conf::msg $!";
$count++;
if ( -x '/usr/bin/file' ) {
eval {
open F, 'file t/lmConf-1.json |';
$_ = join( '', <F> );
close F;
ok( /(ascii|utf-?8)/si, "File is $1 encoded" )
or print STDERR "Result: $_\n";
$count++;
};
}
my $cfg;
ok( $cfg = $h->load(1), "Test $i can be read" )
or print STDERR $Lemonldap::NG::Common::Conf::msg;
or print STDERR $Lemonldap::NG::Common::Conf::msg;
ok( $cfg->{test} eq $test[$i]->{test}, "Test $i is restored" )
or print STDERR "Expect $cfg->{test} eq $test[$i]->{test}\n";
or print STDERR "Expect $cfg->{test} eq $test[$i]->{test}\n";
$count += 2;
}

View File

@ -243,14 +243,6 @@ sub sfa {
$self->logger->debug(
"Removing sessions unless a $_ device is registered");
}
#else {
# (
# return $self->sendError(
# $req, "Bad or Missing " . $_ . "Check parameter", 400
# )
# );
#}
}
my $total = ( keys %$res );

View File

@ -1585,6 +1585,10 @@ qr/^(?:\*\.)?(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.])*(?:[a-zA-Z][
'mailPwdRstSubject' => {
'type' => 'text'
},
'mailPwdRstTimeout' => {
'default' => 0,
'type' => 'int'
},
'mailPwdRstUrl' => {
'default' => 'http://auth.example.com/resetpwd',
'type' => 'url'
@ -1596,10 +1600,6 @@ qr/^(?:\*\.)?(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.])*(?:[a-zA-Z][
'default' => 'mail',
'type' => 'text'
},
'mailTimeout' => {
'default' => 0,
'type' => 'int'
},
'maintenance' => {
'default' => 0,
'type' => 'bool'

View File

@ -1137,19 +1137,19 @@ sub attributes {
},
mailReplyTo =>
{ type => 'text', documentation => 'Reply-To address' },
mailTimeout => {
mailPwdRstTimeout => {
type => 'int',
default => 0,
documentation => 'Mail session timeout',
documentation => 'Mail password reset session timeout',
},
# Password reset
mailPwdRstBody =>
{ type => 'longtext', documentation => 'Custom mail body', },
{ type => 'longtext', documentation => 'Custom password reset mail body', },
mailPwdRstConfirmBody => {
type => 'longtext',
documentation => 'Custom confirm mail body',
documentation => 'Custom confirm password reset mail body',
},
mailPwdRstConfirmSubject => {
type => 'text',

View File

@ -113,6 +113,7 @@ sub tree {
nodes_cond => [
{ title => 'adParams',
help => 'authad.html',
form => 'simpleInputContainer',
nodes => [ 'ADPwdMaxAge', 'ADPwdExpireWarning' ]
},
{ title => 'choiceParams',
@ -201,6 +202,7 @@ sub tree {
]
},
{ title => 'kerberosParams',
form => 'simpleInputContainer',
help => 'authkerberos.html',
nodes => [
'krbKeytab', 'krbByJs',
@ -506,11 +508,14 @@ sub tree {
'upgradeSession',
{ title => 'portalServers',
help => 'portalservers.html',
form => 'simpleInputContainer',
nodes => [
'wsdlServer', 'restSessionServer',
'restConfigServer', 'soapSessionServer',
'soapConfigServer', 'exportedAttr'
'wsdlServer',
'restSessionServer',
'restConfigServer',
'soapSessionServer',
'soapConfigServer',
'exportedAttr',
]
},
{ title => 'loginHistory',
@ -537,20 +542,6 @@ sub tree {
{ title => 'passwordManagement',
help => 'resetpassword.html',
nodes => [
{ title => 'SMTP',
nodes => [
'SMTPServer', 'SMTPPort',
'SMTPAuthUser', 'SMTPAuthPass',
'SMTPTLS', 'SMTPTLSOpts',
]
},
{ title => 'mailHeaders',
form => 'simpleInputContainer',
nodes => [
'mailFrom', 'mailReplyTo',
'mailCharset'
]
},
{ title => 'mailPwdRstContent',
form => 'simpleInputContainer',
nodes => [
@ -564,9 +555,8 @@ sub tree {
form => 'simpleInputContainer',
nodes => [
'mailPwdRstUrl',
'mailPwdRstTimeout',
'randomPasswordRegexp',
'mailTimeout',
'mailSessionKey'
]
}
]
@ -621,25 +611,32 @@ sub tree {
help => 'u2f.html',
form => 'simpleInputContainer',
nodes => [
'u2fActivation', 'u2fSelfRegistration',
'u2fAuthnLevel', 'u2fUserCanRemoveKey',
'u2fActivation',
'u2fSelfRegistration',
'u2fAuthnLevel',
'u2fUserCanRemoveKey',
]
},
{ title => 'external2f',
help => 'external2f.html',
form => 'simpleInputContainer',
nodes => [
'ext2fActivation', 'ext2FSendCommand',
'ext2FValidateCommand', 'ext2fAuthnLevel',
'ext2fActivation',
'ext2FSendCommand',
'ext2FValidateCommand',
'ext2fAuthnLevel',
'ext2fLogo',
]
},
{ title => 'rest2f',
help => 'rest2f.html',
nodes => [
'rest2fActivation', 'rest2fInitUrl',
'rest2fInitArgs', 'rest2fVerifyUrl',
'rest2fVerifyArgs', 'rest2fAuthnLevel',
'rest2fActivation',
'rest2fInitUrl',
'rest2fInitArgs',
'rest2fVerifyUrl',
'rest2fVerifyArgs',
'rest2fAuthnLevel',
'rest2fLogo',
]
},
@ -666,6 +663,26 @@ sub tree {
nodes => [
'customFunctions',
'multiValuesSeparator',
{ title => 'SMTP',
help => 'smtp.html',
nodes => [
'mailSessionKey',
'SMTPServer',
'SMTPPort',
'SMTPAuthUser',
'SMTPAuthPass',
'SMTPTLS',
'SMTPTLSOpts',
{ title => 'mailHeaders',
form => 'simpleInputContainer',
nodes => [
'mailFrom',
'mailReplyTo',
'mailCharset'
]
},
]
},
{ title => 'security',
help =>
'security.html#configure_security_settings',
@ -710,17 +727,20 @@ sub tree {
help => 'redirections.html#portal_redirections',
form => 'simpleInputContainer',
nodes => [
'jsRedirect', 'noAjaxHook',
'jsRedirect',
'noAjaxHook',
'skipRenewConfirmation',
]
},
'nginxCustomHandlers',
'logoutServices',
'logoutServices',
{ title => 'forms',
form => 'simpleInputContainer',
nodes => [
'infoFormMethod', 'confirmFormMethod',
'redirectFormMethod', 'activeTimer',
'infoFormMethod',
'confirmFormMethod',
'redirectFormMethod',
'activeTimer',
]
},
]
@ -932,7 +952,9 @@ sub tree {
{ title => 'casServiceMetadata',
nodes => [
'casAttr',
'casAccessControlPolicy', 'casStorage', 'casStorageOptions',
'casAccessControlPolicy',
'casStorage',
'casStorageOptions',
'casAttributes',
]

View File

@ -20,11 +20,11 @@ filterFunctions =
# Little function to select good node
p = (s) ->
tmp = s.toLowerCase()
if tmp == 'ad'
tmp = 'ldap'
else if tmp == 'openidconnect'
if tmp == 'openidconnect'
tmp = 'oidc'
nToShow.push tmp + 'Params'
if tmp == 'ad'
nToShow.push 'ldapParams'
# Show all normal nodes
for n in node.nodes

View File

@ -20,7 +20,22 @@
</ul>
</div>
</div>
<script type="text/menu">
[{
"title": "up",
"icon": "arrow-up"
},{
"title": "down",
"icon": "arrow-down"
},{
"title": "deleteEntry",
"icon": "minus-sign"
},{
"title": "newApp",
"icon": "plus-sign"
}]
</script>
<!-- Uncomment this snippet to enable sub categories
<script type="text/menu">
[{
"title": "up",
@ -39,3 +54,4 @@
"icon": "plus-sign"
}]
</script>
-->

View File

@ -1,4 +1,4 @@
// Generated by CoffeeScript 1.12.8
// Generated by CoffeeScript 1.12.7
(function() {
var filterFunctions;
@ -18,12 +18,13 @@
p = function(s) {
var tmp;
tmp = s.toLowerCase();
if (tmp === 'ad') {
tmp = 'ldap';
} else if (tmp === 'openidconnect') {
if (tmp === 'openidconnect') {
tmp = 'oidc';
}
return nToShow.push(tmp + 'Params');
nToShow.push(tmp + 'Params');
if (tmp === 'ad') {
return nToShow.push('ldapParams');
}
};
ref1 = node.nodes;
for (j = 0, len1 = ref1.length; j < len1; j++) {

View File

@ -1 +1 @@
(function(){var filterFunctions;filterFunctions={authParams:function(scope,$q,node){var i,len,n,ref,wait;wait=[];ref=node.nodes;for(i=0,len=ref.length;i<len;i++){n=ref[i];wait.push(scope.getKey(n))}return $q.all(wait).then(function(){var all,j,k,l,len1,len2,len3,len4,len5,len6,m,nToShow,nd,o,p,q,r,ref1,ref2,ref3,ref4,restart,s;all=false;nToShow=[];p=function(s){var tmp;tmp=s.toLowerCase();if(tmp==="ad"){tmp="ldap"}else if(tmp==="openidconnect"){tmp="oidc"}return nToShow.push(tmp+"Params")};ref1=node.nodes;for(j=0,len1=ref1.length;j<len1;j++){n=ref1[j];p(n.data)}ref2=node.nodes_cond;for(k=0,len2=ref2.length;k<len2;k++){n=ref2[k];restart=0;nd=n._nodes?n._nodes:n.nodes;if(node.nodes[0].data==="Choice"&&n.id==="choiceParams"){console.log("Choice is selected");if(nd[1].cnodes){restart++}else{nd=nd[1]._nodes?nd[1]._nodes:nd[1].nodes;for(l=0,len3=nd.length;l<len3;l++){m=nd[l];ref3=m.data;for(o=0,len4=ref3.length;o<len4;o++){s=ref3[o];if(typeof s==="string"){p(s)}}}}}else if(node.nodes[0].data==="Combination"&&n.id==="combinationParams"){console.log("Combination is selected");if(nd[1].cnodes){restart++}else{nd=nd[1]._nodes?nd[1]._nodes:nd[1].nodes;for(q=0,len5=nd.length;q<len5;q++){m=nd[q];p(m.data.type)}}}if(restart){scope.waiting=true;scope.download({$modelValue:nd[1]}).then(function(){return filterFunctions.authParams(scope,$q,node)});return}}ref4=node.nodes_cond;for(r=0,len6=ref4.length;r<len6;r++){n=ref4[r];if(!all&&nToShow.indexOf(n.id)===-1){n.show=false}else{n.show=true}}})}};window.filterFunctions=filterFunctions}).call(this);
(function(){var filterFunctions;filterFunctions={authParams:function(scope,$q,node){var i,len,n,ref,wait;wait=[];ref=node.nodes;for(i=0,len=ref.length;i<len;i++){n=ref[i];wait.push(scope.getKey(n))}return $q.all(wait).then(function(){var all,j,k,l,len1,len2,len3,len4,len5,len6,m,nToShow,nd,o,p,q,r,ref1,ref2,ref3,ref4,restart,s;all=false;nToShow=[];p=function(s){var tmp;tmp=s.toLowerCase();if(tmp==="openidconnect"){tmp="oidc"}nToShow.push(tmp+"Params");if(tmp==="ad"){return nToShow.push("ldapParams")}};ref1=node.nodes;for(j=0,len1=ref1.length;j<len1;j++){n=ref1[j];p(n.data)}ref2=node.nodes_cond;for(k=0,len2=ref2.length;k<len2;k++){n=ref2[k];restart=0;nd=n._nodes?n._nodes:n.nodes;if(node.nodes[0].data==="Choice"&&n.id==="choiceParams"){console.log("Choice is selected");if(nd[1].cnodes){restart++}else{nd=nd[1]._nodes?nd[1]._nodes:nd[1].nodes;for(l=0,len3=nd.length;l<len3;l++){m=nd[l];ref3=m.data;for(o=0,len4=ref3.length;o<len4;o++){s=ref3[o];if(typeof s==="string"){p(s)}}}}}else if(node.nodes[0].data==="Combination"&&n.id==="combinationParams"){console.log("Combination is selected");if(nd[1].cnodes){restart++}else{nd=nd[1]._nodes?nd[1]._nodes:nd[1].nodes;for(q=0,len5=nd.length;q<len5;q++){m=nd[q];p(m.data.type)}}}if(restart){scope.waiting=true;scope.download({$modelValue:nd[1]}).then(function(){return filterFunctions.authParams(scope,$q,node)});return}}ref4=node.nodes_cond;for(r=0,len6=ref4.length;r<len6;r++){n=ref4[r];if(!all&&nToShow.indexOf(n.id)===-1){n.show=false}else{n.show=true}}})}};window.filterFunctions=filterFunctions}).call(this);

View File

@ -392,8 +392,8 @@
"mailReplyTo":"عنوان الجواب",
"mailSessionKey":"مفتاح الجلسة الذي يحتوي على عنوان البريد الإلكتروني",
"mailPwdRstSubject":"نجاح عنوان البريد",
"mailTimeout":"عدم تطابق كلمت المرور",
"mailPwdRstUrl":"Password reset page URL",
"mailPwdRstTimeout":"عدم تطابق كلمت المرور",
"mailPwdRstUrl":"Page URL",
"maintenance":"وضع الصيانة",
"malformedValue":"قيمة سيئة التصميم",
"managerDn":"حساب",

View File

@ -38,7 +38,7 @@
"addU2FKey":"U2F - Schlüssel hinzufügen",
"addTOTPKey":"TOTP-Schlüssel hinzufügen",
"addVhost":"Virtuellen Host hinzufügen",
"adParams":"Active Directory Einstellungen",
"adParams":"Active Directory einstellungen",
"ADPwdExpireWarning":"Passwort - Ablauf Warnung",
"ADPwdMaxAge":"maximales Alter",
"advancedParams":"Weitere Einstellungen",
@ -392,8 +392,8 @@
"mailReplyTo":"Reply address",
"mailSessionKey":"Session key containing mail address",
"mailPwdRstSubject":"Success mail subject",
"mailTimeout":"Validity time of a password reset request",
"mailPwdRstUrl":"Password reset page URL",
"mailPwdRstTimeout":"Validity time of a password reset request",
"mailPwdRstUrl":"Page URL",
"maintenance":"Maintenance mode",
"malformedValue":"Malformed value",
"managerDn":"Account",

View File

@ -38,7 +38,7 @@
"addU2FKey":"Add U2F key",
"addTOTPKey":"Add TOTP key",
"addVhost":"Add virtualhost",
"adParams":"Active Directory Parameters",
"adParams":"Active Directory parameters",
"ADPwdExpireWarning":"Password expire warning",
"ADPwdMaxAge":"Password max age",
"advancedParams":"Advanced parameters",
@ -392,8 +392,8 @@
"mailReplyTo":"Reply address",
"mailSessionKey":"Session key containing mail address",
"mailPwdRstSubject":"Success mail subject",
"mailTimeout":"Validity time of a password reset request",
"mailPwdRstUrl":"Password reset page URL",
"mailPwdRstTimeout":"Validity time of a password reset request",
"mailPwdRstUrl":"Reset page URL",
"maintenance":"Maintenance mode",
"malformedValue":"Malformed value",
"managerDn":"Account",
@ -635,7 +635,7 @@
"registerDB":"Register module",
"registerDoneSubject":"Subject for done mail",
"registerTimeout":"Validity time of a register request",
"registerUrl":"Page URL",
"registerUrl":"Register page URL",
"reloadParams":"Configuration reload",
"reloadTimeout":"Reload timeout",
"reloadUrls":"Reload URLs",

View File

@ -383,7 +383,7 @@
"mailCharset":"Charset",
"mailPwdRstConfirmBody":"Contenu du message de confirmation",
"mailPwdRstConfirmSubject":"Sujet du message de confirmation",
"mailPwdRstContent":"Contenu du mail de réinitialisation du mot de passe",
"mailPwdRstContent":"Contenu du mail de réinitialisation",
"mailFrom":"Expéditeur du message",
"mailHeaders":"En-têtes du mail",
"mailLDAPFilter":"Filtre mail",
@ -392,8 +392,8 @@
"mailReplyTo":"Adresse de réponse",
"mailSessionKey":"Clef de session contenant l'adresse email",
"mailPwdRstSubject":"Sujet du message de succès",
"mailTimeout":"Durée de validité d'une demande de réinitialisation",
"mailPwdRstUrl":"URL de la page de réinitialisation du mot de passe",
"mailPwdRstTimeout":"Durée de validité d'une requête de réinitialisation",
"mailPwdRstUrl":"URL de la page de réinitialisation",
"maintenance":"Mode maintenance",
"malformedValue":"Valeur mal formée",
"managerDn":"Compte de connexion LDAP",
@ -634,8 +634,8 @@
"registerConfirmSubject":"Sujet du mail de confirmation",
"registerDB":"Module d'auto-enregistrement",
"registerDoneSubject":"Sujet du mail de prise en compte",
"registerTimeout":"Durée de validité de la requête",
"registerUrl":"URL de la page",
"registerTimeout":"Durée de validité d'une requête de création",
"registerUrl":"URL de la page de création",
"reloadParams":"Mise à jour de la configuration",
"reloadTimeout":"Délai de mise à jour",
"reloadUrls":"URLs de mise à jour",

View File

@ -392,8 +392,8 @@
"mailReplyTo":"Indirizzo di risposta",
"mailSessionKey":"Chiave di sessione contenente l'indirizzo di posta",
"mailPwdRstSubject":"Successo del soggetto della mail",
"mailTimeout":"Tempo di validità di una richiesta di ripristino della password",
"mailPwdRstUrl":"URL della pagina di ripristino della password",
"mailPwdRstTimeout":"Tempo di validità di una richiesta di ripristino della password",
"mailPwdRstUrl":"URL della pagina",
"maintenance":"Modalità di manutenzione",
"malformedValue":"Valore malformato",
"managerDn":"Account",
@ -632,10 +632,10 @@
"regexps":"Espressioni regolari",
"register":"Registra nuovo account",
"registerConfirmSubject":"Oggetto per la mail di conferma",
"registerDB":"Modulo di autoregistrazione",
"registerDB":"Modulo di autoregidi registrazionestrazione",
"registerDoneSubject":"Oggetto per la mail inviata",
"registerTimeout":"Tempo di validità di una richiesta di registrazione",
"registerUrl":"URL della pagina",
"registerUrl":"URL della pagina di registrazione",
"reloadParams":"Ricarica di configurazione",
"reloadTimeout":"Ricarica il timeout",
"reloadUrls":"Ricarica gli URL",

View File

@ -392,8 +392,8 @@
"mailReplyTo":"Địa chỉ trả lời",
"mailSessionKey":"Khóa phiên chứa địa chỉ thư",
"mailPwdRstSubject":"Chủ đề thư thành công",
"mailTimeout":"Thời hạn hợp lệ của yêu cầu đặt lại mật khẩu",
"mailPwdRstUrl":"Password reset page URL",
"mailPwdRstTimeout":"Thời hạn hợp lệ của yêu cầu đặt lại mật khẩu",
"mailPwdRstUrl":"Reset page URL",
"maintenance":"Chế độ bảo trì",
"malformedValue":"Giá trị không hợp lệ",
"managerDn":"Tài khoản",
@ -635,7 +635,7 @@
"registerDB":"Register module",
"registerDoneSubject":"Tiêu đề cho thư đã hoàn tất",
"registerTimeout":"Thời hạn hiệu lực của yêu cầu đăng ký",
"registerUrl":"Page URL",
"registerUrl":"Register page URL",
"reloadParams":"Configuration reload",
"reloadTimeout":"Reload timeout",
"reloadUrls":"Reload URLs",

View File

@ -392,8 +392,8 @@
"mailReplyTo":"Reply address",
"mailSessionKey":"Session key containing mail address",
"mailPwdRstSubject":"Success mail subject",
"mailTimeout":"Validity time of a password reset request",
"mailPwdRstUrl":"Password reset page URL",
"mailPwdRstTimeout":"Validity time of a password reset request",
"mailPwdRstUrl":"Reset page URL",
"maintenance":"Maintenance mode",
"malformedValue":"Malformed value",
"managerDn":"账户",
@ -635,7 +635,7 @@
"registerDB":"Register module",
"registerDoneSubject":"Subject for done mail",
"registerTimeout":"Validity time of a register request",
"registerUrl":"Page URL",
"registerUrl":"Register page URL",
"reloadParams":"Configuration reload",
"reloadTimeout":"Reload timeout",
"reloadUrls":"Reload URLs",

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1338,8 +1338,8 @@
"title": "randomPasswordRegexp"
}, {
"default": 0,
"id": "mailTimeout",
"title": "mailTimeout",
"id": "mailPwdRstTimeout",
"title": "mailPwdRstTimeout",
"type": "int"
}, {
"default": "mail",

View File

@ -1512,8 +1512,8 @@
"data": "[A-Z]{3}[a-z]{5}.\\d{2}"
}, {
"default": 0,
"id": "mailTimeout",
"title": "mailTimeout",
"id": "mailPwdRstTimeout",
"title": "mailPwdRstTimeout",
"type": "int",
"data": 0
}, {

View File

@ -1523,8 +1523,8 @@
"data": "[A-Z]{3}[a-z]{5}.\\d{2}"
}, {
"default": 0,
"id": "mailTimeout",
"title": "mailTimeout",
"id": "mailPwdRstTimeout",
"title": "mailPwdRstTimeout",
"type": "int",
"data": 0
}, {

View File

@ -1523,8 +1523,8 @@
"data": "[A-Z]{3}[a-z]{5}.\\d{2}"
}, {
"default": 0,
"id": "mailTimeout",
"title": "mailTimeout",
"id": "mailPwdRstTimeout",
"title": "mailPwdRstTimeout",
"type": "int",
"data": 0
}, {

View File

@ -1502,8 +1502,8 @@
"data": "[A-Z]{3}[a-z]{5}.\\d{2}"
}, {
"default": 0,
"id": "mailTimeout",
"title": "mailTimeout",
"id": "mailPwdRstTimeout",
"title": "mailPwdRstTimeout",
"type": "int",
"data": 0
}, {

View File

@ -2077,8 +2077,8 @@
},
{
"default": 0,
"id": "mailTimeout",
"title": "mailTimeout",
"id": "mailPwdRstTimeout",
"title": "mailPwdRstTimeout",
"type": "int"
},
{

View File

@ -13,14 +13,14 @@ use strict;
use Mouse;
use JSON qw(from_json to_json);
use Lemonldap::NG::Portal::Main::Constants qw(
PE_ERROR
PE_NOTOKEN
PE_OK
PE_SENDRESPONSE
PE_TOKENEXPIRED
PE_ERROR
PE_NOTOKEN
PE_OK
PE_SENDRESPONSE
PE_TOKENEXPIRED
);
our $VERSION = '2.0.1';
our $VERSION = '2.0.2';
extends 'Lemonldap::NG::Portal::Main::Plugin';
@ -36,8 +36,8 @@ has ott => (
is => 'rw',
lazy => 1,
default => sub {
my $ott =
$_[0]->{p}->loadModule('Lemonldap::NG::Portal::Lib::OneTimeToken');
my $ott = $_[0]->{p}
->loadModule('Lemonldap::NG::Portal::Lib::OneTimeToken');
$ott->timeout( $_[0]->{conf}->{formTimeout} );
return $ott;
}
@ -55,7 +55,7 @@ sub init {
? 'available2FSelfRegistration'
: 'available2F'
}
)
)
{
my $prefix = lc($_);
$prefix =~ s/2f$//i;
@ -67,9 +67,10 @@ sub init {
# Unless $rule, skip loading
if ( $self->conf->{$ap} ) {
$self->logger->debug("Trying to load $_ 2F");
my $m =
$self->p->loadPlugin( $i ? "::2F::Register::$_" : "::2F::$_" )
or return 0;
my $m
= $self->p->loadPlugin(
$i ? "::2F::Register::$_" : "::2F::$_" )
or return 0;
# Rule and prefix may be modified by 2F module, reread them
my $rule = $self->conf->{$ap};
@ -79,13 +80,13 @@ sub init {
$rule = $self->p->HANDLER->substitute($rule);
unless ( $rule = $self->p->HANDLER->buildSub($rule) ) {
$self->error( 'External 2F rule error: '
. $self->p->HANDLER->tsv->{jail}->error );
. $self->p->HANDLER->tsv->{jail}->error );
return 0;
}
# Store module
push @{ $self->{ $i ? 'sfRModules' : 'sfModules' } },
{ p => $prefix, m => $m, r => $rule };
{ p => $prefix, m => $m, r => $rule };
}
else {
$self->logger->debug(' -> not enabled');
@ -99,10 +100,10 @@ sub init {
$self->p->HANDLER->substitute( $self->conf->{sfRequired} )
)
)
)
)
{
$self->error( 'Error in sfRequired rule'
. $self->p->HANDLER->tsv->{jail}->error );
. $self->p->HANDLER->tsv->{jail}->error );
return 0;
}
@ -163,13 +164,12 @@ sub run {
if ( $self->sfReq->( $req, $req->sessionInfo ) ) {
$self->logger->debug("2F is required...");
$self->logger->debug(" -> Register 2F");
$req->pdata->{sfRegToken} =
$self->ott->createToken( $req->sessionInfo );
$req->pdata->{sfRegToken}
= $self->ott->createToken( $req->sessionInfo );
$self->logger->debug("Just one 2F is enabled");
$self->logger->debug(" -> Redirect to 2fregisters/");
$req->response(
[
302,
[ 302,
[ Location => $self->conf->{portal} . '2fregisters/' ], []
]
);
@ -181,7 +181,7 @@ sub run {
}
$self->userLogger->info( 'Second factor required for '
. $req->sessionInfo->{ $self->conf->{whatToTrace} } );
. $req->sessionInfo->{ $self->conf->{whatToTrace} } );
# Store user data in a token
$req->sessionInfo->{_2fRealSession} = $req->id;
@ -206,7 +206,8 @@ sub run {
MAIN_LOGO => $self->conf->{portalMainLogo},
SKIN => $self->conf->{portalSkin},
TOKEN => $token,
MODULES => [ map { { CODE => $_->prefix, LOGO => $_->logo } } @am ],
MODULES =>
[ map { { CODE => $_->prefix, LOGO => $_->logo } } @am ],
CHECKLOGINS => $checkLogins
}
);
@ -232,15 +233,16 @@ sub _choice {
# Restore session
unless ( $token = $req->param('token') ) {
$self->userLogger->error( $self->prefix . ' 2F access without token' );
$self->userLogger->error(
$self->prefix . ' 2F access without token' );
$req->mustRedirect(1);
return $self->p->do( $req, [ sub { PE_NOTOKEN } ] );
return $self->p->do( $req, [ sub {PE_NOTOKEN} ] );
}
my $session;
unless ( $session = $self->ott->getToken($token) ) {
$self->userLogger->info('Token expired');
return $self->p->do( $req, [ sub { PE_TOKENEXPIRED } ] );
return $self->p->do( $req, [ sub {PE_TOKENEXPIRED} ] );
}
$req->sessionInfo($session);
@ -255,8 +257,7 @@ sub _choice {
$req->authResult($res);
return $self->p->do(
$req,
[
sub { $res }, 'controlUrl',
[ sub {$res}, 'controlUrl',
'buildCookie', @{ $self->p->endAuth },
]
);
@ -271,10 +272,7 @@ sub _redirect {
my $arg = $req->env->{QUERY_STRING};
$self->logger->debug('Call sfEngine _redirect method');
return [
302,
[
Location => $self->conf->{portal} . ( $arg ? "?$arg" : '' )
],
302, [ Location => $self->conf->{portal} . ( $arg ? "?$arg" : '' ) ],
[]
];
}
@ -286,8 +284,8 @@ sub _displayRegister {
# - display template if $tpl
# - else display choice template
if ($tpl) {
my ($m) =
grep { $_->{m}->prefix eq $tpl } @{ $self->sfRModules };
my ($m)
= grep { $_->{m}->prefix eq $tpl } @{ $self->sfRModules };
unless ($m) {
return $self->p->sendError( $req,
'Inexistent register module', 400 );
@ -307,34 +305,56 @@ sub _displayRegister {
'Looking if ' . $m->{m}->prefix . '2F register is available' );
if ( $m->{r}->( $req, $req->userData ) ) {
push @am,
{
{
CODE => $m->{m}->prefix,
URL => '/2fregisters/' . $m->{m}->prefix,
LOGO => $m->{m}->logo,
};
};
}
}
if (
@am == 1
if (@am == 1
and not( $req->userData->{_2fDevices}
or $req->data->{sfRegRequired} )
)
)
{
return [ 302, [ Location => $self->conf->{portal} . $am[0]->{URL} ],
[] ];
}
my $_2fDevices =
$req->userData->{_2fDevices}
? eval { from_json( $req->userData->{_2fDevices},
{ allow_nonref => 1 } ); }
: undef;
# Retrieve user all second factors
my $_2fDevices = $req->userData->{_2fDevices}
? eval {
from_json( $req->userData->{_2fDevices}, { allow_nonref => 1 } );
}
: undef;
unless ($_2fDevices) {
$self->logger->debug("No 2F Device found");
$_2fDevices = [];
}
# Parse second factors to display delete button if allowed
my $action = '';
foreach
my $type ( split /,\s*/, $self->conf->{available2FSelfRegistration} )
{
foreach (@$_2fDevices) {
$_->{type} =~ s/^UBK$/Yubikey/;
if ( $_->{type} eq $type ) {
my $t = lc($type);
$t =~ s/2f$//i;
$_->{delAllowed}
= $self->conf->{ $t . '2fActivation' }
&& $self->conf->{ $t . '2fUserCanRemoveKey' }
&& $self->conf->{ $t . '2fSelfRegistration' };
}
$action ||= $_->{delAllowed};
$_->{type} =~ s/^Yubikey$/UBK/;
}
}
# Display template
return $self->p->sendHtml(
$req,
'2fregisters',
@ -343,6 +363,7 @@ sub _displayRegister {
SKIN => $self->conf->{portalSkin},
MODULES => \@am,
SFDEVICES => $_2fDevices,
ACTION => $action,
REG_REQUIRED => $req->data->{sfRegRequired},
}
);
@ -356,8 +377,8 @@ sub register {
# - call register run method if $tpl
# - else give JSON list of available registers for this user
if ($tpl) {
my ($m) =
grep { $_->{m}->prefix eq $tpl } @{ $self->sfRModules };
my ($m)
= grep { $_->{m}->prefix eq $tpl } @{ $self->sfRModules };
unless ($m) {
return $self->p->sendError( $req,
'Inexistent register module', 400 );
@ -376,11 +397,11 @@ sub register {
$self->logger->debug(' -> OK');
my $name = $m->{m}->prefix;
push @am,
{
{
name => $name,
logo => $m->{m}->logo,
url => "/2fregisters/$name"
};
};
}
}
return $self->p->sendJSONresponse( $req, \@am );
@ -389,12 +410,12 @@ sub register {
sub restoreSession {
my ( $self, $req, @path ) = @_;
my $token = $req->pdata->{sfRegToken}
or return [ 302, [ Location => $self->conf->{portal} ], [] ];
or return [ 302, [ Location => $self->conf->{portal} ], [] ];
$req->userData( $self->ott->getToken( $token, 1 ) );
$req->data->{sfRegRequired} = 1;
return $req->method eq 'POST'
? $self->register( $req, @path )
: $self->_displayRegister( $req, @path );
? $self->register( $req, @path )
: $self->_displayRegister( $req, @path );
}
1;

View File

@ -287,7 +287,7 @@ sub display {
ASK_LOGINS => $req->param('checkLogins') || 0,
DISPLAY_RESETPASSWORD => $self->conf->{portalDisplayResetPassword},
DISPLAY_REGISTER => $self->conf->{portalDisplayRegister},
MAIL_URL => $self->conf->{mailUrl},
MAIL_URL => $self->conf->{mailPwdRstUrl},
REGISTER_URL => $self->conf->{registerUrl},
HIDDEN_INPUTS => $self->buildHiddenForm($req),
STAYCONNECTED => $self->conf->{stayConnected},

View File

@ -203,9 +203,9 @@ sub _reset {
return PE_MAILNOTFOUND;
}
my $mailTimeout
= $self->conf->{mailTimeout} || $self->conf->{timeout};
my $expTimestamp = time() + $mailTimeout;
my $mailPwdRstTimeout
= $self->conf->{mailPwdRstTimeout} || $self->conf->{timeout};
my $expTimestamp = time() + $mailPwdRstTimeout;
$req->data->{expMailDate}
= strftime( "%d/%m/%Y", localtime $expTimestamp );
$req->data->{expMailTime}
@ -227,12 +227,12 @@ sub _reset {
# Use default session timeout and mail session timeout to compute it
my $time = time();
my $timeout = $self->conf->{timeout};
my $mailTimeout = $self->conf->{mailTimeout} || $timeout;
my $mailPwdRstTimeout = $self->conf->{mailPwdRstTimeout} || $timeout;
$infos->{_utime} = $time + ( $mailTimeout - $timeout );
$infos->{_utime} = $time + ( $mailPwdRstTimeout - $timeout );
# Store expiration timestamp for further use
$infos->{mailSessionTimeoutTimestamp} = $time + $mailTimeout;
$infos->{mailSessionTimeoutTimestamp} = $time + $mailPwdRstTimeout;
# Store start timestamp for further use
$infos->{mailSessionStartTimestamp} = $time;

View File

@ -15,7 +15,10 @@ displayError = (j, status, err) ->
if res and res.error
res = res.error.replace /.* /, ''
console.log 'Returned error', res
setMsg res, 'warning'
if res.match /module/
setMsg 'notAuthorized', 'warning'
else
setMsg res, 'warning'
# Delete function (launched by "delete" button)
delete2F = (device, epoch) ->

View File

@ -24,7 +24,11 @@ LemonLDAP::NG 2F registration script
if (res && res.error) {
res = res.error.replace(/.* /, '');
console.log('Returned error', res);
return setMsg(res, 'warning');
if (res.match(/module/)) {
return setMsg('notAuthorized', 'warning');
} else {
return setMsg(res, 'warning');
}
}
};

View File

@ -1 +1 @@
(function(){var delete2F,displayError,setMsg;setMsg=function(msg,level){$("#msg").html(window.translate(msg));$("#color").removeClass("message-positive message-warning alert-success alert-warning");$("#color").addClass("message-"+level);if(level==="positive"){level="success"}return $("#color").addClass("alert-"+level)};displayError=function(j,status,err){var res;console.log("Error",err);res=JSON.parse(j.responseText);if(res&&res.error){res=res.error.replace(/.* /,"");console.log("Returned error",res);return setMsg(res,"warning")}};delete2F=function(device,epoch){if(device==="U2F"){device="u"}else if(device==="UBK"){device="yubikey"}else if(device==="TOTP"){device="totp"}else{setMsg("u2fFailed","warning")}return $.ajax({type:"POST",url:portal+"2fregisters/"+device+"/delete",data:{epoch:epoch},dataType:"json",error:displayError,success:function(resp){if(resp.error){if(resp.error.match(/notAuthorized/)){return setMsg("notAuthorized","warning")}else{return setMsg("unknownAction","warning")}}else if(resp.result){$("#delete-"+epoch).hide();return setMsg("yourKeyIsUnregistered","positive")}},error:displayError})};$(document).ready(function(){$("body").on("click",".btn-danger",function(){return delete2F($(this).attr("device"),$(this).attr("epoch"))});$("#goback").attr("href",portal);return $(".data-epoch").each(function(){var myDate;myDate=new Date($(this).text()*1e3);return $(this).text(myDate.toLocaleString())})})}).call(this);
(function(){var delete2F,displayError,setMsg;setMsg=function(msg,level){$("#msg").html(window.translate(msg));$("#color").removeClass("message-positive message-warning alert-success alert-warning");$("#color").addClass("message-"+level);if(level==="positive"){level="success"}return $("#color").addClass("alert-"+level)};displayError=function(j,status,err){var res;console.log("Error",err);res=JSON.parse(j.responseText);if(res&&res.error){res=res.error.replace(/.* /,"");console.log("Returned error",res);if(res.match(/module/)){return setMsg("notAuthorized","warning")}else{return setMsg(res,"warning")}}};delete2F=function(device,epoch){if(device==="U2F"){device="u"}else if(device==="UBK"){device="yubikey"}else if(device==="TOTP"){device="totp"}else{setMsg("u2fFailed","warning")}return $.ajax({type:"POST",url:portal+"2fregisters/"+device+"/delete",data:{epoch:epoch},dataType:"json",error:displayError,success:function(resp){if(resp.error){if(resp.error.match(/notAuthorized/)){return setMsg("notAuthorized","warning")}else{return setMsg("unknownAction","warning")}}else if(resp.result){$("#delete-"+epoch).hide();return setMsg("yourKeyIsUnregistered","positive")}},error:displayError})};$(document).ready(function(){$("body").on("click",".btn-danger",function(){return delete2F($(this).attr("device"),$(this).attr("epoch"))});$("#goback").attr("href",portal);return $(".data-epoch").each(function(){var myDate;myDate=new Date($(this).text()*1e3);return $(this).text(myDate.toLocaleString())})})}).call(this);

View File

@ -17,7 +17,10 @@
<th><span trspan="type">Type</span></th>
<th><span trspan="name">Name</span></th>
<th><span trspan="date">Date</span></th>
<th><span trspan="action">Action</span></th>
<th>
<TMPL_IF NAME="ACTION">
<span trspan="action">Action</span></th>
</TMPL_IF>
</tr>
</thead>
<tbody>
@ -27,11 +30,13 @@
<td class="align-middle"><TMPL_VAR NAME="name"></td>
<td class="data-epoch"><TMPL_VAR NAME="epoch"></td>
<td>
<span device='<TMPL_VAR NAME="type">' epoch='<TMPL_VAR NAME="epoch">' class="btn btn-danger" role="button">
<span class="fa fa-minus-circle"></span>
<span trspan="unregister">Unregister</span>
</span>
</td>
<TMPL_IF NAME="delAllowed">
<span device='<TMPL_VAR NAME="type">' epoch='<TMPL_VAR NAME="epoch">' class="btn btn-danger" role="button">
<span class="fa fa-minus-circle"></span>
<span trspan="unregister">Unregister</span>
</span>
</TMPL_IF>
</td>
</tr>
</TMPL_LOOP>
</tbody>

View File

@ -23,7 +23,7 @@
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-eye"></i> </span>
</div>
<input type="text" name="captcha" size="<TMPL_VAR NAME=CAPTCHA_SIZE>" class="form-control" trplaceholder="captcha" required aria-required="true"/>
<input type="text" name="captcha" size="<TMPL_VAR NAME=CAPTCHA_SIZE>" class="form-control" trplaceholder="captcha" required aria-required="true" autocomplete="off" />
</div>
</TMPL_IF>
<input type="hidden" name="token" value="<TMPL_VAR NAME="TOKEN">" />

View File

@ -36,7 +36,7 @@
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-eye"></i> </span>
</div>
<input type="text" name="captcha" size="<TMPL_VAR NAME=CAPTCHA_SIZE>" class="form-control" trplaceholder="captcha" required />
<input type="text" name="captcha" size="<TMPL_VAR NAME=CAPTCHA_SIZE>" class="form-control" trplaceholder="captcha" required autocomplete="off" />
</div>
</TMPL_IF>
<TMPL_IF NAME="TOKEN">
@ -87,7 +87,7 @@
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-eye"></i> </span>
</div>
<input type="text" name="captcha" size="<TMPL_VAR NAME=CAPTCHA_SIZE>" class="form-control" trplaceholder="captcha" required />
<input type="text" name="captcha" size="<TMPL_VAR NAME=CAPTCHA_SIZE>" class="form-control" trplaceholder="captcha" required autocomplete="off"/>
</div>
</TMPL_IF>

View File

@ -48,7 +48,7 @@
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-eye"></i> </span>
</div>
<input type="text" name="captcha" size="<TMPL_VAR NAME=CAPTCHA_SIZE>" class="form-control" placeholder="Captcha" required aria-required="true"/>
<input type="text" name="captcha" size="<TMPL_VAR NAME=CAPTCHA_SIZE>" class="form-control" placeholder="Captcha" required aria-required="true" autocomplete="off" />
</div>
</TMPL_IF>
<TMPL_IF NAME="TOKEN">

View File

@ -21,7 +21,7 @@
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-eye"></i> </span>
</div>
<input type="text" name="captcha" size="<TMPL_VAR NAME=CAPTCHA_SIZE>" class="form-control" trplaceholder="captcha" required aria-required="true"/>
<input type="text" name="captcha" size="<TMPL_VAR NAME=CAPTCHA_SIZE>" class="form-control" trplaceholder="captcha" required aria-required="true" autocomplete="off" />
</div>
</TMPL_IF>
<TMPL_IF NAME="TOKEN">