From 35f506bd60ff0d6a487e7ebe4007acf0d9342944 Mon Sep 17 00:00:00 2001 From: Xavier Guimard Date: Wed, 8 Feb 2017 13:01:02 +0000 Subject: [PATCH] Start rewrite Register::U2F using Ajax (#1148) NB: broken for now --- .../lib/Lemonldap/NG/Common/PSGI.pm | 3 +- .../lib/Lemonldap/NG/Common/PSGI/Router.pm | 11 +- .../lib/Lemonldap/NG/Portal/Main/Display.pm | 1 - .../lib/Lemonldap/NG/Portal/Main/Run.pm | 4 + .../lib/Lemonldap/NG/Portal/Register/U2F.pm | 105 ++++++++++++------ 5 files changed, 82 insertions(+), 42 deletions(-) diff --git a/lemonldap-ng-common/lib/Lemonldap/NG/Common/PSGI.pm b/lemonldap-ng-common/lib/Lemonldap/NG/Common/PSGI.pm index fe73ae1e6..d2b72ad94 100644 --- a/lemonldap-ng-common/lib/Lemonldap/NG/Common/PSGI.pm +++ b/lemonldap-ng-common/lib/Lemonldap/NG/Common/PSGI.pm @@ -254,8 +254,9 @@ sub sendHtml { ( $args{params} ? %{ $args{params} } - : ( $self->can('tplParams') ? ( $self->tplParams ) : () ) + : () ), + ( $self->can('tplParams') ? ( $self->tplParams ) : () ), ); }; if ($@) { diff --git a/lemonldap-ng-common/lib/Lemonldap/NG/Common/PSGI/Router.pm b/lemonldap-ng-common/lib/Lemonldap/NG/Common/PSGI/Router.pm index a92531ad9..bd8a805bf 100644 --- a/lemonldap-ng-common/lib/Lemonldap/NG/Common/PSGI/Router.pm +++ b/lemonldap-ng-common/lib/Lemonldap/NG/Common/PSGI/Router.pm @@ -36,6 +36,7 @@ sub genRoute { } } else { + $dest //= $word; if ( $word =~ /^:(.*)$/ ) { $routes->{'#'} = $1; die "Target required for $word" unless ($dest); @@ -48,6 +49,12 @@ sub genRoute { else { $dest ||= $word; } + if ( $dest =~ /^(.+)\.html$/ ) { + my $tpl = $1 or die; + $self->lmLog( "route $dest will use $tpl" ); + $routes->{$word} = sub { $self->sendHtml( $_[1], $tpl ) }; + return; + } if ( $transform and ( not ref($dest) or ref($dest) eq 'CODE' ) ) { $dest = $transform->($dest); } @@ -72,10 +79,6 @@ sub genRoute { die "Type $t unauthorizated in routes"; } } - elsif ( $dest =~ /^(.+)\.html$/ ) { - my $tpl = $1 or die; - $routes->{$word} = sub { $self->sendHtml( $_[1], $tpl ) }; - } elsif ( $self->can($dest) ) { $routes->{$word} = sub { shift; $self->$dest(@_) }; } diff --git a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Main/Display.pm b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Main/Display.pm index 786f97fe8..c0c905bea 100644 --- a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Main/Display.pm +++ b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Main/Display.pm @@ -306,7 +306,6 @@ sub display { %templateParams = ( %templateParams, SKIN_PATH => $portalPath . "skins", - SKIN => $skin, ANTIFRAME => $self->conf->{portalAntiFrame}, SKIN_BG => $self->conf->{portalSkinBackground}, %{ $self->customParameters }, diff --git a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Main/Run.pm b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Main/Run.pm index 2c4fdb2ab..3f003bf39 100644 --- a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Main/Run.pm +++ b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Main/Run.pm @@ -680,4 +680,8 @@ sub rebuildCookies { $self->buildCookie($req); } +sub tplParams { + return ( SKIN => $_[0]->conf->{portalSkin} ); +} + 1; diff --git a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Register/U2F.pm b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Register/U2F.pm index 86dfdbc66..8412d190d 100644 --- a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Register/U2F.pm +++ b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Register/U2F.pm @@ -14,7 +14,8 @@ extends 'Lemonldap::NG::Portal::Lib::U2F'; sub init { my ($self) = @_; return 0 unless $self->SUPER::init; - $self->addAuthRoute( u2fregister => 'run', [ 'GET', 'POST' ] ); + $self->addAuthRoute( u2fregister => { ':action' => 'run' }, ['POST'] ); + $self->addAuthRoute( 'u2fregister.html' => undef, ['GET'] ); return 1; } @@ -23,12 +24,20 @@ sub init { # Main method sub run { my ( $self, $req ) = @_; + my $action = $req->param('action'); - # Check for registration response - if ( my $response = $req->param('registration') ) { - $self->lmLog( "Get registration data $response", 'debug' ); - my ( $keyHandle, $userKey ) = - $self->crypter->registrationVerify($response); + if ( $action eq 'register' ) { + my $challenge = $self->crypter->registrationChallenge; + return [ 200, [ 'Content-Type' => 'application/json' ], [$challenge] ]; + } + if ( $action eq 'registration' ) { + my $resp; + unless ( $resp = $req->param('registration') ) { + return $self->p->sendError( $req, 'Missing registration parameter', + 400 ); + } + $self->lmLog( "Get registration data $resp", 'debug' ); + my ( $keyHandle, $userKey ) = $self->crypter->registrationVerify($resp); if ( $keyHandle and $userKey ) { $self->p->updatePersistentSession( $req, @@ -37,39 +46,63 @@ sub run { _u2fUserKey => encode_base64( $userKey, '' ) } ); - return $self->p->sendHtml( - $req, - 'u2fregister', - params => { - PORTAL_URL => $self->conf->{portal}, - SKIN => $self->conf->{portalSkin}, - SUCCESS => 1 - } - ); + return [ + 200, [ 'Content-Type' => 'application/json' ], + ['{"result":1}'] + ]; } - $self->p->userError( 'U2F Registration failed: ' - . Crypt::U2F::Server::Simple::lastError() ); - return $self->p->sendHtml( - $req, - 'u2fregister', - params => { - PORTAL_URL => $self->conf->{portal}, - SKIN => $self->conf->{portalSkin}, - FAILED => 1 - } - ); + my $err = Crypt::U2F::Server::Simple::lastError(); + $self->p->userError("U2F Registration failed: $err"); + return $self->p->sendError( $req, $err, 200 ); } - my $challenge = $self->crypter->registrationChallenge; - return $self->p->sendHtml( - $req, - 'u2fregister', - params => { - PORTAL_URL => $self->conf->{portal}, - SKIN => $self->conf->{portalSkin}, - CHALLENGE => $challenge, - APPID => $self->origin + if ( $action eq 'verify' ) { + my ( $err, $error ) = $self->loadUser($req); + if ( $err == -1 ) { + return $self->p->sendError( $req, "U2F error: $error", 200 ); } - ); + elsif ( $err == 0 ) { + return $self->p->sendError( $req, "noU2FKeyFound" ); + } + my $challenge = $self->crypter->authenticationChallenge; + return [ 200, [ 'Content-Type' => 'application/json' ], [$challenge] ]; + } + if ( $action eq 'signature' ) { + my $resp; + unless ( $resp = $req->param('signature') ) { + return $self->p->sendError( $req, 'Missing signature parameter', + 400 ); + } + my ( $err, $error ) = $self->loadUser($req); + if ( $err == -1 ) { + return $self->p->sendError( $req, "U2F error: $error", 200 ); + } + elsif ( $err == 0 ) { + return $self->p->sendError( $req, "noU2FKeyFound" ); + } + my $res = ( $self->crypter->authenticationVerify($resp) ? 1 : 0 ); + return [ + 200, [ 'Content-Type' => 'application/json' ], + [qq'{"result":$res}'] + ]; + } +} + +sub loadUser { + my ( $self, $req ) = @_; + my $uid = $req->userData->{ $self->conf->{whatToTrace} }; + my $session = getPersistentSession($uid); + my $kh = $session->data->{_u2fKeyHandle}; + my $uk = $session->data->{_u2fUserKey}; + unless ( $kh and $uk ) { + return 0; + } + $self->crypter->{keyHandle} = decode_base64($kh); + $self->crypter->{publicKey} = decode_base64($uk); + unless ( $self->crypter->setKeyHandle and $self->crypter->setPublicKey ) { + my $error = Crypt::U2F::Server::Simple::lastError(); + return ( -1, $error ); + } + return 1; } 1;