Merge branch '2775' into 'v2.0'

Allow to continue notification process with JSON response

See merge request lemonldap-ng/lemonldap-ng!279
This commit is contained in:
Christophe Maudoux 2022-07-20 07:45:40 +00:00
commit fede7f01d3
6 changed files with 90 additions and 53 deletions

View File

@ -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.

View File

@ -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 ) )

View File

@ -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 } ] );

View File

@ -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;

View File

@ -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;
}

View File

@ -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);