diff --git a/doc/sources/admin/notifications.rst b/doc/sources/admin/notifications.rst index 87217ea77f795fa1e4fd72419519f22c83f30a12..2656a4f85dad6c8070837de973a54f67cfda29d5 100644 --- a/doc/sources/admin/notifications.rst +++ b/doc/sources/admin/notifications.rst @@ -515,3 +515,14 @@ connect with any user, the message will be prompted. .. |image1| image:: /documentation/portal-notification.png :class: align-center + +JSON response +~~~~~~~~~~~~~ + +If a notification is pending, JSON response fields are: + +- ``result``: ``0`` +- ``error``: ``36`` +- ``ciphered_id``: a ciphered session id is returned in this field. + This id can be used to forward and continue the notification process if you call the REST ``/notifback`` endpoint + with a LL::NG cookie built with this id. diff --git a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Lib/Notifications/JSON.pm b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Lib/Notifications/JSON.pm index 86af1ef1081f22180d6be20d29ea270a6a7a3f58..9459ffab621d98feabeb18c23f82b6e46d7b2058 100644 --- a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Lib/Notifications/JSON.pm +++ b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Lib/Notifications/JSON.pm @@ -5,7 +5,7 @@ use Mouse; use JSON qw(from_json); use POSIX qw(strftime); -our $VERSION = '2.0.12'; +our $VERSION = '2.0.15'; no warnings 'redefine'; @@ -178,9 +178,8 @@ sub getNotifBack { # Search for Lemonldap::NG cookie (ciphered) my $id; - unless ( $id = $req->cookies->{ $self->{conf}->{cookieName} } ) { - return $self->p->sendError( $req, 'No cookie found', 401 ); - } + return $self->p->sendError( $req, 'No cookie found', 401 ) + unless ( $id = $req->cookies->{ $self->{conf}->{cookieName} } ); if ( $req->param('cancel') ) { $self->logger->debug('Cancel called -> remove ciphered cookie'); @@ -197,12 +196,13 @@ sub getNotifBack { return $self->p->do( $req, [] ); } - # Look if all notifications have been accepted. If not, redirect to - # portal + # Look if all notifications have been accepted. + # If not, redirect to Portal # Try to decrypt Lemonldap::NG ciphered cookie $id = $self->p->HANDLER->tsv->{cipher}->decrypt($id) - or return $self->p->sendError( $req, 'Unable to decrypt', 400 ); + or + return $self->p->sendError( $req, 'Unable to decrypt ciphered id', 400 ); # Check that session exists $req->userData( $self->p->HANDLER->retrieveSession( $req, $id ) ) diff --git a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Lib/Notifications/XML.pm b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Lib/Notifications/XML.pm index c80562209edee15b958655daefa8b64152ce751a..a981c8671fda7c47165cf5e54984a2e7bf1cbab8 100644 --- a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Lib/Notifications/XML.pm +++ b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Lib/Notifications/XML.pm @@ -236,9 +236,8 @@ sub getNotifBack { # Search for Lemonldap::NG cookie (ciphered) my $id; - unless ( $id = $req->cookies->{ $self->{conf}->{cookieName} } ) { - return $self->p->sendError( $req, 'No cookie found', 401 ); - } + return $self->p->sendError( $req, 'No cookie found', 401 ) + unless ( $id = $req->cookies->{ $self->{conf}->{cookieName} } ); if ( $req->param('cancel') ) { $self->logger->debug('Cancel called -> remove ciphered cookie'); @@ -255,12 +254,12 @@ sub getNotifBack { return $self->p->do( $req, [] ); } - # Look if all notifications have been accepted. If not, redirect to - # portal + # Look if all notifications have been accepted. + # If not, redirect to Portal # Try to decrypt Lemonldap::NG ciphered cookie $id = $self->p->HANDLER->tsv->{cipher}->decrypt($id) - or return $self->sendError( $req, 'Unable to decrypt', 500 ); + or return $self->sendError( $req, 'Unable to decrypt ciphered id', 400 ); # Check that session exists $req->userData( $self->p->HANDLER->retrieveSession( $req, $id ) ) @@ -381,7 +380,6 @@ sub getNotifBack { # One pending notification has been found and not accepted, # restart process to display pending notifications - # TODO: is it a good idea to launch all 'endAuth' subs ? $self->logger->debug( 'Pending notification has been found and not accepted'); return $self->p->do( $req, [ @{ $self->p->endAuth } ] ); 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 27cf8a6f70a7717ca0491c69b519a9c29d1f10ef..51b7308a8581e6811c47dc21439748efe33433ac 100644 --- a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Main/Process.pm +++ b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Main/Process.pm @@ -40,7 +40,7 @@ sub process { } } $self->logger->debug( "Returned " . $self->_formatProcessResult($err) ) - if ($err); + if $err; return $err; } @@ -79,7 +79,7 @@ sub _formatProcessResult { sub restoreArgs { my ( $self, $req ) = @_; $req->mustRedirect(1); - PE_OK; + return PE_OK; } sub importHandlerData { @@ -87,7 +87,7 @@ sub importHandlerData { $req->{sessionInfo} = $req->userData; $req->id( $req->sessionInfo->{_session_id} ); $req->user( $req->sessionInfo->{ $self->conf->{whatToTrace} } ); - PE_OK; + return PE_OK; } # Verify url and confirm parameter @@ -191,7 +191,7 @@ sub controlUrl { $req->data->{_url} = $req->pdata->{_url} = encode_base64( $req->{urldc}, '' ); # Avoid \n or \r } - PE_OK; + return PE_OK; } sub checkLogout { @@ -200,7 +200,7 @@ sub checkLogout { $req->steps( [ @{ $self->beforeLogout }, 'authLogout', 'deleteSession' ] ); } - PE_OK; + return PE_OK; } sub checkUnauthLogout { @@ -221,7 +221,7 @@ sub checkUnauthLogout { ); $req->steps( [ sub { PE_LOGOUT_OK } ] ); } - PE_OK; + return PE_OK; } sub authLogout { @@ -461,7 +461,6 @@ sub setSessionInfo { # Call UserDB setSessionInfo return $self->_userDB->setSessionInfo($req); - PE_OK; } sub setMacros { @@ -470,7 +469,7 @@ sub setMacros { $req->{sessionInfo}->{$_} = $self->_macros->{$_}->( $req, $req->sessionInfo ); } - PE_OK; + return PE_OK; } sub setGroups { @@ -488,7 +487,6 @@ sub setPersistentSessionInfo { return PE_OK unless ( $key and length($key) ); my $persistentSession = $self->getPersistentSession($key); - if ($persistentSession) { $self->logger->debug("Persistent session found for $key"); foreach my $k ( keys %{ $persistentSession->data } ) { @@ -500,7 +498,7 @@ sub setPersistentSessionInfo { } } } - PE_OK; + return PE_OK; } sub setLocalGroups { @@ -520,7 +518,7 @@ sub setLocalGroups { $req->{sessionInfo}->{groups} =~ s/^$self->{conf}->{multiValuesSeparator}//o; } - PE_OK; + return PE_OK; } sub store { @@ -547,11 +545,10 @@ sub store { foreach my $k ( keys %{ $req->{sessionInfo} } ) { next unless defined $req->{sessionInfo}->{$k}; my $displayValue = $req->{sessionInfo}->{$k}; - if ( $self->conf->{hiddenAttributes} - and $self->conf->{hiddenAttributes} =~ /\b$k\b/ ) - { - $displayValue = '****'; - } + $displayValue = '****' + if ( $self->conf->{hiddenAttributes} + and $self->conf->{hiddenAttributes} =~ /\b$k\b/ ); + $self->logger->debug("Store $displayValue in session key $k"); $self->_dump($displayValue) if ref($displayValue); $infos->{$k} = $req->{sessionInfo}->{$k}; @@ -563,7 +560,7 @@ sub store { force => $req->{force}, info => $infos ); - return PE_APACHESESSIONERROR unless ($session); + return PE_APACHESESSIONERROR unless $session; # Update current request $req->id( $session->id ); @@ -582,7 +579,7 @@ sub store { . $req->{sessionInfo}->{_httpSession} ); } $req->refresh(0); - PE_OK; + return PE_OK; } sub buildCookie { @@ -616,7 +613,7 @@ sub buildCookie { . $ref->{ $self->conf->{whatToTrace} } . " successfully authenticated at level $ref->{authenticationLevel}" ); - PE_OK; + return PE_OK; } sub secondFactor { @@ -629,7 +626,7 @@ sub storeHistory { if ( $self->conf->{loginHistoryEnabled} ) { $self->registerLogin($req); } - PE_OK; + return PE_OK; } 1; 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 d3323e3a5f1683f19b73c6da2e596e07071f6046..3f77b406a3f4a58d90c579e0d46f8fac06d21946 100644 --- a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Main/Run.pm +++ b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Main/Run.pm @@ -208,13 +208,10 @@ sub refresh { if (/^_/) { # But not OIDC tokens, which can be refreshed - if ( + delete $data{$_} + if ( /^(_oidc_access_token|_oidc_refresh_token|_oidc_access_token_eol)$/ - ) - { - delete $data{$_}; - } - + ); } # Other variables should be refreshed @@ -306,14 +303,10 @@ sub do { } # Update history - if ( $err == PE_SENDRESPONSE ) { - return $req->response; - } + return $req->response if $err == PE_SENDRESPONSE; # Remove userData if authentication fails - if ( $err == PE_BADCREDENTIALS or $err == PE_BADOTP ) { - $req->userData( {} ); - } + $req->userData( {} ) if ( $err == PE_BADCREDENTIALS or $err == PE_BADOTP ); if ( !$self->conf->{noAjaxHook} and $req->wantJSON ) { $self->logger->debug('Processing to JSON response'); @@ -342,7 +335,15 @@ sub do { elsif ( $err > 0 and $err != PE_PASSWORD_OK and $err != PE_LOGOUT_OK ) { return $self->sendJSONresponse( $req, - { result => 0, error => $err }, + { + result => 0, + error => $err, + ( + $err == PE_NOTIFICATION && $req->id + ? ( ciphered_id => $req->id ) + : () + ) + }, code => 400 ); } @@ -956,7 +957,6 @@ sub sendHtml { { $self->logger->debug( "Add SAML Discovery Protocol URL in CSP form-action"); - $csp .= " " . $self->conf->{samlDiscoveryProtocolURL}; } $csp .= ';'; @@ -1168,12 +1168,11 @@ sub _sumUpSession { $withoutUser ? {} : { user => $session->{ $self->conf->{whatToTrace} } }; - $res->{$_} = $session->{$_} - foreach ( + $res->{$_} = $session->{$_} foreach ( "_utime", "ipAddr", keys %{ $self->conf->{sessionDataToRemember} }, keys %{ $self->pluginSessionDataToRemember } - ); + ); return $res; } diff --git a/lemonldap-ng-portal/t/40-Notifications-JSON-File.t b/lemonldap-ng-portal/t/40-Notifications-JSON-File.t index c6053297331e0443f0397df660d4d0d7496281f4..8d3e6832aa2ef043a4ae9cdf119963cdf76a024c 100644 --- a/lemonldap-ng-portal/t/40-Notifications-JSON-File.t +++ b/lemonldap-ng-portal/t/40-Notifications-JSON-File.t @@ -1,10 +1,12 @@ use Test::More; use strict; use IO::String; +use JSON; require 't/test-lib.pm'; my $res; +my $json; my $file = "$main::tmpDir/20160530_dwho_dGVzdHJlZg==.json"; open F, "> $file" or die($!); @@ -33,9 +35,39 @@ my $client = LLNG::Manager::Test->new( { } } ); +use Lemonldap::NG::Portal::Main::Constants 'PE_NOTIFICATION'; # Try to authenticate # ------------------- +ok( + $res = $client->_post( + '/', + IO::String->new( + 'user=dwho&password=dwho&url=aHR0cDovL3Rlc3QxLmV4YW1wbGUuY29tLw=='), + length => 64, + ), + 'Auth query (JSON required)' +); +ok( $json = eval { from_json( $res->[2]->[0] ) }, 'Response is JSON' ) + or print STDERR "$@\n" . Dumper($res); +ok( $json->{result} == 0, ' Good result' ) + or explain( $json, 'result => 0' ); +ok( $json->{error} == PE_NOTIFICATION, ' Notificationtion is pending' ) + or explain( $json, 'error => 36' ); +my $id = $json->{ciphered_id}; + +# Verify that cookie can be deciphered (ciphered_id is valid) +ok( + $res = $client->_get( + '/notifback', + accept => 'text/html', + cookie => "lemonldap=$id" + ), + 'Test received Id' +); +count(5); +expectForm( $res, undef, '/notifback', 'reference1x1', 'url' ); + ok( $res = $client->_post( '/', @@ -48,7 +80,7 @@ ok( ); count(1); expectOK($res); -my $id = expectCookie($res); +$id = expectCookie($res); expectForm( $res, undef, '/notifback', 'reference1x1', 'url' ); # Verify that cookie is ciphered (session invalid) @@ -58,7 +90,7 @@ ok( query => 'url=aHR0cDovL3Rlc3QxLmV4YW1wbGUuY29tLw==', cookie => "lemonldap=$id", ), - 'Test cookie received' + 'Test received cookie' ); count(1); expectReject($res);