Yubikey 2nd factor (closes: #1399)
parent
396fc2362b
commit
c6908b87a2
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -0,0 +1,53 @@ |
||||
package Lemonldap::NG::Portal::2F::Register::Yubikey; |
||||
|
||||
use strict; |
||||
use Mouse; |
||||
use Lemonldap::NG::Portal::Main::Constants qw( |
||||
PE_FORMEMPTY |
||||
PE_ERROR |
||||
PE_OK |
||||
); |
||||
|
||||
our $VERSION = '2.0.0'; |
||||
|
||||
extends 'Lemonldap::NG::Portal::Main::Plugin'; |
||||
|
||||
# INITIALIZATION |
||||
|
||||
has prefix => ( is => 'rw', default => 'yubikey' ); |
||||
|
||||
has template => ( is => 'ro', default => 'yubikey2fregister' ); |
||||
|
||||
has logo => ( is => 'rw', default => 'u2f.png' ); |
||||
|
||||
sub init { |
||||
my ($self) = @_; |
||||
$self->conf->{yubikey2fPublicIDSize} ||= 12; |
||||
return 1; |
||||
} |
||||
|
||||
# RUNNING METHODS |
||||
|
||||
# Main method |
||||
sub run { |
||||
my ( $self, $req, $action ) = @_; |
||||
if ( $action eq 'register' ) { |
||||
my $otp = $req->param('otp'); |
||||
if ( $otp and length($otp) > 12 ) { |
||||
my $keys = $req->userData->{_yubikeys} || ''; |
||||
$keys .= ( $keys ? ', ' : '' ) |
||||
. substr( $otp, 0, $self->conf->{yubikeyPublicIDSize} ); |
||||
$self->p->updatePersistentSession( $req, { _yubikeys => $keys } ); |
||||
} |
||||
else { |
||||
$self->userLogger->error('Yubikey 2F: no code'); |
||||
return PE_FORMEMPTY; |
||||
} |
||||
} |
||||
else { |
||||
$self->userLogger->error("Unknown Yubikey action $action"); |
||||
return PE_ERROR; |
||||
} |
||||
} |
||||
|
||||
1; |
@ -0,0 +1,109 @@ |
||||
package Lemonldap::NG::Portal::2F::Yubikey; |
||||
|
||||
use strict; |
||||
use Mouse; |
||||
use Lemonldap::NG::Portal::Main::Constants qw( |
||||
PE_BADCREDENTIALS |
||||
PE_FORMEMPTY |
||||
PE_OK |
||||
PE_SENDRESPONSE |
||||
); |
||||
|
||||
our $VERSION = '2.0.0'; |
||||
|
||||
extends 'Lemonldap::NG::Portal::Main::SecondFactor'; |
||||
|
||||
# INITIALIZATION |
||||
|
||||
has prefix => ( is => 'ro', default => 'yubikey' ); |
||||
|
||||
has logo => ( is => 'rw', default => 'u2f.png' ); |
||||
|
||||
has yubi => ( is => 'rw' ); |
||||
|
||||
sub init { |
||||
my ($self) = @_; |
||||
eval { require Auth::Yubikey_WebClient }; |
||||
if ($@) { |
||||
$self->logger->error($@); |
||||
return 0; |
||||
} |
||||
if ( $self->conf->{yubikey2fSelfRegistration} |
||||
and $self->conf->{yubikey2fActivation} eq '1' ) |
||||
{ |
||||
$self->conf->{yubikey2fActivation} = '$_yubikeys'; |
||||
} |
||||
unless ($self->conf->{yubikey2fClientID} |
||||
and $self->conf->{yubikey2fSecretKey} ) |
||||
{ |
||||
$self->logger->error( |
||||
"Missing mandatory parameters (Client ID and secret key)"); |
||||
return 0; |
||||
} |
||||
$self->conf->{yubikey2fPublicIDSize} ||= 12; |
||||
$self->yubi( |
||||
Auth::Yubikey_WebClient->new( |
||||
{ |
||||
id => $self->conf->{yubikey2fClientID}, |
||||
api => $self->conf->{yubikey2fSecretKey}, |
||||
nonce => $self->conf->{yubikey2fNonce}, |
||||
url => $self->conf->{yubikey2fUrl} |
||||
} |
||||
) |
||||
); |
||||
return $self->SUPER::init(); |
||||
} |
||||
|
||||
sub run { |
||||
my ( $self, $req, $token ) = @_; |
||||
|
||||
unless ( $req->{sessionInfo}->{_yubikeys} ) { |
||||
$self->userLogger->warn( 'User ' |
||||
. $req->{sessionInfo}->{ $self->conf->{whatToTrace} } |
||||
. ' has no Yubikey registered' ); |
||||
return PE_BADCREDENTIALS; |
||||
} |
||||
|
||||
# Prepare form |
||||
my $tmp = $self->p->sendHtml( |
||||
$req, |
||||
'ext2fcheck', |
||||
params => { |
||||
SKIN => $self->conf->{portalSkin}, |
||||
TOKEN => $token, |
||||
TARGET => '/yubikey2fcheck', |
||||
INPUTLOGO => 'yubikey.png', |
||||
LEGEND => 'clickOnYubikey', |
||||
} |
||||
); |
||||
$self->logger->debug("Display Yubikey form"); |
||||
|
||||
$req->response($tmp); |
||||
return PE_SENDRESPONSE; |
||||
} |
||||
|
||||
sub verify { |
||||
my ( $self, $req, $session ) = @_; |
||||
my $code; |
||||
unless ( $code = $req->param('code') ) { |
||||
$self->userLogger->error('Yubikey 2F: no code'); |
||||
return PE_FORMEMPTY; |
||||
} |
||||
|
||||
# Verify OTP |
||||
if ( |
||||
index( substr( $code, 0, $self->conf->{yubikey2fPublicIDSize} ), |
||||
$session->{_yubikeys} ) == -1 |
||||
) |
||||
{ |
||||
$self->userLogger->warn('Yubikey not registered'); |
||||
return PE_BADCREDENTIALS; |
||||
} |
||||
if ( $self->yubi->otp($code) ne 'OK' ) { |
||||
$self->userLogger->warn('Yubikey verification failed'); |
||||
return PE_BADCREDENTIALS; |
||||
} |
||||
PE_OK; |
||||
} |
||||
|
||||
1 |
@ -0,0 +1,25 @@ |
||||
<TMPL_INCLUDE NAME="header.tpl"> |
||||
|
||||
<div class="container"> |
||||
<div id="color" class="message message-positive alert"><span id="msg" trspan="clickOnYubikey"></span></div> |
||||
</div> |
||||
<main id="menucontent" class="container"> |
||||
<div class="panel panel-info"> |
||||
<div class="panel-body"> |
||||
<form action="/2fregisters/yubikey/register" method="post"> |
||||
<div class="form-group"> |
||||
<input id="otp" name="otp" /> |
||||
</div> |
||||
</form> |
||||
</div> |
||||
</div> |
||||
</main> |
||||
|
||||
<div class="buttons"> |
||||
<a id="goback" href="<TMPL_VAR NAME="PORTAL_URL"><TMPL_IF NAME="AUTH_URL">/?url=<TMPL_VAR NAME="AUTH_URL"></TMPL_IF>" class="btn btn-primary" role="button"> |
||||
<span class="glyphicon glyphicon-home"></span> |
||||
<span trspan="goToPortal">Go to portal</span> |
||||
</a> |
||||
</div> |
||||
|
||||
<TMPL_INCLUDE NAME="footer.tpl"> |
Loading…
Reference in new issue