2F engine works with 1 2F enabled (#1148)

This commit is contained in:
Xavier Guimard 2018-03-08 20:36:32 +01:00
parent e9e820ecd1
commit 06cb8a6e11
21 changed files with 62 additions and 68 deletions

View File

@ -25,7 +25,7 @@ staticPrefix = /static
languages = fr, en, vi, it, ar
templateDir = __pwd__/lemonldap-ng-portal/site/templates
;totp2fActivation = 1
;totpSelfRegistration = 1
;totp2fSelfRegistration = 1
[handler]

View File

@ -3010,7 +3010,8 @@ qr/(?:(?:https?):\/\/(?:(?:(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.]
'type' => 'keyTextContainer'
},
'sfEngine' => {
'default' => '::2F::Engine::Default'
'default' => '::2F::Engine::Default',
'type' => 'text'
},
'singleIP' => {
'default' => 0,
@ -3175,7 +3176,7 @@ qr/^(?:(?:(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.])*(?:[a-zA-Z][-a-
'default' => 1,
'type' => 'int'
},
'totpSelfRegistration' => {
'totp2fSelfRegistration' => {
'default' => 0,
'type' => 'bool'
},

View File

@ -1036,7 +1036,7 @@ sub attributes {
default => 0,
documentation => 'TOTP activation',
},
totpSelfRegistration => {
totp2fSelfRegistration => {
type => 'bool',
default => 0,
documentation => 'TOTP self registration activation',

View File

@ -663,7 +663,7 @@ sub tree {
help => 'totp2f.html',
form => 'simpleInputContainer',
nodes => [
'totp2fActivation', 'totpSelfRegistration',
'totp2fActivation', 'totp2fSelfRegistration',
'totp2fAuthnLevel', 'totp2fIssuer',
'totp2fInterval', 'totp2fRange',
'totp2fDigits',

View File

@ -716,7 +716,7 @@
"totp2fInterval":"Interval",
"totp2fIssuer":"TOTP Issuer name",
"totp2fRange":"Range of attempts",
"totpSelfRegistration":"Self registration",
"totp2fSelfRegistration":"Self registration",
"trustedDomains":"النطاقات الموثوق بها",
"trustedProxies":"عناوين الآي بي البروكسي الموثوق بها",
"twitterAppName":"اسم التطبيق",

View File

@ -716,7 +716,7 @@
"totp2fInterval":"Interval",
"totp2fIssuer":"TOTP Issuer name",
"totp2fRange":"Range of attempts",
"totpSelfRegistration":"Self registration",
"totp2fSelfRegistration":"Self registration",
"trustedDomains":"Trusted domains",
"trustedProxies":"Trusted proxies IP",
"twitterAppName":"Application name",

View File

@ -716,7 +716,7 @@
"totp2fInterval":"Intervalle",
"totp2fIssuer":"Nom du fournisseur TOTP",
"totp2fRange":"Nombre d'intervalles à tester",
"totpSelfRegistration":"Auto-enregistrement",
"totp2fSelfRegistration":"Auto-enregistrement",
"trustedDomains":"Domaines approuvés",
"trustedProxies":"IP des proxys de confiance",
"twitterAppName":"Nom de l'application",

View File

@ -716,7 +716,7 @@
"totp2fInterval":"Interval",
"totp2fIssuer":"TOTP Issuer name",
"totp2fRange":"Range of attempts",
"totpSelfRegistration":"Self registration",
"totp2fSelfRegistration":"Self registration",
"trustedDomains":"Domini attendibili",
"trustedProxies":"IP proxy attendibili",
"twitterAppName":"Nome dell'applicazione",

View File

@ -716,7 +716,7 @@
"totp2fInterval":"Interval",
"totp2fIssuer":"TOTP Issuer name",
"totp2fRange":"Range of attempts",
"totpSelfRegistration":"Self registration",
"totp2fSelfRegistration":"Self registration",
"trustedDomains":"Miền tin cậy",
"trustedProxies":"proxies IP tin cậy",
"twitterAppName":"Tên ứng dụng",

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -16,6 +16,16 @@ has sfModules => ( is => 'rw', default => sub { [] } );
has sfRModules => ( is => 'rw', default => sub { [] } );
has ott => (
is => 'rw',
default => sub {
my $ott =
$_[0]->{p}->loadModule('Lemonldap::NG::Portal::Lib::OneTimeToken');
$ott->timeout( $_[0]->{conf}->{formTimeout} );
return $ott;
}
);
sub init {
my ($self) = @_;
@ -26,7 +36,7 @@ sub init {
# Load 2F modules
for my $i ( 0 .. 1 ) {
foreach (
split /,\s+/,
split /,\s*/,
$self->conf->{ $i ? 'available2FSelfRegistration' : 'available2F' }
)
{
@ -34,10 +44,11 @@ sub init {
$prefix =~ s/2f$//i;
# Activation parameter
my $ap = $prefix. ( $i ? '2fSelfRegistration' : '2fActivation' );
my $ap = $prefix . ( $i ? '2fSelfRegistration' : '2fActivation' );
$self->logger->debug("Checking $ap");
# Unless $rule, skip loading
if ($self->conf->{$ap}) {
if ( $self->conf->{$ap} ) {
$self->logger->debug("Trying to load $_ 2F");
my $m =
$self->p->loadPlugin( $i ? "::2F::Register::$_" : "::2F::$_" )
@ -59,6 +70,9 @@ sub init {
push @{ $self->{ $i ? 'sfRModules' : 'sfModules' } },
{ m => $m, r => $rule };
}
else {
$self->logger->debug(' -> not enabled');
}
}
}
@ -69,40 +83,44 @@ sub init {
# run is called at each authentication, just after sesionInfo populate
sub run {
my($self,$req)=@_;
my ( $self, $req ) = @_;
# Skip 2F unless a module has been registered
return PE_OK unless(@{$self->sfModules});
return PE_OK unless ( @{ $self->sfModules } );
# Search for authorizated modules for this user
my @am;
foreach my $m (@{$self->sfModules}) {
$self->logger->debug('Looking if '.$m->{m}->prefix.'2F is available');
if($m->{r}->( $req, $req->sessionInfo ) ) {
foreach my $m ( @{ $self->sfModules } ) {
$self->logger->debug(
'Looking if ' . $m->{m}->prefix . '2F is available' );
if ( $m->{r}->( $req, $req->sessionInfo ) ) {
$self->logger->debug(' -> OK');
push @am,$m->{m};
push @am, $m->{m};
}
}
# If no 2F modules are authorizated, skipping 2F
# Note that a rule may forbid access after (GrantSession plugin)
return PE_OK unless(@am);
return PE_OK unless (@am);
# If only one 2F is authorizated, display it
$self->userLogger->info( 'Second factor required for '
. $req->sessionInfo->{ $self->conf->{whatToTrace} } );
# Store user data in a token
$req->sessionInfo->{_2fRealSession} = $req->id;
$req->sessionInfo->{_2fUrldc} = $req->urldc;
my $token = $self->ott->createToken( $req->sessionInfo );
unless($#am) {
my $res = $am[0]->run($req, $token);
# If only one 2F is authorizated, display it
unless ($#am) {
my $res = $am[0]->run( $req, $token );
delete $req->{authResult} if ($res);
return $res;
}
# More than 1 2F has been found, display choice
# TODO
return PE_OK
return PE_OK;
}
1;

View File

@ -10,6 +10,8 @@ extends 'Lemonldap::NG::Portal::Main::Plugin', 'Lemonldap::NG::Common::TOTP';
# INITIALIZATION
has prefix => ( is => 'rw', default => 'totp' );
has ott => (
is => 'rw',
lazy => 1,

View File

@ -10,6 +10,8 @@ extends 'Lemonldap::NG::Portal::Lib::U2F';
# INITIALIZATION
has prefix => ( is => 'rw', default => 'u' );
sub init {
my ($self) = @_;
return 0 unless $self->SUPER::init;

View File

@ -24,7 +24,7 @@ sub init {
# If self registration is enabled and "activation" is simply set to
# "enabled", replace the rule to detect if user has register its key
if ( $self->conf->{totpSelfRegistration}
if ( $self->conf->{totp2fSelfRegistration}
and $self->conf->{totp2fActivation} eq '1' )
{
$self->conf->{totp2fActivation} = '$_totp2fSecret';

View File

@ -27,6 +27,7 @@ has menu => ( is => 'rw', default => sub { {} } );
has _authentication => ( is => 'rw' );
has _userDB => ( is => 'rw' );
has _passwordDB => ( is => 'rw' );
has _sfEngine => ( is => 'rw' );
has loadedModules => ( is => 'rw' );
@ -220,7 +221,8 @@ sub reloadConf {
# Load second-factor engine
$self->conf->{'sfEngine'} ||= '::2F::Engines::Default';
return $self->fail
unless $self->{sfEngine} = $self->loadPlugin( $self->conf->{'sfEngine'} );
unless $self->{_sfEngine} =
$self->loadPlugin( $self->conf->{'sfEngine'} );
# Initialize trusted domain regexp
if ( $self->conf->{trustedDomains}

View File

@ -16,11 +16,6 @@ our @pList = (
portalDisplayResetPassword => '::Plugins::MailReset',
portalStatus => '::Plugins::Status',
cda => '::Plugins::CDA',
ext2fActivation => '::2F::External2F',
totp2fActivation => '::2F::TOTP',
totpSelfRegistration => '::2F::Register::TOTP',
u2fActivation => '::2F::U2F',
u2fSelfRegistration => '::2F::Register::U2F',
notification => '::Plugins::Notifications',
portalCheckLogins => '::Plugins::History',
stayConnected => '::Plugins::StayConnected',

View File

@ -477,4 +477,9 @@ sub buildCookie {
PE_OK;
}
sub secondFactor {
my ( $self, $req ) = @_;
return $self->_sfEngine->run($req);
}
1;

View File

@ -20,7 +20,7 @@ sub authProcess { qw(extractFormInfo getUser authenticate) }
sub sessionDatas {
qw(setAuthSessionInfo setSessionInfo setMacros setGroups setPersistentSessionInfo
setLocalGroups store buildCookie);
setLocalGroups store buildCookie secondFactor);
}
# RESPONSE HANDLER

View File

@ -12,10 +12,6 @@ our $VERSION = '2.0.0';
extends 'Lemonldap::NG::Portal::Main::Plugin';
# INTERFACE
sub afterDatas { '_run' }
# INITIALIZATION
has ott => (
@ -28,22 +24,12 @@ has ott => (
}
);
has rule => ( is => 'rw' );
has prefix => ( is => 'rw' );
sub init {
my ($self) = @_;
$self->addUnauthRoute( $self->prefix . '2fcheck' => '_verify', ['POST'] );
$self->addUnauthRoute( $self->prefix . '2fcheck' => '_redirect', ['GET'] );
my $rule = $self->conf->{ $self->prefix . '2fActivation' };
$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 );
return 0;
}
$self->rule($rule);
1;
}
@ -55,23 +41,6 @@ sub _redirect {
];
}
sub _run {
my ( $self, $req ) = @_;
return PE_OK unless ( $self->rule->( $req, $req->sessionInfo ) );
$self->userLogger->info( 'Second factor required ('
. $self->prefix
. ') for '
. $req->sessionInfo->{ $self->conf->{whatToTrace} } );
$req->sessionInfo->{_2fRealSession} = $req->id;
$req->sessionInfo->{_2fUrldc} = $req->urldc;
my $token = $self->ott->createToken( $req->sessionInfo );
$req->id(0);
$self->p->rebuildCookies($req);
my $res = $self->run( $req, $token );
delete $req->{authResult} if ($res);
return $res;
}
sub _verify {
my ( $self, $req ) = @_;

View File

@ -15,9 +15,9 @@ SKIP: {
my $client = LLNG::Manager::Test->new(
{
ini => {
logLevel => 'error',
totpSelfRegistration => 1,
totp2fActivation => 1,
logLevel => 'error',
totp2fSelfRegistration => 1,
totp2fActivation => 1,
}
}
);