Merge branch 'v2.0' into globalLogout

This commit is contained in:
Christophe Maudoux 2019-11-18 22:15:23 +01:00
commit ef5d0cde48
27 changed files with 215 additions and 65 deletions

View File

@ -161,7 +161,8 @@ sub defaultValues {
'multiValuesSeparator' => '; ',
'mySessionAuthorizedRWKeys' =>
[ '_appsListOrder', '_oidcConnectedRP', '_oidcConsents' ],
'notificationServerPOST' => 1,
'notificationDefaultCond' => '',
'notificationServerPOST' => 1,
'notificationServerSentAttributes' =>
'uid reference date title subtitle text check',
'notificationStorage' => 'File',

View File

@ -4,11 +4,12 @@ use strict;
use Mouse;
use JSON qw(from_json to_json);
our $VERSION = '2.0.6';
our $VERSION = '2.0.7';
sub newNotification {
my ( $self, $jsonString ) = @_;
my ( $self, $jsonString, $defaultCond ) = @_;
my $json;
$defaultCond ||= '';
eval { $json = from_json( $jsonString, { allow_nonref => 1 } ) };
if ( my $err = $@ ) {
eval { $self->logger->error("Unable to decode JSON file: $err") };
@ -35,7 +36,8 @@ sub newNotification {
}
push @data, $tmp;
}
push @data, ( $notif->{condition} // '' );
$notif->{condition} //= $defaultCond;
push @data, ( $notif->{condition} );
my $body = to_json($notif);
push @notifs, [ @data, $body ];
}

View File

@ -4,7 +4,7 @@ use strict;
use Mouse;
use XML::LibXML;
our $VERSION = '2.0.6';
our $VERSION = '2.0.7';
# XML parser
has parser => (
@ -18,7 +18,8 @@ has parser => (
# @param $xml XML string containing notification
# @return number of notifications done
sub newNotification {
my ( $self, $xml ) = @_;
my ( $self, $xml, $defaultCond ) = @_;
$defaultCond ||= '';
eval { $xml = $self->parser->parse_string($xml) };
if ( my $err = $@ ) {
eval { $self->logger->error("Unable to read XML file : $err") };
@ -53,7 +54,9 @@ sub newNotification {
if ( $tmp = $notif->getAttribute($_) ) {
push @data, $tmp;
}
else { push @data, ""; }
else {
push @data, $defaultCond;
}
}
my $result = XML::LibXML::Document->new( $version, $encoding );

View File

@ -1804,6 +1804,10 @@ qr/^(?:\*\.)?(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.])*(?:[a-zA-Z][
'default' => 0,
'type' => 'bool'
},
'notificationDefaultCond' => {
'default' => '',
'type' => 'text'
},
'notificationServer' => {
'default' => 0,
'type' => 'bool'

View File

@ -1106,6 +1106,11 @@ sub attributes {
type => 'bool',
documentation => 'Notification server activation',
},
notificationDefaultCond => {
type => 'text',
default => '',
documentation => 'Notification default condition',
},
notificationServerGET => {
default => 0,
type => 'bool',

View File

@ -615,6 +615,7 @@ sub tree {
help => 'notifications.html#server',
nodes => [
'notificationServer',
'notificationDefaultCond',
'notificationServerSentAttributes',
{
title =>

View File

@ -453,7 +453,6 @@ sub tests {
# Warn if 2F dependencies seem missing
sfaDependencies => sub {
my $ok = 0;
foreach (qw(u totp utotp yubikey)) {
$ok ||= $conf->{ $_ . '2fActivation' };
@ -684,8 +683,24 @@ sub tests {
"Notifications enabled WITHOUT persistent session storage" )
if ( $conf->{notification} );
return ( 1,
"BruteForceProtection plugin enabled WITHOUT persistent session storage" )
if ( $conf->{bruteForceProtection} );
"BruteForceProtection plugin enabled WITHOUT persistent session storage"
) if ( $conf->{bruteForceProtection} );
# Return
return 1;
},
# Warn if XML dependencies seem missing
xmlDependencies => sub {
return 1 unless ( $conf->{oldNotifFormat} );
eval "use XML::LibXML";
return ( 1,
"XML::LibXML module is required to enable old format notifications"
) if ($@);
eval "use XML::LibXSLT";
return ( 1,
"XML::LibXSLT module is required to enable old format notifications"
) if ($@);
# Return
return 1;

View File

@ -2,6 +2,9 @@
# LemonLDAP::NG Notifications Explorer client
###
# Max number of notifications to display (see overScheme)
max = 25
scheme = [
(v) ->
"groupBy=substr(uid,1)"
@ -11,6 +14,19 @@ scheme = [
"uid=#{v}"
]
# When number of children nodes exceeds "max" value
# and does not return "null", a level is added. See
# "$scope.updateTree" method
overScheme =
(v,level,over) ->
# "v.length > over" avoids a loop if one user opened more than "max"
# notifications
console.log 'overScheme => level', level, 'over', over
if level == 1 and v.length > over
"uid=#{v}*&groupBy=substr(uid,#{(level+over+1)})"
else
null
# Session menu
menu =
actives: [
@ -112,7 +128,7 @@ llapp.controller 'NotificationsExplorerCtrl', [ '$scope', '$translator', '$locat
$scope.stoggle = (scope) ->
node = scope.$modelValue
if node.nodes.length == 0
$scope.updateTree node.value, node.nodes, node.level, node.query
$scope.updateTree node.value, node.nodes, node.level, node.over, node.query, node.count
scope.toggle()
$scope.notifDate = (s) ->
@ -139,19 +155,34 @@ llapp.controller 'NotificationsExplorerCtrl', [ '$scope', '$translator', '$locat
$scope.init()
autoId = 0
$scope.updateTree = (value, node, level, currentQuery) ->
$scope.updateTree = (value, node, level, over, currentQuery, count) ->
$scope.waiting = true
query = scheme[level] value, currentQuery
# If number of notifications exceeds "max", call it
if count > max
if tmp = overScheme value, level, over
over++
query = tmp
level = level - 1
else
over = 0
else
over = 0
# Launch HTTP query
$http.get("#{scriptname}notifications/#{$scope.type}?#{query}").then (response) ->
data = response.data
if data.result
for n in data.values
autoId++
n.id = "node#{autoId}"
if level <scheme.length - 1
if level < scheme.length - 1
n.nodes = []
n.level = level + 1
n.query = query
n.over = over
node.push n
$scope.waiting = false
, (resp) ->
@ -246,7 +277,7 @@ llapp.controller 'NotificationsExplorerCtrl', [ '$scope', '$translator', '$locat
$scope.currentNotification = null
$q.all [
$translator.init $scope.lang
$scope.updateTree '', $scope.data, 0
$scope.updateTree '', $scope.data, 0, 0
]
.then ->
$scope.waiting = false

View File

@ -78,26 +78,26 @@ overScheme =
_whatToTrace: (t,v,level,over) ->
# "v.length > over" avoids a loop if one user opened more than "max"
# sessions
console.log 'overSchema => level', level, 'over', over
console.log 'overScheme => level', level, 'over', over
if level == 1 and v.length > over
"#{t}=#{v}*&groupBy=substr(#{t},#{(level+over+1)})"
else
null
# Note: IPv4 only
ipAddr: (t,v,level,over) ->
console.log 'overSchema => level', level, 'over', over
console.log 'overScheme => level', level, 'over', over
if level > 0 and level < 4 and !v.match(/^\d+\.\d/) and over < 2
"#{t}=#{v}*&groupBy=net(#{t},#{16*level+4*(over+1)},#{1+level+over})"
else
null
_startTime: (t,v,level,over) ->
console.log 'overSchema => level', level, 'over', over
console.log 'overScheme => level', level, 'over', over
if level > 3
"#{t}=#{v}*&groupBy=substr(#{t},#{(10+level+over)})"
else
null
_session_uid: (t,v,level,over) ->
console.log 'overSchema => level', level, 'over', over
console.log 'overScheme => level', level, 'over', over
if level == 1 and v.length > over
"#{t}=#{v}*&groupBy=substr(#{t},#{(level+over+1)})"
else

View File

@ -5,7 +5,9 @@
*/
(function() {
var llapp, menu, scheme;
var llapp, max, menu, overScheme, scheme;
max = 25;
scheme = [
function(v) {
@ -17,6 +19,15 @@
}
];
overScheme = function(v, level, over) {
console.log('overSchema => level', level, 'over', over);
if (level === 1 && v.length > over) {
return "uid=" + v + "*&groupBy=substr(uid," + (level + over + 1) + ")";
} else {
return null;
}
};
menu = {
actives: [
{
@ -131,7 +142,7 @@
var node;
node = scope.$modelValue;
if (node.nodes.length === 0) {
$scope.updateTree(node.value, node.nodes, node.level, node.query);
$scope.updateTree(node.value, node.nodes, node.level, node.over, node.query, node.count);
return scope.toggle();
}
};
@ -164,10 +175,21 @@
}
});
autoId = 0;
$scope.updateTree = function(value, node, level, currentQuery) {
var query;
$scope.updateTree = function(value, node, level, over, currentQuery, count) {
var query, tmp;
$scope.waiting = true;
query = scheme[level](value, currentQuery);
if (count > max) {
if (tmp = overScheme(value, level, over)) {
over++;
query = tmp;
level = level - 1;
} else {
over = 0;
}
} else {
over = 0;
}
return $http.get(scriptname + "notifications/" + $scope.type + "?" + query).then(function(response) {
var data, i, len, n, ref;
data = response.data;
@ -181,6 +203,7 @@
n.nodes = [];
n.level = level + 1;
n.query = query;
n.over = over;
}
node.push(n);
}
@ -301,7 +324,7 @@
$scope.data = [];
$scope.currentScope = null;
$scope.currentNotification = null;
$q.all([$translator.init($scope.lang), $scope.updateTree('', $scope.data, 0)]).then(function() {
$q.all([$translator.init($scope.lang), $scope.updateTree('', $scope.data, 0, 0)]).then(function() {
return $scope.waiting = false;
}, function(resp) {
return $scope.waiting = false;

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -83,7 +83,7 @@
overScheme = {
_whatToTrace: function(t, v, level, over) {
console.log('overSchema => level', level, 'over', over);
console.log('overScheme => level', level, 'over', over);
if (level === 1 && v.length > over) {
return t + "=" + v + "*&groupBy=substr(" + t + "," + (level + over + 1) + ")";
} else {
@ -91,7 +91,7 @@
}
},
ipAddr: function(t, v, level, over) {
console.log('overSchema => level', level, 'over', over);
console.log('overScheme => level', level, 'over', over);
if (level > 0 && level < 4 && !v.match(/^\d+\.\d/) && over < 2) {
return t + "=" + v + "*&groupBy=net(" + t + "," + (16 * level + 4 * (over + 1)) + "," + (1 + level + over) + ")";
} else {
@ -99,7 +99,7 @@
}
},
_startTime: function(t, v, level, over) {
console.log('overSchema => level', level, 'over', over);
console.log('overScheme => level', level, 'over', over);
if (level > 3) {
return t + "=" + v + "*&groupBy=substr(" + t + "," + (10 + level + over) + ")";
} else {
@ -107,7 +107,7 @@
}
},
_session_uid: function(t, v, level, over) {
console.log('overSchema => level', level, 'over', over);
console.log('overScheme => level', level, 'over', over);
if (level === 1 && v.length > over) {
return t + "=" + v + "*&groupBy=substr(" + t + "," + (level + over + 1) + ")";
} else {

File diff suppressed because one or more lines are too long

View File

@ -494,6 +494,7 @@
"notAValidPerlExpression":"عبارة بيرل ليست صحيحة",
"notification":"تفعيل",
"notifications":"إشعار",
"notificationDefaultCond":"Default condition",
"notificationServer":"إشعارالخادم",
"notificationServerDELETE":"DELETE method",
"notificationServerGET":"GET method",

View File

@ -494,6 +494,7 @@
"notAValidPerlExpression":"Not a valid Perl expression",
"notification":"Activation",
"notifications":"Notifications",
"notificationDefaultCond":"Default condition",
"notificationServer":"Notification server",
"notificationServerDELETE":"DELETE method",
"notificationServerGET":"GET method",
@ -503,8 +504,8 @@
"serverNotification":"Server",
"notificationCreated":"Notification has been created",
"notificationDeleted":"Notification deleted",
"notificationDone":"notification done",
"notificationsDone":"notifications done",
"notificationDone":"Notification done",
"notificationsDone":"Notifications done",
"notificationNotCreated":"The notification was not created",
"notificationNotDeleted":"The notification was not marked as done",
"notificationNotFound":"The notification was not found",

View File

@ -494,6 +494,7 @@
"notAValidPerlExpression":"Not a valid Perl expression",
"notification":"Activation",
"notifications":"Notifications",
"notificationDefaultCond":"Default condition",
"notificationServer":"Notification server",
"notificationServerDELETE":"DELETE method",
"notificationServerGET":"GET method",
@ -503,8 +504,8 @@
"serverNotification":"Server",
"notificationCreated":"Notification has been created",
"notificationDeleted":"Notification deleted",
"notificationDone":"notification done",
"notificationsDone":"notifications done",
"notificationDone":"Notification done",
"notificationsDone":"Notifications done",
"notificationNotCreated":"The notification was not created",
"notificationNotDeleted":"The notification was not marked as done",
"notificationNotFound":"The notification was not found",

View File

@ -494,6 +494,7 @@
"notAValidPerlExpression":"Pas une expression Perl valide",
"notification":"Activation",
"notifications":"Notifications",
"notificationDefaultCond":"Condition par défaut",
"notificationServer":"Serveur de notifications",
"notificationServerDELETE":"Méthode DELETE",
"notificationServerGET":"Méthode GET",
@ -503,8 +504,8 @@
"serverNotification":"Serveur",
"notificationCreated":"La notification a été créée",
"notificationDeleted":"La notification a été marquée comme lue",
"notificationDone":"notification validée",
"notificationsDone":"notifications validées",
"notificationDone":"Notification validée",
"notificationsDone":"Notifications validées",
"notificationNotCreated":"La notification n'a pas été créée",
"notificationNotDeleted":"La notification n'a pas été marquée comme lue",
"notificationNotFound":"La notification n'a pas été trouvée",

View File

@ -494,6 +494,7 @@
"notAValidPerlExpression":"Non una valida espressione Perl",
"notification":"Attivazione",
"notifications":"Notifiche",
"notificationDefaultCond":"Default condition",
"notificationServer":"Server di notifica",
"notificationServerDELETE":"DELETE method",
"notificationServerGET":"GET method",

View File

@ -494,6 +494,7 @@
"notAValidPerlExpression":"Không phải là một biểu thức Perl hợp lệ",
"notification":"Kích hoạt",
"notifications":"Thông báo",
"notificationDefaultCond":"Default condition",
"notificationServer":"Máy chủ Thông báo",
"notificationServerDELETE":"DELETE method",
"notificationServerGET":"GET method",
@ -503,8 +504,8 @@
"serverNotification":"Server",
"notificationCreated":"Thông báo đã được tạo ra",
"notificationDeleted":"Thông báo đã xoá",
"notificationDone":"thông báo được thực hiện",
"notificationsDone":"thông báo đã hoàn tất",
"notificationDone":"Thông báo được thực hiện",
"notificationsDone":"Thông báo đã hoàn tất",
"notificationNotCreated":"Thông báo không được tạo ra",
"notificationNotDeleted":"Thông báo không được đánh dấu là đã hoàn tất",
"notificationNotFound":"Không tìm thấy thông báo",

View File

@ -494,6 +494,7 @@
"notAValidPerlExpression":"Not a valid Perl expression",
"notification":"激活",
"notifications":"Notifications",
"notificationDefaultCond":"Default condition",
"notificationServer":"Notification server",
"notificationServerDELETE":"DELETE method",
"notificationServerGET":"GET method",
@ -503,8 +504,8 @@
"serverNotification":"Server",
"notificationCreated":"Notification has been created",
"notificationDeleted":"Notification deleted",
"notificationDone":"notification done",
"notificationsDone":"notifications done",
"notificationDone":"Notification done",
"notificationsDone":"Notifications done",
"notificationNotCreated":"The notification was not created",
"notificationNotDeleted":"The notification was not marked as done",
"notificationNotFound":"The notification was not found",

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -4,7 +4,7 @@ use strict;
use Mouse;
use JSON qw(from_json);
our $VERSION = '2.0.6';
our $VERSION = '2.0.7';
no warnings 'redefine';
@ -44,9 +44,25 @@ sub checkForNotifications {
# Get the reference
my $reference = $notif->{reference};
$self->logger->debug("Get reference $reference");
# Check condition if any
if ( my $condition = $notif->{condition} ) {
$self->logger->debug("Get condition $condition");
$condition = $self->p->HANDLER->substitute($condition);
unless ( $condition = $self->p->HANDLER->buildSub($condition) )
{
$self->logger->error( 'Notification condition error: '
. $self->p->HANDLER->tsv->{jail}->error );
next LOOP;
}
unless ( $condition->( $req, $req->sessionInfo ) ) {
$self->logger->debug(
'Notification condition not authorized');
next LOOP;
}
}
# Check it in session
if ( exists $req->{sessionInfo}->{"notification_$reference"} ) {
@ -149,7 +165,8 @@ sub getNotifBack {
# Verity that checkboxes have been checked
if ( $notif->{check} ) {
$notif->{check} = [$notif->{check}] unless ( ref($notif->{check}) eq 'ARRAY' );
$notif->{check} = [ $notif->{check} ]
unless ( ref( $notif->{check} ) eq 'ARRAY' );
if ( my $toCheckCount = @{ $notif->{check} } ) {
unless ($checks->{$refId}
and $toCheckCount == @{ $checks->{$refId} } )
@ -218,7 +235,8 @@ sub toForm {
@notifs = map {
$i++;
if ( $_->{check} ) {
$_->{check} = [$_->{check}] unless ( ref($_->{check}) eq 'ARRAY' );
$_->{check} = [ $_->{check} ]
unless ( ref( $_->{check} ) eq 'ARRAY' );
my $j = 0;
$_->{check} =
[ map { $j++; { id => '1x' . $i . 'x' . $j, value => $_ } }
@ -240,8 +258,10 @@ sub notificationServer {
my ( $res, $err );
if ( $req->method =~ /^POST$/i ) {
$self->p->logger->debug("POST request");
( $res, $err ) =
eval { $self->notifObject->newNotification( $req->content ) };
( $res, $err ) = eval {
$self->notifObject->newNotification( $req->content,
$self->conf->{notificationDefaultCond} );
};
return $self->p->sendError( $req, $@, 500 ) if ($@);
}
elsif ( $req->method =~ /^GET$/i ) {

View File

@ -5,7 +5,7 @@ use Mouse;
use XML::LibXML;
use XML::LibXSLT;
our $VERSION = '2.0.6';
our $VERSION = '2.0.7';
# Lemonldap::NG::Portal::Main::Plugin provides addAuthRoute() and
# addUnauthRoute() methods in addition of Lemonldap::NG::Common::Module.
@ -74,7 +74,6 @@ sub checkForNotifications {
# Get the reference
my $reference = $notif->getAttribute('reference');
$self->logger->debug("Get reference $reference");
# Check it in session
@ -90,10 +89,7 @@ sub checkForNotifications {
}
# Check condition if any
my $condition = $notif->getAttribute('condition');
if ($condition) {
if ( my $condition = $notif->getAttribute('condition') ) {
$self->logger->debug("Get condition $condition");
$condition = $self->p->HANDLER->substitute($condition);
unless ( $condition = $self->p->HANDLER->buildSub($condition) )
@ -103,7 +99,6 @@ sub checkForNotifications {
$notif->unbindNode();
next LOOP;
}
unless ( $condition->( $req, $req->sessionInfo ) ) {
$self->logger->debug(
'Notification condition not authorized');
@ -113,7 +108,6 @@ sub checkForNotifications {
next LOOP;
}
}
$j++;
}
@ -260,7 +254,7 @@ sub getNotifBack {
# launch 'controlUrl' to restore "urldc" using do()
$self->logger->debug('All pending notifications have been accepted');
$self->p->rebuildCookies($req);
return $self->p->do( $req, ['controlUrl', @{ $self->p->endAuth }] );
return $self->p->do( $req, [ 'controlUrl', @{ $self->p->endAuth } ] );
}
else {
# No notifications checked here, this entry point must not be called.
@ -299,7 +293,8 @@ sub notificationServer {
sub newNotification {
my ( $self, $req, $xml ) = @_;
return $self->notifObject->newNotification($xml);
return $self->notifObject->newNotification( $xml,
$self->conf->{notificationDefaultCond} );
}
1;

View File

@ -3,9 +3,9 @@ use strict;
use IO::String;
my $res;
my $maintests = 9;
my $maintests = 10;
require 't/test-lib.pm';
my $file = tempdb();
my $file = tempdb();
SKIP: {
eval { require DBI; require DBD::SQLite; };
@ -28,7 +28,7 @@ q{INSERT INTO notifications VALUES ('dwho','testref','2016-05-30 00:00:00',?,nul
"title": "Test title",
"subtitle": "Test subtitle",
"text": "This is a test text",
"check": ["Accept test"]
"check": "Accept test"
}
]'
);
@ -40,11 +40,28 @@ q{INSERT INTO notifications VALUES ('dwho','testref2','2016-05-30 00:00:00',?,nu
"uid": "dwho",
"date": "2016-05-29",
"reference": "testref2",
"condition": "\'1\'",
"title": "Test2 title",
"subtitle": "Test2 subtitle",
"text": "This is a second test text",
"check": ["Accept test"]
}
]'
);
$dbh->prepare(
q{INSERT INTO notifications VALUES ('rtyler','testref','2016-05-30 00:00:00',?,null,null)}
)->execute(
'[
{
"uid": "rtyler",
"date": "2016-05-29",
"reference": "testref",
"condition": "\'0\'",
"title": "Test title",
"subtitle": "Test subtitle",
"text": "This is a test text",
"check": ["Accept test"]
}
]'
);
@ -131,6 +148,7 @@ q{INSERT INTO notifications VALUES ('dwho','testref2','2016-05-30 00:00:00',?,nu
!defined( $cookies->{lemonldappdata} ),
" Make sure no pdata is returned"
);
$client->logout($id);
# Verify that notification was tagged as 'done'
my $sth =
@ -140,10 +158,26 @@ q{INSERT INTO notifications VALUES ('dwho','testref2','2016-05-30 00:00:00',?,nu
while ( $sth->fetchrow_hashref ) { $i++ }
ok( $i == 2, 'Notification was deleted' );
# Try to authenticate
# -------------------
ok(
$res = $client->_post(
'/',
IO::String->new(
'user=rtyler&password=rtyler&url=aHR0cDovL3Rlc3QxLmV4YW1wbGUuY29tLw=='
),
accept => 'text/html',
length => 68,
),
'Auth query'
);
expectRedirection( $res, 'http://test1.example.com/' );
$id = expectCookie($res);
$client->logout($id);
clean_sessions();
eval { unlink $file };
}
count($maintests);

View File

@ -270,7 +270,12 @@ ok(
m%<input type="checkbox" name="check1x2x1" id="1x2x1" value="accepted">I agree</label>%,
'Checkbox is displayed'
) or print STDERR Dumper( $res->[2]->[0] );
count(3);
my @c = ( $res->[2]->[0] =~ m%<input type="checkbox"%gs );
## Three entries found
ok( @c == 1, ' -> One checkbox found' )
or explain( $res->[2]->[0], "Number of checkbox(es) found = " . scalar @c );
count(4);
# Try to validate notification
my $str = 'reference1x1=testref&check1x2x1=accepted';
@ -312,7 +317,12 @@ ok(
m%<input type="checkbox" name="check1x1x2" id="1x1x2" value="accepted">I am sure</label>%,
'Checkbox is displayed'
) or print STDERR Dumper( $res->[2]->[0] );
count(4);
@c = ( $res->[2]->[0] =~ m%<input type="checkbox"%gs );
## Three entries found
ok( @c == 2, ' -> Two checkboxes found' )
or explain( $res->[2]->[0], "Number of checkbox(es) found = " . scalar @c );
count(5);
# Try to validate notification
$str = 'reference1x1=testref&check1x1x1=accepted&check1x1x2=accepted';