merge
This commit is contained in:
commit
eddd54383e
1
Makefile
1
Makefile
|
@ -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
3
debian/changelog
vendored
|
@ -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
2
debian/control
vendored
|
@ -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/
|
||||
|
|
|
@ -26,6 +26,8 @@ templateDir = __pwd__/lemonldap-ng-portal/site/templates
|
|||
portalStatus = 1
|
||||
totp2fActivation = 1
|
||||
totp2fSelfRegistration = 1
|
||||
captcha_mail_enabled = 0
|
||||
portalDisplayResetPassword = 1
|
||||
|
||||
[handler]
|
||||
|
||||
|
|
|
@ -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": {
|
||||
|
|
1
e2e-tests/persistent/5efe8af397fc3577e05b483aca964f1b
Normal file
1
e2e-tests/persistent/5efe8af397fc3577e05b483aca964f1b
Normal 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}
|
|
@ -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$/);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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');
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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');
|
||||
});
|
||||
});
|
||||
|
||||
});
|
|
@ -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'
|
||||
},
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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 );
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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',
|
||||
|
||||
]
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>
|
||||
-->
|
|
@ -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++) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -392,8 +392,8 @@
|
|||
"mailReplyTo":"عنوان الجواب",
|
||||
"mailSessionKey":"مفتاح الجلسة الذي يحتوي على عنوان البريد الإلكتروني",
|
||||
"mailPwdRstSubject":"نجاح عنوان البريد",
|
||||
"mailTimeout":"عدم تطابق كلمت المرور",
|
||||
"mailPwdRstUrl":"Password reset page URL",
|
||||
"mailPwdRstTimeout":"عدم تطابق كلمت المرور",
|
||||
"mailPwdRstUrl":"Page URL",
|
||||
"maintenance":"وضع الصيانة",
|
||||
"malformedValue":"قيمة سيئة التصميم",
|
||||
"managerDn":"حساب",
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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
|
@ -1338,8 +1338,8 @@
|
|||
"title": "randomPasswordRegexp"
|
||||
}, {
|
||||
"default": 0,
|
||||
"id": "mailTimeout",
|
||||
"title": "mailTimeout",
|
||||
"id": "mailPwdRstTimeout",
|
||||
"title": "mailPwdRstTimeout",
|
||||
"type": "int"
|
||||
}, {
|
||||
"default": "mail",
|
||||
|
|
|
@ -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
|
||||
}, {
|
||||
|
|
|
@ -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
|
||||
}, {
|
||||
|
|
|
@ -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
|
||||
}, {
|
||||
|
|
|
@ -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
|
||||
}, {
|
||||
|
|
|
@ -2077,8 +2077,8 @@
|
|||
},
|
||||
{
|
||||
"default": 0,
|
||||
"id": "mailTimeout",
|
||||
"title": "mailTimeout",
|
||||
"id": "mailPwdRstTimeout",
|
||||
"title": "mailPwdRstTimeout",
|
||||
"type": "int"
|
||||
},
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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},
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) ->
|
||||
|
|
|
@ -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');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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">" />
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -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">
|
||||
|
|
Loading…
Reference in New Issue
Block a user