From 25365061deaab9d2313e2383a9cbb226c5611775 Mon Sep 17 00:00:00 2001 From: Xavier Guimard Date: Mon, 23 May 2016 21:52:32 +0000 Subject: [PATCH] In progress... (#595) --- .../lib/Lemonldap/NG/Portal/Auth/Apache.pm | 4 + .../lib/Lemonldap/NG/Portal/Auth/BrowserID.pm | 144 ++++++++++++++++++ .../lib/Lemonldap/NG/Portal/Auth/Null.pm | 8 + .../lib/Lemonldap/NG/Portal/Main/Display.pm | 7 +- .../lib/Lemonldap/NG/Portal/Main/Process.pm | 19 +-- .../lib/Lemonldap/NG/Portal/Main/Request.pm | 10 +- lemonldap-ng-portal/t/20-XSS-protection.t | 2 +- 7 files changed, 171 insertions(+), 23 deletions(-) create mode 100644 lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Auth/BrowserID.pm diff --git a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Auth/Apache.pm b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Auth/Apache.pm index ac6c28dd0..d6b13a361 100644 --- a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Auth/Apache.pm +++ b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Auth/Apache.pm @@ -38,4 +38,8 @@ sub authLogout { PE_OK; } +sub getDisplayType { + return 'logo'; +} + 1; diff --git a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Auth/BrowserID.pm b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Auth/BrowserID.pm new file mode 100644 index 000000000..90f699aaf --- /dev/null +++ b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Auth/BrowserID.pm @@ -0,0 +1,144 @@ +package Lemonldap::NG::Portal::Auth::BrowserID; + +use strict; +use Mouse; +use JSON; +use LWP::UserAgent; +use HTTP::Request; +use Lemonldap::NG::Portal::Main::Constants qw(PE_OK PE_ERROR PE_BADCREDENTIALS PE_FIRSTACCESS); + +our $VERSION = '2.0.0'; + +extends 'Lemonldap::NG::Portal::Auth::Base'; + +## @method LWP::UserAgent ua() +# @return LWP::UserAgent object +has ua => ( + is => 'rw', + lasy => 1, + builder => sub { + + # TODO : LWP options to use a proxy for example + my $ua = LWP::UserAgent->new(); + push @{ $ua->requests_redirectable }, 'POST'; + $ua->env_proxy(); + return $ua; + } +); + +# Initialization: enables Browser ID (required for templates) and +# LWP::UserAgent object +sub init { + my ($self) = @_; + $self->p->{customParameters}->{browserIdEnabled} = 1; + foreach ( + qw(browserIdSiteName browserIdSiteLogo browserIdBackgroundColor browserIdAutoLogin) + ) + { + $self->p->{customParameters}->{$_} = $self->conf->{$_} + if ( $self->conf->{$_} ); + } + return $self->ua; +} + +## @apmethod int extractFormInfo() +# Get BrowserID assertion +# @return Lemonldap::NG::Portal constant +sub extractFormInfo { + my ( $self, $req ) = @_; + + # Assertion should be browserIdAssertion parameter + if ( my $browserIdAssertion = $req->param('browserIdAssertion') ) { + $self->lmLog( "BrowserID Assertion found: $browserIdAssertion", + 'debug' ); + + # Resolve assertion + my $postdata = + "assertion=$browserIdAssertion&audience=" . $self->conf->{portal}; + + $self->lmLog( + "Send $postdata to " . $self->conf->{browserIdVerificationURL}, + 'debug' ); + + # Prepare and launch request to BrowserID IdP + my $request = HTTP::Request->new( + 'POST' => $self->conf->{browserIdVerificationURL} ); + $request->content_type('application/x-www-form-urlencoded'); + $request->content($postdata); + + my $answer = $self->ua()->request($request); + + $self->lmLog( "Verification response: " . $answer->as_string, 'debug' ); + + # Check if HTTP response is OK + if ( $answer->code() == "200" ) { + + # Get JSON answser and decode it + my $browserIdAnswer = $answer->content; + $self->lmLog( "Received BrowserID answer: $browserIdAnswer", + 'debug' ); + eval { $browserIdAnswer = JSON::from_json($browserIdAnswer); }; + if ($@) { + $self->lmLog( "JSON decode error: $@", 'error' ); + return PE_ERROR; + } + + # Then check for IdP response + if ( $browserIdAnswer->{status} eq "okay" ) { + $req->user( $browserIdAnswer->{email} ); + $self->lmLog( + "Found user $req->user in BrowserID verification answer", + 'debug' ); + return PE_OK; + } + + # Look for IdP rejection reason + else { + if ( $browserIdAnswer->{reason} ) { + $self->lmLog( +"Assertion $browserIdAssertion verification error: $browserIdAnswer->{reason}", + 'error' + ); + + } + else { + $self->lmLog( +"Assertion $browserIdAssertion not verified by BrowserID provider", + 'error' + ); + } + return PE_BADCREDENTIALS; + } + } + else { + $self->lmLog( +"Fail to validate BrowserId assertion $browserIdAssertion: BrowserID server has returned a $answer->code code", + 'error' + ); + return PE_ERROR; + } + } + + # No assertion, return to login page with BrowserID login script + $req->{customParameters}->{browserIdLoadLoginScript} = 1; + return PE_FIRSTACCESS; +} + +sub authenticate { + PE_OK; +} + +sub authLogout { + $_[1]->{customParameters}->{browserIdLoadLoginScript} = 1; + PE_OK; +} + +sub authForce { + return PE_OK; +} + +sub getDisplayType { + return 'logo'; +} + +1; diff --git a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Auth/Null.pm b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Auth/Null.pm index 8ec20f53d..9b8d932ef 100644 --- a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Auth/Null.pm +++ b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Auth/Null.pm @@ -30,4 +30,12 @@ sub authLogout { PE_OK; } +sub getDisplayType { + return 'logo'; +} + +sub getDisplayType { + return ''; +} + 1; 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 2d1e09904..669325e14 100644 --- a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Main/Display.pm +++ b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Main/Display.pm @@ -15,7 +15,7 @@ sub display { my $skin_dir = $self->conf->{templatesDir}; my $skinfile; my %templateParams = - ( %{ $self->customParameters }, %{ $req->customParameters } ); + ( %{ $self->customParameters }, %{ $req->{customParameters} // {} } ); # 0. Display error page if ( my $http_error = $req->param('lmError') ) { @@ -97,7 +97,7 @@ sub display { # 2.1 A notification has to be done (session is created but hidden and unusable # until the user has accept the message) - elsif ( my $notif = $req->notification ) { + elsif ( my $notif = $req->{notification} ) { $skinfile = 'notification'; %templateParams = ( AUTH_ERROR_TYPE => $req->error_type, @@ -281,7 +281,8 @@ sub display { DISPLAY_YUBIKEY_FORM => $displayType eq "yubikeyform" ? 1 : 0, DISPLAY_LOGO_FORM => $displayType eq "logo" ? 1 : 0, - module => $displayType eq "logo" ? $self->get_module('auth') + module => $displayType eq "logo" + ? $self->getModule( $req, 'auth' ) : "", AUTH_LOOP => [], PORTAL_URL => diff --git a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Main/Process.pm b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Main/Process.pm index 026361598..67304fdab 100644 --- a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Main/Process.pm +++ b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Main/Process.pm @@ -119,7 +119,7 @@ sub checkLogout { } sub authLogout { - my $self = shift; + my ( $self, $req ) = @_; return $self->_authentication->authLogout(@_); } @@ -215,18 +215,18 @@ sub checkXSSAttack { # ------------------------------------------------------- sub extractFormInfo { - my $self = shift; - return $self->_authentication->extractFormInfo(@_); + my ( $self, $req ) = @_; + return $self->_authentication->extractFormInfo($req); } sub getUser { - my $self = shift; - return $self->_userDB->getUser(@_); + my ( $self, $req ) = @_; + return $self->_userDB->getUser($req); } sub authenticate { - my $self = shift; - return $self->_authentication->authenticate(@_); + my ( $self, $req ) = @_; + return $self->_authentication->authenticate($req); } # Third block: Session data providing @@ -288,7 +288,7 @@ sub setMacros { sub setGroups { my ( $self, $req ) = @_; - return $self->_userDB->setGroups(@_); + return $self->_userDB->setGroups($req); } sub setPersistentSessionInfo { @@ -396,8 +396,6 @@ sub buildCookie { expires => $self->conf->{cookieExpiration}, ); if ( $self->conf->{securedCookie} >= 2 ) { - shift; - shift; push @{ $req->respHeaders }, 'Set-Cookie' => $self->cookie( name => $self->conf->{cookieName} . "http", @@ -407,7 +405,6 @@ sub buildCookie { secure => 0, HttpOnly => $self->conf->{httpOnly}, expires => $self->conf->{cookieExpiration}, - @_, ); } PE_OK; diff --git a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Main/Request.pm b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Main/Request.pm index ca3492053..f252f3897 100644 --- a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Main/Request.pm +++ b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Main/Request.pm @@ -31,7 +31,7 @@ extends 'Lemonldap::NG::Common::PSGI::Request'; has steps => ( is => 'rw' ); # Datas shared between methods -has datas => ( is => 'rw', default => sub { {} } ); +has datas => ( is => 'rw' ); # Session datas when created has id => ( is => 'rw' ); @@ -45,7 +45,7 @@ has respCookies => ( is => 'rw' ); has template => ( is => 'rw' ); # Custom template parameters -has customParameters => ( is => 'rw', default => sub { {} } ); +has customParameters => ( is => 'rw' ); # Boolean to indicate that response must be a redirection has mustRedirect => ( is => 'rw' ); @@ -62,12 +62,6 @@ has info => ( is => 'rw' ); # Menu error has menuError => ( is => 'rw' ); -# Notification -has notification => ( is => 'rw' ); - -has _authChoice => ( is => 'rw' ); -has _openidPortal => ( is => 'rw' ); - # Error type sub error_type { my $req = shift; diff --git a/lemonldap-ng-portal/t/20-XSS-protection.t b/lemonldap-ng-portal/t/20-XSS-protection.t index 620a0471a..0a96190b8 100644 --- a/lemonldap-ng-portal/t/20-XSS-protection.t +++ b/lemonldap-ng-portal/t/20-XSS-protection.t @@ -112,7 +112,7 @@ my $id; ok( $id = getCookies($res)->{lemonldap}, 'Get LLNG cookie' ) or explain( $res, 'Set-Cookie: something' ); -count(4); +count(3); while ( defined( my $url = shift(@tests) ) ) { last if ( $url eq 'LOGOUT' );