diff --git a/lemonldap-ng-common/scripts/lemonldap-ng-cli b/lemonldap-ng-common/scripts/lemonldap-ng-cli
index 834c773c9..21556d21d 100755
--- a/lemonldap-ng-common/scripts/lemonldap-ng-cli
+++ b/lemonldap-ng-common/scripts/lemonldap-ng-cli
@@ -22,7 +22,7 @@ for ( my $i = 0 ; $i < @ARGV ; $i++ ) {
$action ||= "help";
-if ( $action =~ /^(?:[gs]et|(?:add|del)Key)$/ ) {
+if ( $action =~ /^(?:[gs]et|(?:add|del)Key|save|restore)$/ ) {
eval { require Lemonldap::NG::Manager::Cli; };
die "Manager libraries not available, aborting ($@)" if ($@);
Lemonldap::NG::Manager::Cli->run(@ARGV);
@@ -61,18 +61,31 @@ lemonldap-ng-cli - Command-line manager for Lemonldap::NG web-SSO system.
=head1 SYNOPSIS
- # Get information about current configuration
+Get information about current configuration
+
$ lemonldap-ng-cli info
- # Update local configuration cache
+Update local configuration cache
+
$ lemonldap-ng-cli update-cache
- # Get some configuration parameter values
+Save configuration
+
+ $ lemonldap-ng-cli save >conf.json
+
+Restore configuration
+
+ $ lemonldap-ng-cli restore conf.json
+ # OR
+ $ lemonldap-ng-cli restore -
and L
+=head2 Available commands
+
+=over
+
+=item info
+
+=item update-cache
+
+=item save
+
+=item restore
+
+=item get
+
+=item set
+
+=item addKey
+
+=item delKey
+
+=back
+
=head1 SEE ALSO
L, L
diff --git a/lemonldap-ng-handler/t/65-Lemonldap-NG-Handler-PSGI-ServiceToken.t b/lemonldap-ng-handler/t/65-Lemonldap-NG-Handler-PSGI-ServiceToken.t
index bff8988a9..d333d74d7 100644
--- a/lemonldap-ng-handler/t/65-Lemonldap-NG-Handler-PSGI-ServiceToken.t
+++ b/lemonldap-ng-handler/t/65-Lemonldap-NG-Handler-PSGI-ServiceToken.t
@@ -41,6 +41,7 @@ ok(
ok( $res->[0] == 200, 'Code is 200' ) or explain( $res->[0], 200 );
count(2);
+diag 'Waiting';
sleep 2;
ok(
@@ -54,6 +55,7 @@ ok(
ok( $res->[0] == 200, 'Code is 200' ) or explain( $res->[0], 200 );
count(2);
+diag 'Waiting';
sleep 1;
ok(
@@ -67,6 +69,7 @@ ok(
ok( $res->[0] == 302, 'Code is 200' ) or explain( $res->[0], 302 );
count(2);
+diag 'Waiting';
sleep 1;
ok(
@@ -80,6 +83,7 @@ ok(
ok( $res->[0] == 200, 'Code is 200' ) or explain( $res->[0], 200 );
count(2);
+diag 'Waiting';
sleep 1;
ok(
diff --git a/lemonldap-ng-manager/lib/Lemonldap/NG/Manager/Cli.pm b/lemonldap-ng-manager/lib/Lemonldap/NG/Manager/Cli.pm
index 329ca2f4b..a01425ccd 100644
--- a/lemonldap-ng-manager/lib/Lemonldap/NG/Manager/Cli.pm
+++ b/lemonldap-ng-manager/lib/Lemonldap/NG/Manager/Cli.pm
@@ -194,6 +194,31 @@ sub lastCfg {
return $self->jsonResponse('/confs/latest')->{cfgNum};
}
+sub save {
+ my ($self) = @_;
+ my $conf = $self->jsonResponse( '/confs/latest', 'full=1' );
+ my $json = JSON->new->indent->canonical;
+ print $json->encode($conf);
+}
+
+sub restore {
+ my ( $self, $file ) = @_;
+ require IO::String;
+ my $conf;
+ if ( $file eq '-' ) {
+ $conf = join '', ;
+ }
+ else {
+ open my $f, $file;
+ $conf = join '', <$f>;
+ close $f;
+ }
+ my $res = $self->_post( '/confs/raw', '', IO::String->new($conf),
+ 'application/json', length($conf) );
+ use Data::Dumper;
+ print STDERR Dumper($res);
+}
+
sub _getKey {
my ( $self, $key ) = @_;
my $sep = $self->sep;
@@ -304,7 +329,7 @@ sub run {
}
$self->cfgNum( $self->lastCfg ) unless ( $self->cfgNum );
my $action = shift;
- unless ( $action =~ /^(?:get|set|addKey|delKey)$/ ) {
+ unless ( $action =~ /^(?:get|set|addKey|delKey|save|restore)$/ ) {
die
"unknown action $action. Only get, set, addKey or delKey are accepted";
}
diff --git a/lemonldap-ng-manager/site/coffee/sessions.coffee b/lemonldap-ng-manager/site/coffee/sessions.coffee
index 907b4b33e..c037ecb53 100644
--- a/lemonldap-ng-manager/site/coffee/sessions.coffee
+++ b/lemonldap-ng-manager/site/coffee/sessions.coffee
@@ -61,7 +61,7 @@ schemes =
overScheme =
_whatToTrace: (t,v,level,over) ->
- if level == 1 and v.length > over
+ if level == 1 and v.length < max
"#{t}=#{v}*&groupBy=substr(#{t},#{(level+over+1)})"
else
null
@@ -70,6 +70,11 @@ overScheme =
"#{t}=#{v}*&groupBy=net(#{t},#{16*level+4*(over+1)},2)"
else
null
+ _startTime: (t,v,level,over) ->
+ if level > 3
+ "#{t}=#{v}*&groupBy=substr(#{t},#{(9+level+over+1)})"
+ else
+ null
hiddenAttributes = '_password'
@@ -410,7 +415,7 @@ llapp.controller 'SessionsExplorerCtrl', ['$scope', '$translator', '$location',
scheme = if schemes[$scope.type]
schemes[$scope.type]
- # - _updateTime must be displayed as startDate
+ # - _updateTime must be displayed as startTime
else if $scope.type == '_updateTime'
schemes._startTime
diff --git a/lemonldap-ng-manager/site/htdocs/static/js/sessions.js b/lemonldap-ng-manager/site/htdocs/static/js/sessions.js
index 8ff3bf858..50c03b177 100644
--- a/lemonldap-ng-manager/site/htdocs/static/js/sessions.js
+++ b/lemonldap-ng-manager/site/htdocs/static/js/sessions.js
@@ -74,7 +74,7 @@
overScheme = {
_whatToTrace: function(t, v, level, over) {
- if (level === 1 && v.length > over) {
+ if (level === 1 && v.length < max) {
return t + "=" + v + "*&groupBy=substr(" + t + "," + (level + over + 1) + ")";
} else {
return null;
@@ -86,6 +86,13 @@
} else {
return null;
}
+ },
+ _startTime: function(t, v, level, over) {
+ if (level > 3) {
+ return t + "=" + v + "*&groupBy=substr(" + t + "," + (9 + level + over + 1) + ")";
+ } else {
+ return null;
+ }
}
};
diff --git a/lemonldap-ng-manager/site/htdocs/static/js/sessions.min.js b/lemonldap-ng-manager/site/htdocs/static/js/sessions.min.js
index fafadd1ea..ce37f3060 100644
--- a/lemonldap-ng-manager/site/htdocs/static/js/sessions.min.js
+++ b/lemonldap-ng-manager/site/htdocs/static/js/sessions.min.js
@@ -1 +1 @@
-(function(){var categories,hiddenAttributes,llapp,max,menu,overScheme,schemes;max=25;schemes={_whatToTrace:[function(t,v){return"groupBy=substr("+t+",1)"},function(t,v){return t+"="+v+"*&groupBy="+t},function(t,v){return t+"="+v}],ipAddr:[function(t,v){return"groupBy=net("+t+",16,1)"},function(t,v){if(!v.match(/:/)){v=v+"."}return t+"="+v+"*&groupBy=net("+t+",32,2)"},function(t,v){if(!v.match(/:/)){v=v+"."}return t+"="+v+"*&groupBy=net("+t+",48,3)"},function(t,v){if(!v.match(/:/)){v=v+"."}return t+"="+v+"*&groupBy=net("+t+",128,4)"},function(t,v){return t+"="+v+"&groupBy=_whatToTrace"},function(t,v,q){return q.replace(/\&groupBy.*$/,"")+("&_whatToTrace="+v)}],_startTime:[function(t,v){return"groupBy=substr("+t+",8)"},function(t,v){return t+"="+v+"*&groupBy=substr("+t+",10)"},function(t,v){return t+"="+v+"*&groupBy=substr("+t+",11)"},function(t,v){return t+"="+v+"*&groupBy=substr("+t+",12)"},function(t,v){return t+"="+v+"*&groupBy=_whatToTrace"},function(t,v,q){console.log(t);console.log(v);console.log(q);return q.replace(/\&groupBy.*$/,"")+("&_whatToTrace="+v)}],doubleIp:[function(t,v){return t},function(t,v){return"_whatToTrace="+v+"&groupBy=ipAddr"},function(t,v,q){return q.replace(/\&groupBy.*$/,"")+("&ipAddr="+v)}]};overScheme={_whatToTrace:function(t,v,level,over){if(level===1&&v.length>over){return t+"="+v+"*&groupBy=substr("+t+","+(level+over+1)+")"}else{return null}},ipAddr:function(t,v,level,over){if(level>0&&level<4){return t+"="+v+"*&groupBy=net("+t+","+(16*level+4*(over+1))+",2)"}else{return null}}};hiddenAttributes="_password";categories={dateTitle:["_utime","_startTime","_updateTime","_lastAuthnUTime","_lastSeen"],connectionTitle:["ipAddr","_timezone","_url"],authenticationTitle:["_session_id","_user","_password","authenticationLevel"],modulesTitle:["_auth","_userDB","_passwordDB","_issuerDB","_authChoice","_authMulti","_userDBMulti"],saml:["_idp","_idpConfKey","_samlToken","_lassoSessionDump","_lassoIdentityDump"],groups:["groups","hGroups"],ldap:["dn"],BrowserID:["_browserIdAnswer","_browserIdAnswerRaw"],OpenIDConnect:["_oidc_id_token","_oidc_OP","_oidc_access_token"],sfaTitle:["_2fDevices"],oidcConsents:["_oidcConsents"]};menu={session:[{title:"deleteSession",icon:"trash"}],home:[]};llapp=angular.module("llngSessionsExplorer",["ui.tree","ui.bootstrap","llApp"]);llapp.controller("SessionsExplorerCtrl",["$scope","$translator","$location","$q","$http",function($scope,$translator,$location,$q,$http){var autoId,c,pathEvent,sessionType;$scope.links=links;$scope.menulinks=menulinks;$scope.staticPrefix=staticPrefix;$scope.scriptname=scriptname;$scope.formPrefix=formPrefix;$scope.impPrefix=impPrefix;$scope.sessionTTL=sessionTTL;$scope.availableLanguages=availableLanguages;$scope.waiting=true;$scope.showM=false;$scope.showT=true;$scope.data=[];$scope.currentScope=null;$scope.currentSession=null;$scope.menu=menu;$scope.translateP=$translator.translateP;$scope.translate=$translator.translate;$scope.translateTitle=function(node){return $translator.translateField(node,"title")};sessionType="global";$scope.menuClick=function(button){if(button.popup){window.open(button.popup)}else{if(!button.action){button.action=button.title}switch(typeof button.action){case"function":button.action($scope.currentNode,$scope);break;case"string":$scope[button.action]();break;default:console.log(typeof button.action)}}return $scope.showM=false};$scope.deleteOIDCConsent=function(rp,epoch){var item;item=angular.element(".data-"+epoch);item.remove();$scope.waiting=true;$http["delete"](scriptname+"sessions/OIDCConsent/"+sessionType+"/"+$scope.currentSession.id+"?rp="+rp+"&epoch="+epoch).then(function(response){return $scope.waiting=false},function(resp){return $scope.waiting=false});return $scope.showT=false};$scope.deleteSession=function(){$scope.waiting=true;return $http["delete"](scriptname+"sessions/"+sessionType+"/"+$scope.currentSession.id).then(function(response){$scope.currentSession=null;$scope.currentScope.remove();return $scope.waiting=false},function(resp){$scope.currentSession=null;$scope.currentScope.remove();return $scope.waiting=false})};$scope.stoggle=function(scope){var node;node=scope.$modelValue;if(node.nodes.length===0){$scope.updateTree(node.value,node.nodes,node.level,node.over,node.query,node.count)}return scope.toggle()};$scope.displaySession=function(scope){var sessionId,transformSession;transformSession=function(session){var _insert,array,attr,attrs,category,cv,element,epoch,i,id,j,k,key,l,len,len1,len2,len3,len4,len5,m,name,o,oidcConsent,p,real,ref,ref1,res,sfDevice,spoof,subres,time,title,tmp,value;_insert=function(re,title){var key,reg,tmp,value;tmp=[];reg=new RegExp(re);for(key in session){value=session[key];if(key.match(reg)&&value){tmp.push({title:key,value:value});delete session[key]}}if(tmp.length>0){return res.push({title:title,nodes:tmp})}};time=session._utime;id=session._session_id;for(key in session){value=session[key];if(!value){delete session[key]}else{if(typeof session==="string"&&value.match(/; /)){session[key]=value.split("; ")}if(typeof session[key]!=="object"){if(hiddenAttributes.match(new RegExp("\b"+key+"\b"))){session[key]="********"}else if(key.match(/^(_utime|_lastAuthnUTime|_lastSeen|notification)$/)){session[key]=$scope.localeDate(value)}else if(key.match(/^(_startTime|_updateTime)$/)){session[key]=$scope.strToLocaleDate(value)}}}}res=[];for(category in categories){attrs=categories[category];subres=[];for(i=0,len=attrs.length;i0){res.push({title:"__"+category+"__",nodes:subres})}}_insert("^openid","OpenID");_insert("^notification_(.+)","__notificationsDone__");if(session._loginHistory){tmp=[];if(session._loginHistory.successLogin){ref=session._loginHistory.successLogin;for(m=0,len3=ref.length;mb.title){return 1}else if(a.title real attribute");real.push(element)}else{spoof.push(element)}}tmp=spoof.concat(real);res.push({title:"__attributesAndMacros__",nodes:tmp});return{_utime:time,id:id,nodes:res}};$scope.currentScope=scope;sessionId=scope.$modelValue.session;$http.get(scriptname+"sessions/"+sessionType+"/"+sessionId).then(function(response){return $scope.currentSession=transformSession(response.data)});return $scope.showT=false};$scope.localeDate=function(s){var d;d=new Date(s*1e3);return d.toLocaleString()};$scope.isValid=function(epoch,type){var isValid,now,path;path=$location.path();now=Date.now()/1e3;console.log("Path",path);console.log("Session epoch",epoch);console.log("Current date",now);console.log("Session TTL",sessionTTL);isValid=now-epochmax&&overScheme[$scope.type]){if(tmp=overScheme[$scope.type]($scope.type,value,level,over,currentQuery)){over++;query=tmp;level=level-1}else{over=0}}else{over=0}return $http.get(scriptname+"sessions/"+sessionType+"?"+query).then(function(response){var data,i,len,n,ref;data=response.data;if(data.result){ref=data.values;for(i=0,len=ref.length;i0&&level<4){return t+"="+v+"*&groupBy=net("+t+","+(16*level+4*(over+1))+",2)"}else{return null}},_startTime:function(t,v,level,over){if(level>3){return t+"="+v+"*&groupBy=substr("+t+","+(9+level+over+1)+")"}else{return null}}};hiddenAttributes="_password";categories={dateTitle:["_utime","_startTime","_updateTime","_lastAuthnUTime","_lastSeen"],connectionTitle:["ipAddr","_timezone","_url"],authenticationTitle:["_session_id","_user","_password","authenticationLevel"],modulesTitle:["_auth","_userDB","_passwordDB","_issuerDB","_authChoice","_authMulti","_userDBMulti"],saml:["_idp","_idpConfKey","_samlToken","_lassoSessionDump","_lassoIdentityDump"],groups:["groups","hGroups"],ldap:["dn"],BrowserID:["_browserIdAnswer","_browserIdAnswerRaw"],OpenIDConnect:["_oidc_id_token","_oidc_OP","_oidc_access_token"],sfaTitle:["_2fDevices"],oidcConsents:["_oidcConsents"]};menu={session:[{title:"deleteSession",icon:"trash"}],home:[]};llapp=angular.module("llngSessionsExplorer",["ui.tree","ui.bootstrap","llApp"]);llapp.controller("SessionsExplorerCtrl",["$scope","$translator","$location","$q","$http",function($scope,$translator,$location,$q,$http){var autoId,c,pathEvent,sessionType;$scope.links=links;$scope.menulinks=menulinks;$scope.staticPrefix=staticPrefix;$scope.scriptname=scriptname;$scope.formPrefix=formPrefix;$scope.impPrefix=impPrefix;$scope.sessionTTL=sessionTTL;$scope.availableLanguages=availableLanguages;$scope.waiting=true;$scope.showM=false;$scope.showT=true;$scope.data=[];$scope.currentScope=null;$scope.currentSession=null;$scope.menu=menu;$scope.translateP=$translator.translateP;$scope.translate=$translator.translate;$scope.translateTitle=function(node){return $translator.translateField(node,"title")};sessionType="global";$scope.menuClick=function(button){if(button.popup){window.open(button.popup)}else{if(!button.action){button.action=button.title}switch(typeof button.action){case"function":button.action($scope.currentNode,$scope);break;case"string":$scope[button.action]();break;default:console.log(typeof button.action)}}return $scope.showM=false};$scope.deleteOIDCConsent=function(rp,epoch){var item;item=angular.element(".data-"+epoch);item.remove();$scope.waiting=true;$http["delete"](scriptname+"sessions/OIDCConsent/"+sessionType+"/"+$scope.currentSession.id+"?rp="+rp+"&epoch="+epoch).then(function(response){return $scope.waiting=false},function(resp){return $scope.waiting=false});return $scope.showT=false};$scope.deleteSession=function(){$scope.waiting=true;return $http["delete"](scriptname+"sessions/"+sessionType+"/"+$scope.currentSession.id).then(function(response){$scope.currentSession=null;$scope.currentScope.remove();return $scope.waiting=false},function(resp){$scope.currentSession=null;$scope.currentScope.remove();return $scope.waiting=false})};$scope.stoggle=function(scope){var node;node=scope.$modelValue;if(node.nodes.length===0){$scope.updateTree(node.value,node.nodes,node.level,node.over,node.query,node.count)}return scope.toggle()};$scope.displaySession=function(scope){var sessionId,transformSession;transformSession=function(session){var _insert,array,attr,attrs,category,cv,element,epoch,i,id,j,k,key,l,len,len1,len2,len3,len4,len5,m,name,o,oidcConsent,p,real,ref,ref1,res,sfDevice,spoof,subres,time,title,tmp,value;_insert=function(re,title){var key,reg,tmp,value;tmp=[];reg=new RegExp(re);for(key in session){value=session[key];if(key.match(reg)&&value){tmp.push({title:key,value:value});delete session[key]}}if(tmp.length>0){return res.push({title:title,nodes:tmp})}};time=session._utime;id=session._session_id;for(key in session){value=session[key];if(!value){delete session[key]}else{if(typeof session==="string"&&value.match(/; /)){session[key]=value.split("; ")}if(typeof session[key]!=="object"){if(hiddenAttributes.match(new RegExp("\b"+key+"\b"))){session[key]="********"}else if(key.match(/^(_utime|_lastAuthnUTime|_lastSeen|notification)$/)){session[key]=$scope.localeDate(value)}else if(key.match(/^(_startTime|_updateTime)$/)){session[key]=$scope.strToLocaleDate(value)}}}}res=[];for(category in categories){attrs=categories[category];subres=[];for(i=0,len=attrs.length;i0){res.push({title:"__"+category+"__",nodes:subres})}}_insert("^openid","OpenID");_insert("^notification_(.+)","__notificationsDone__");if(session._loginHistory){tmp=[];if(session._loginHistory.successLogin){ref=session._loginHistory.successLogin;for(m=0,len3=ref.length;mb.title){return 1}else if(a.title real attribute");real.push(element)}else{spoof.push(element)}}tmp=spoof.concat(real);res.push({title:"__attributesAndMacros__",nodes:tmp});return{_utime:time,id:id,nodes:res}};$scope.currentScope=scope;sessionId=scope.$modelValue.session;$http.get(scriptname+"sessions/"+sessionType+"/"+sessionId).then(function(response){return $scope.currentSession=transformSession(response.data)});return $scope.showT=false};$scope.localeDate=function(s){var d;d=new Date(s*1e3);return d.toLocaleString()};$scope.isValid=function(epoch,type){var isValid,now,path;path=$location.path();now=Date.now()/1e3;console.log("Path",path);console.log("Session epoch",epoch);console.log("Current date",now);console.log("Session TTL",sessionTTL);isValid=now-epochmax&&overScheme[$scope.type]){if(tmp=overScheme[$scope.type]($scope.type,value,level,over,currentQuery)){over++;query=tmp;level=level-1}else{over=0}}else{over=0}return $http.get(scriptname+"sessions/"+sessionType+"?"+query).then(function(response){var data,i,len,n,ref;data=response.data;if(data.result){ref=data.values;for(i=0,len=ref.length;i (
@@ -119,17 +122,38 @@ sub check {
}
if ( $user eq $req->{user} or !$user ) {
- $self->userLogger->notice("Retrieve session from Sessions database");
+ $self->logger->debug("checkUser requested for myself");
+ $self->userLogger->notice("Return userData...");
$self->userLogger->warn("Using spoofed SSO groups if exist!!!")
if ( $self->conf->{impersonationRule} );
$attrs = $req->userData;
+ $user = $req->{user};
}
else {
- $self->logger->debug("checkUser requested for $req->{user}");
- $req->{user} = $user;
- $self->userLogger->notice(
- "Retrieve session from userDB and compute Groups & Macros");
- $attrs = $self->_userDatas($req);
+ $self->logger->debug("checkUser requested for $user");
+
+ # Try to retrieve session from sessions DB
+ $self->userLogger->notice('Try to retrieve session from DB...');
+ my $moduleOptions = $self->conf->{globalStorageOptions} || {};
+ $moduleOptions->{backend} = $self->conf->{globalStorage};
+ my $sessions =
+ $self->module->searchOn( $moduleOptions, $self->conf->{whatToTrace},
+ $user );
+ my $age = '1';
+ foreach my $id ( keys %$sessions ) {
+ my $session = $self->p->getApacheSession($id) or next;
+
+ if ( $session->{data}->{_utime} gt $age ) {
+ $attrs = $session->{data};
+ $age = $session->{data}->{_utime};
+ }
+ }
+ unless ( defined $attrs->{_session_id} ) {
+ $req->{user} = $user;
+ $self->userLogger->notice(
+ "NO session found in DB. Compute userData...");
+ $attrs = $self->_userData($req);
+ }
}
if ( $req->error ) {
@@ -204,11 +228,8 @@ sub check {
LANGS => $self->conf->{showLanguages},
MSG => $msg,
ALERTE => ( $msg eq 'checkUser' ? 'alert-info' : 'alert-warning' ),
- LOGIN => (
- $self->p->checkXSSAttack( 'LOGIN', $req->{userData}->{uid} ) ? ""
- : $req->{userData}->{uid}
- ),
- URL => (
+ LOGIN => $user,
+ URL => (
$self->p->checkXSSAttack( 'URL', $url ) ? ""
: $url
),
@@ -300,10 +321,10 @@ sub _urlFormat {
return lc("$proto$vhost$port") . "$appuri";
}
-sub _userDatas {
+sub _userData {
my ( $self, $req ) = @_;
- # Search user in database
+ # Compute session
my $steps = [ 'getUser', 'setSessionInfo', 'setMacros', 'setGroups' ];
$self->conf->{checkUserDisplayPersistentInfo}
? push @$steps, 'setPersistentSessionInfo', 'setLocalGroups'
@@ -328,7 +349,12 @@ sub _userDatas {
$self->logger->debug('Identity not authorized');
return $req->error(PE_BADCREDENTIALS);
}
+ unless ( defined $req->sessionInfo->{uid} ) {
+ # Avoid error with SAML, OIDC, etc...
+ $self->logger->debug("\"$req->{user}\" NOT found in userDB");
+ return $req->error(PE_BADCREDENTIALS);
+ }
$self->logger->debug("Return \"$req->{user}\" sessionInfo");
return $req->{sessionInfo};
}
diff --git a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/UserDB/SAML.pm b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/UserDB/SAML.pm
index 2b72e7768..2ce8aed17 100644
--- a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/UserDB/SAML.pm
+++ b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/UserDB/SAML.pm
@@ -43,29 +43,34 @@ sub setSessionInfo {
my $exportedAttr;
# Force UTF-8
- my $force_utf8 = $self->conf->{samlIDPMetaDataOptions}->{$idpConfKey}
- ->{samlIDPMetaDataOptionsForceUTF8};
+ my $force_utf8 =
+ $self->conf->{samlIDPMetaDataOptions}->{$idpConfKey}
+ ->{samlIDPMetaDataOptionsForceUTF8}
+ if $idpConfKey;
# Get all required attributes, not already set
# in setAuthSessionInfo()
- foreach (
- keys %{ $self->conf->{samlIDPMetaDataExportedAttributes}->{$idpConfKey}
- } )
- {
+ if ($idpConfKey) {
+ foreach (
+ keys
+ %{ $self->conf->{samlIDPMetaDataExportedAttributes}->{$idpConfKey} }
+ )
+ {
- # Extract fields from exportedAttr value
- my ( $mandatory, $name, $format, $friendly_name ) =
- split( /;/,
- $self->conf->{samlIDPMetaDataExportedAttributes}->{$idpConfKey}
- ->{$_} );
+ # Extract fields from exportedAttr value
+ my ( $mandatory, $name, $format, $friendly_name ) =
+ split( /;/,
+ $self->conf->{samlIDPMetaDataExportedAttributes}->{$idpConfKey}
+ ->{$_} );
- # Keep mandatory attributes not sent in authentication response
- if ( $mandatory and not defined $req->{sessionInfo}->{$_} ) {
- $exportedAttr->{$_} =
- $self->conf->{samlIDPMetaDataExportedAttributes}->{$idpConfKey}
- ->{$_};
- $self->logger->debug(
- "Attribute $_ will be requested to $idpConfKey");
+ # Keep mandatory attributes not sent in authentication response
+ if ( $mandatory and not defined $req->{sessionInfo}->{$_} ) {
+ $exportedAttr->{$_} =
+ $self->conf->{samlIDPMetaDataExportedAttributes}
+ ->{$idpConfKey}->{$_};
+ $self->logger->debug(
+ "Attribute $_ will be requested to $idpConfKey");
+ }
}
}
diff --git a/lemonldap-ng-portal/site/templates/common/mail_register_done.tpl b/lemonldap-ng-portal/site/templates/common/mail_register_done.tpl
index d5fa2a43b..cca48225e 100644
--- a/lemonldap-ng-portal/site/templates/common/mail_register_done.tpl
+++ b/lemonldap-ng-portal/site/templates/common/mail_register_done.tpl
@@ -14,6 +14,6 @@
$password
-Click here to access to portal
+Click here to access to portal
diff --git a/lemonldap-ng-portal/t/61-BruteForceProtection.t b/lemonldap-ng-portal/t/61-BruteForceProtection.t
index 600afb342..689b745c7 100644
--- a/lemonldap-ng-portal/t/61-BruteForceProtection.t
+++ b/lemonldap-ng-portal/t/61-BruteForceProtection.t
@@ -167,6 +167,8 @@ count(1);
ok( $res->[2]->[0] =~ /<\/span>/,
'Rejected -> Protection enabled' );
count(1);
+
+diag 'Waiting';
sleep 1;
## Sixth failed connection -> Rejected
@@ -184,6 +186,8 @@ count(1);
ok( $res->[2]->[0] =~ /<\/span>/,
'Rejected -> Protection enabled' );
count(1);
+
+diag 'Waiting';
sleep 2;
## Sixth successful connection -> Rejected
@@ -201,6 +205,8 @@ count(1);
ok( $res->[2]->[0] =~ /<\/span>/,
'Rejected -> Protection enabled' );
count(1);
+
+diag 'Waiting';
sleep 3;
## Seventh successful connection -> Accepted
diff --git a/lemonldap-ng-portal/t/61-Session-ActivityTimeout.t b/lemonldap-ng-portal/t/61-Session-ActivityTimeout.t
index 5de50dc52..307a0ae73 100644
--- a/lemonldap-ng-portal/t/61-Session-ActivityTimeout.t
+++ b/lemonldap-ng-portal/t/61-Session-ActivityTimeout.t
@@ -33,6 +33,7 @@ expectOK($res);
my $id1 = expectCookie($res);
count(1);
+diag 'Waiting';
sleep 3;
ok(
@@ -48,6 +49,7 @@ ok( $res->[2]->[0] =~ qr%Your applications%,
or print STDERR Dumper( $res->[2]->[0] );
count(2);
+diag 'Waiting';
sleep 5;
ok(
diff --git a/lemonldap-ng-portal/t/61-Session-Timeout.t b/lemonldap-ng-portal/t/61-Session-Timeout.t
index c89cdff8a..7ab540f75 100644
--- a/lemonldap-ng-portal/t/61-Session-Timeout.t
+++ b/lemonldap-ng-portal/t/61-Session-Timeout.t
@@ -32,6 +32,7 @@ expectOK($res);
my $id1 = expectCookie($res);
count(1);
+diag 'Waiting';
sleep 9;
ok(
@@ -47,6 +48,7 @@ ok( $res->[2]->[0] =~ qr%Your applications%,
or print STDERR Dumper( $res->[2]->[0] );
count(2);
+diag 'Waiting';
sleep 2;
ok(
diff --git a/lemonldap-ng-portal/t/67-CheckUser-with-issuer-SAML-POST.t b/lemonldap-ng-portal/t/67-CheckUser-with-issuer-SAML-POST.t
new file mode 100644
index 000000000..0e7620b33
--- /dev/null
+++ b/lemonldap-ng-portal/t/67-CheckUser-with-issuer-SAML-POST.t
@@ -0,0 +1,638 @@
+use lib 'inc';
+use Test::More;
+use strict;
+use IO::String;
+use LWP::UserAgent;
+use LWP::Protocol::PSGI;
+use MIME::Base64;
+
+BEGIN {
+ require 't/test-lib.pm';
+ require 't/saml-lib.pm';
+}
+
+my $maintests = 24;
+my $debug = 'error';
+my ( $issuer, $sp, $res );
+my %handlerOR = ( issuer => [], sp => [] );
+
+# Redefine LWP methods for tests
+LWP::Protocol::PSGI->register(
+ sub {
+ my $req = Plack::Request->new(@_);
+ fail('POST should not launch SOAP requests');
+ count(1);
+ return [ 500, [], [] ];
+ }
+);
+
+SKIP: {
+ eval "use Lasso";
+ if ($@) {
+ skip 'Lasso not found', $maintests;
+ }
+
+ # Initialization
+ ok( $issuer = issuer(), 'Issuer portal' );
+ $handlerOR{issuer} = \@Lemonldap::NG::Handler::Main::_onReload;
+ switch ('sp');
+ &Lemonldap::NG::Handler::Main::cfgNum( 0, 0 );
+
+ ok( $sp = sp(), 'SP portal' );
+ $handlerOR{sp} = \@Lemonldap::NG::Handler::Main::_onReload;
+
+ # Simple SP access
+ my $res;
+ ok(
+ $res = $sp->_get(
+ '/', accept => 'text/html',
+ ),
+ 'Unauth SP request'
+ );
+ expectOK($res);
+ ok( expectCookie( $res, 'lemonldapidp' ), 'IDP cookie defined' )
+ or explain(
+ $res->[1],
+'Set-Cookie => lemonldapidp=http://auth.idp.com/saml/metadata; domain=.sp.com; path=/'
+ );
+ my ( $host, $url, $s ) =
+ expectAutoPost( $res, 'auth.idp.com', '/saml/singleSignOn',
+ 'SAMLRequest' );
+
+ # Push SAML request to IdP
+ switch ('issuer');
+ ok(
+ $res = $issuer->_post(
+ $url,
+ IO::String->new($s),
+ accept => 'text/html',
+ length => length($s)
+ ),
+ 'Post SAML request to IdP'
+ );
+ expectOK($res);
+ my $pdata = 'lemonldappdata=' . expectCookie( $res, 'lemonldappdata' );
+
+ # Try to authenticate with an unauthorized user to IdP
+ $s = "user=dwho&password=dwho&$s";
+ ok(
+ $res = $issuer->_post(
+ $url,
+ IO::String->new($s),
+ accept => 'text/html',
+ cookie => $pdata,
+ length => length($s),
+ ),
+ 'Post authentication'
+ );
+ ok( $res->[2]->[0] =~ /trmsg="89"/, 'Reject reason is 89' )
+ or print STDERR Dumper( $res->[2]->[0] );
+
+ # Simple SP access
+ ok(
+ $res = $sp->_get(
+ '/', accept => 'text/html',
+ ),
+ 'Unauth SP request'
+ );
+ expectOK($res);
+ ok( expectCookie( $res, 'lemonldapidp' ), 'IDP cookie defined' )
+ or explain(
+ $res->[1],
+'Set-Cookie => lemonldapidp=http://auth.idp.com/saml/metadata; domain=.sp.com; path=/'
+ );
+ ( $host, $url, $s ) =
+ expectAutoPost( $res, 'auth.idp.com', '/saml/singleSignOn',
+ 'SAMLRequest' );
+
+ # Push SAML request to IdP
+ ok(
+ $res = $issuer->_post(
+ $url,
+ IO::String->new($s),
+ accept => 'text/html',
+ length => length($s)
+ ),
+ 'Post SAML request to IdP'
+ );
+ expectOK($res);
+ $pdata = 'lemonldappdata=' . expectCookie( $res, 'lemonldappdata' );
+
+ # Try to authenticate with an authorized user to IdP
+ $s = "user=davros&password=davros&$s";
+ ok(
+ $res = $issuer->_post(
+ $url,
+ IO::String->new($s),
+ accept => 'text/html',
+ cookie => $pdata,
+ length => length($s),
+ ),
+ 'Post authentication'
+ );
+ my $idpId = expectCookie($res);
+
+ # Expect pdata to be cleared
+ $pdata = expectCookie( $res, 'lemonldappdata' );
+ ok( $pdata !~ 'issuerRequestsaml', 'SAML request cleared from pdata' );
+
+ ( $host, $url, $s ) =
+ expectAutoPost( $res, 'auth.sp.com', '/saml/proxySingleSignOnPost',
+ 'SAMLResponse' );
+
+ # Post SAML response to SP
+ switch ('sp');
+ ok(
+ $res = $sp->_post(
+ $url, IO::String->new($s),
+ accept => 'text/html',
+ length => length($s),
+ cookie => 'lemonldapidp=http://auth.idp.com/saml/metadata',
+ ),
+ 'Post SAML response to SP'
+ );
+
+ # Verify authentication on SP
+ expectRedirection( $res, 'http://auth.sp.com' );
+ my $spId = expectCookie($res);
+
+ ok(
+ $res =
+ $sp->_get( '/', cookie => "lemonldap=$spId", accept => 'text/html' ),
+ 'Get / on SP'
+ );
+ count(1);
+ expectOK($res);
+ expectAuthenticatedAs( $res, 'davros@badguy.org@idp' );
+
+ # Simple SP access
+ my $res;
+ ok(
+ $res = $sp->_get(
+ '/', accept => 'text/html',
+ ),
+ 'Unauth SP request'
+ );
+ expectOK($res);
+ ok( expectCookie( $res, 'lemonldapidp' ), 'IDP cookie defined' )
+ or explain(
+ $res->[1],
+'Set-Cookie => lemonldapidp=http://auth.idp.com/saml/metadata; domain=.sp.com; path=/'
+ );
+ my ( $host, $url, $s ) =
+ expectAutoPost( $res, 'auth.idp.com', '/saml/singleSignOn',
+ 'SAMLRequest' );
+
+ # Push SAML request to IdP
+ switch ('issuer');
+ ok(
+ $res = $issuer->_post(
+ $url,
+ IO::String->new($s),
+ accept => 'text/html',
+ length => length($s)
+ ),
+ 'Post SAML request to IdP'
+ );
+ expectOK($res);
+ my $pdata = 'lemonldappdata=' . expectCookie( $res, 'lemonldappdata' );
+
+ # Try to authenticate with an authorized user to IdP
+ $s = "user=french&password=french&$s";
+ ok(
+ $res = $issuer->_post(
+ $url,
+ IO::String->new($s),
+ accept => 'text/html',
+ cookie => $pdata,
+ length => length($s),
+ ),
+ 'Post authentication'
+ );
+ $idpId = expectCookie($res);
+
+ # Expect pdata to be cleared
+ $pdata = expectCookie( $res, 'lemonldappdata' );
+ ok( $pdata !~ 'issuerRequestsaml', 'SAML request cleared from pdata' );
+
+ ( $host, $url, $s ) =
+ expectAutoPost( $res, 'auth.sp.com', '/saml/proxySingleSignOnPost',
+ 'SAMLResponse' );
+
+ # Post SAML response to SP
+ switch ('sp');
+ ok(
+ $res = $sp->_post(
+ $url, IO::String->new($s),
+ accept => 'text/html',
+ length => length($s),
+ cookie => 'lemonldapidp=http://auth.idp.com/saml/metadata',
+ ),
+ 'Post SAML response to SP'
+ );
+
+ # Verify authentication on SP
+ expectRedirection( $res, 'http://auth.sp.com' );
+ $spId = expectCookie($res);
+
+ ok(
+ $res =
+ $sp->_get( '/', cookie => "lemonldap=$spId", accept => 'text/html' ),
+ 'Get / on SP'
+ );
+ count(1);
+ expectOK($res);
+ expectAuthenticatedAs( $res, 'fa@badwolf.org@idp' );
+
+ # CheckUser form -> granted
+ # ------------------------
+ ok(
+ $res = $sp->_get(
+ '/checkuser',
+ cookie => "lemonldap=$spId",
+ accept => 'text/html'
+ ),
+ 'CheckUser form',
+ );
+ my ( $host, $url, $query ) =
+ expectForm( $res, undef, '/checkuser', 'user', 'url' );
+ ok( $res->[2]->[0] =~ m%%,
+ 'Found trspan="checkUser"' )
+ or explain( $res->[2]->[0], 'trspan="checkUser"' );
+ ok( $res->[2]->[0] =~ m%uid | %,
+ 'Found attribute uid' )
+ or explain( $res->[2]->[0], 'Attribute uid' );
+ ok( $res->[2]->[0] =~ m%french | %,
+ 'Found value french' )
+ or explain( $res->[2]->[0], 'Value french' );
+ count(4);
+
+ # CheckUser request with unknown user
+ $query =~ s/user=french/user=rtyler/;
+ ok(
+ $res = $sp->_post(
+ '/checkuser',
+ IO::String->new($query),
+ cookie => "lemonldap=$spId",
+ length => length($query),
+ accept => 'text/html',
+ ),
+ 'POST checkuser'
+ );
+ ok(
+ $res->[2]->[0] =~
+m%
%,
+ ' PE5 found'
+ ) or explain( $res->[2]->[0], 'PE5 - Unknown identity' );
+ count(2);
+
+ # CheckUser request with an already authneticated user
+ $query =~ s/user=rtyler/user=davros/;
+ ok(
+ $res = $sp->_post(
+ '/checkuser',
+ IO::String->new($query),
+ cookie => "lemonldap=$spId",
+ length => length($query),
+ accept => 'text/html',
+ ),
+ 'POST checkuser'
+ );
+
+ my ( $host, $url, $query ) =
+ expectForm( $res, undef, '/checkuser', 'user', 'url' );
+ ok( $res->[2]->[0] =~ m%%,
+ 'Found trspan="checkUser"' )
+ or explain( $res->[2]->[0], 'trspan="checkUser"' );
+ ok( $res->[2]->[0] =~ m%uid | %,
+ 'Found attribute uid' )
+ or explain( $res->[2]->[0], 'Attribute uid' );
+ ok( $res->[2]->[0] =~ m%mail | %,
+ 'Found attribute mail' )
+ or explain( $res->[2]->[0], 'Attribute mail' );
+ ok( $res->[2]->[0] =~ m%davros\@badguy.org | %,
+ 'Found value davros@badguy.org' )
+ or explain( $res->[2]->[0], 'Value davros@badguy.org' );
+ count(5);
+
+ # Logout initiated by SP
+ ok(
+ $res = $sp->_get(
+ '/',
+ query => 'logout',
+ cookie => "lemonldap=$spId",
+ accept => 'text/html'
+ ),
+ 'Query SP for logout'
+ );
+ ( $host, $url, $s ) =
+ expectAutoPost( $res, 'auth.idp.com', '/saml/singleLogout',
+ 'SAMLRequest' );
+
+ # Push SAML logout request to IdP
+ switch ('issuer');
+ ok(
+ $res = $issuer->_post(
+ $url,
+ IO::String->new($s),
+ accept => 'text/html',
+ cookie => "lemonldap=$idpId",
+ length => length($s)
+ ),
+ 'Post SAML logout request to IdP'
+ );
+ ( $host, $url, $s ) =
+ expectAutoPost( $res, 'auth.sp.com', '/saml/proxySingleLogoutReturn',
+ 'SAMLResponse' );
+
+ # Post SAML response to SP
+ switch ('sp');
+ ok(
+ $res = $sp->_post(
+ $url, IO::String->new($s),
+ accept => 'text/html',
+ length => length($s),
+ cookie => 'lemonldapidp=http://auth.idp.com/saml/metadata',
+ ),
+ 'Post SAML response to SP'
+ );
+ expectRedirection( $res, 'http://auth.sp.com' );
+
+ # Test if logout is done
+ switch ('issuer');
+ ok(
+ $res = $issuer->_get(
+ '/', cookie => "lemonldap=$idpId",
+ ),
+ 'Test if user is reject on IdP'
+ );
+ expectReject($res);
+
+ switch ('sp');
+ ok(
+ $res = $sp->_get(
+ '/',
+ accept => 'text/html',
+ cookie =>
+ "lemonldapidp=http://auth.idp.com/saml/metadata; lemonldap=$spId"
+ ),
+ 'Test if user is reject on SP'
+ );
+ expectOK($res);
+ expectAutoPost( $res, 'auth.idp.com', '/saml/singleSignOn', 'SAMLRequest' );
+}
+
+count($maintests);
+clean_sessions();
+done_testing( count() );
+
+sub switch {
+ my $type = shift;
+ @Lemonldap::NG::Handler::Main::_onReload = @{
+ $handlerOR{$type};
+ };
+}
+
+sub issuer {
+ return LLNG::Manager::Test->new( {
+ ini => {
+ logLevel => $debug,
+ domain => 'idp.com',
+ portal => 'http://auth.idp.com',
+ authentication => 'Demo',
+ userDB => 'Same',
+ issuerDBSAMLActivation => 1,
+ issuerDBSAMLRule => '$uid =~ /(?:french|davros)/',
+ samlSPMetaDataOptions => {
+ 'sp.com' => {
+ samlSPMetaDataOptionsEncryptionMode => 'none',
+ samlSPMetaDataOptionsSignSSOMessage => 1,
+ samlSPMetaDataOptionsSignSLOMessage => 1,
+ samlSPMetaDataOptionsCheckSSOMessageSignature => 1,
+ samlSPMetaDataOptionsCheckSLOMessageSignature => 1,
+ }
+ },
+ samlSPMetaDataExportedAttributes => {
+ 'sp.com' => {
+ cn =>
+'1;cn;urn:oasis:names:tc:SAML:2.0:attrname-format:basic',
+ uid =>
+'1;uid;urn:oasis:names:tc:SAML:2.0:attrname-format:basic',
+ }
+ },
+ samlOrganizationDisplayName => "IDP",
+ samlOrganizationName => "IDP",
+ samlOrganizationURL => "http://www.idp.com/",
+ samlServicePrivateKeyEnc => "-----BEGIN RSA PRIVATE KEY-----
+MIIEogIBAAKCAQEAnfKBDG/K0TnGT7Xu8q1N45sNWvIK91SqNg8nvN2uVeKoHADT
+csus5Xn3id5+8Q9TuMFsW9kIEeXiaPKXQa9ryfSNDhWDWloNkpGEeWif2BnHUu46
+Abu1UBWb0mH6VwcG1PR4qHruLis1odjQ1qnVDNfSEASVIppEBYjDX203ypmURIzU
+6h53GRRRlf1BLWkbVn9ysmDeR57Xw5Rsx/+tBlcnMrkv/40DSUkehQIl2JmlFrl2
+Caik+gU4pd20apA/pNLjBZF0OmGoS08AIR5NMd0KFa6CwZUUSHJqH5GFy5Y2yl4l
+g8K0klAS9q7L7aXI+eFQZhkwidjpxXnHPyxIGQIDAQABAoIBAHnfqjX3eO8SfnP5
+NURp90Td2mNHirCn0qLd9NKl1ySMPR1GgeH9SQ7Umu32EcteAUL5dOw2PiTZVmeW
+cKINgsWVftXUQcOQ4xIqWKb51QUBdy0FhxrZRSFjWxXt5iYK1PmzHfsax/g1/S9C
+RnqtFyjOy1bywkSt9jiy+9YBR2B7BDhLHlILbijWn5zaecaV4YA+L1UK4M/mehdb
++0FVPavbGpnlqBRTY+7YXfZ/mRPCfn5DvO9lW1O0pJMmNdBh9kmm3DxHf6AkK47a
+43gO/dRWiWo2rZ/+Jw7uyqOb23U0MydP7kia0p3tzCUBPsrlgnichYG5RNFp0wqy
+3VT1TYECgYEA0Y9vENy1jJd+s7WbGrsRtSKxfZgtJr0yjSlQVYrIlwbZSGn+ndxq
+V2vVlwIgLX3pz6T40BMfk6SNx08jjy0Sgn6OAM0ILrinno8yWcSAMCmfCU0S/3O1
+55bqtcnk4XTHBHzJ5OrnrPaW5ourvJz0lcWEKMg3BXxLzaF6ZRy85nECgYEAwPMD
+LNAKLCDrUMyYFOpPyPLe7wvszcFvPipGgerSgFP1c6N7xaMUdHDYqBfuis1khPGF
+YcMHeNBYmzX6yEGbp3lrB4PHpUySmTU3mv3u9I05aahInK21gXum3uRkCWyyIF6V
+T/qeszl9mVOCp0CC4eG3IMVpaD0UKDEHVhERYCkCgYAjuTPRyA4a3Wh38ilysRkf
+q75eDqcDx5Tqg3RyYKo5NK2troP9HSnzpSpQB8i8eI53G0RfFCN5479XjqIdMi3J
+mRFUCZ+vd0L7wKVwsBK6Ix49U6o9adhElnGEc9pUpLeYiD1SjMjZr1+iBYVNLeRz
+86vH1/mpMbsqXrCis/dvwQKBgGttomHr/w3s0jftget7PirrFrbP0+wHfDGHhjRF
+kyhCFtJovrwefYALaIXGtVjw3LusYZA570oT7pGUb2naJZkMYEwR0jG1vZWx7KDO
+K6JbkxDB0pPxn7JVL2bAkPYyX8boAohCSOQO6WBZ/8+xem3bp4OGhpa0EyoBik0g
+OaVpAoGATj4SyYsE10hGT676iie8zy3fi5IPC3E+x4QlVuusaLtuY8LJA50stjtx
+gUa/JAKlZZL+gvzvOviQIxyfIChXOdTt5uiOYkdHJDbAF3NSrji7hrXq4v8UZv75
+8hBrwJZIpy6y01dRlrriHmPRtEq1pk7JX2uUg0sP5g4BEcsaCbc=
+-----END RSA PRIVATE KEY-----
+",
+ samlServicePrivateKeySig => "-----BEGIN RSA PRIVATE KEY-----
+MIIEpAIBAAKCAQEAtR/wgDqWB4Maho5V6TjcL/NbNfjgIh7GcgkrB5RZcVT1GTej
+JlMjUQdgBKBuZXQN+7/29P6UcGq1kYalURq6S8SpeJ1ofp5rBEoD/TIkvU0JOcid
+65wp+fdzXGXsfiZvHraU74jSCgjP/wqfVGRyBIQzB0SIxSpnrsigqNsE1E94toDM
+x4wovjHu/9ABAImREV7Sz83OeFF00/sghrjTEJOD/gHf04JCn9MgNOqvSTysr9LX
+Wg/oUKQDEYeTq9ux6pq/oqv1MxwONbSZPtN5yD41mi+hT8Rh+W8Je8rsiML4VMxz
+sb1l9303asw6suo5bLTISKNSbu1nt1NkpNxzywIDAQABAoIBAQCQkbvPPfP+bwC/
+IeEk1IO7qkzFWa7czR+safD0jc6OjTdNN4F716Q6yt4zEzLKu8VliiW+C23EBQiD
+7asKf4DvdTun0ExVtHDK7aEdeealSlXwz1ZtdypyILbtq1UGo/rR0v4x601rQPl0
+IrBmFf6D6FkqleNtLJmxguXpoVfLdYKNwkxH2ux+GOA9r2o5pUCQmJGDap5YWRuQ
+uB71ewJjVWujaL3e1ac/5cP7/tqWmgAiOaN8sYdD6+oWOR47bHj8JKcMBSl4y2QC
+dL31cGmmf5KqBbtISki3RXfHHjT7E3Z85CbESkKTZlEb1ar3XmepY6Z7V5UO16oz
+fFE5R6khAoGBAOl9Qb+qYVVO5ugE65ORjYVeuXykANhM9ssiY5a6zuAakWzw7Zv3
+k6PXm9p7azlEXAlTnTXVwHYMyuuzZDvQ8LRV1iBOdPuIkUAmaQ5K9ASD7VcoHexh
+k8DAKf9Ln7sTRaMdvgceRNczOmJOBIEpTZkssA/jVGXZsoyTWYl1en/ZAoGBAMaW
+RnNbSNprEV2b8UeAJ6i77c4SXwu1I8X2NLtiLScb1ETBjfrdHmdlJglfyd/0gmhH
+p/43Ku2iGUoY5KtuOI6QmahrJYQscRQhoj252VXadG6fNWWAlpgdCm9houhHb5BF
+3zge/bTr0anUe9EA7Z/ymav12rEouoNjIlhI9C5DAoGATR85a2SMt8/TB0owwdJu
+62GpZNkLCmcJkXkvaecUVAOSi2hdI4o4MwMRkK35cbX5rH74y4JqCtQY5pefgP53
+sykzDAK+MyMdzxGg2764MRGegI5Yq+5jDmSquo+xF+q6srEtRk6iMG7UVwosBLmu
+zuxqzySoiOfKSRKWnYe3SakCgYEAwWMkVkAmETXE4oDzFSsS8/mW2l//mPocTTK3
+JWe1CunJ6+8FYbAlZJEW2ngismp8+CoXybNVpbZ+pC7buKoMf6EHUgCNt0pEEFO0
+mCG9KSMk0XlPWXpArP9S4yaUq1itpzSz7QYZES+4rIcU0HLz9RgeWFyCTJWaFErc
+7laVG9sCgYBKOtk5WlIOP4BxSd2y4cYzohgwTZIs1/2kTEn1u4eH73M1xvAlHHFB
+wSF5QXgDKJ8pPAOhNWpdLO/PdtnQn91nOvTNc+ShJZzjdbneUdQVpWpoBf72uA+N
+6rIVf1JBUL2p7HFHaGdUZC7KGQ+yv6ZHrE1+7202nuDvJdvGEEdFsQ==
+-----END RSA PRIVATE KEY-----
+",
+ samlServicePublicKeyEnc => "-----BEGIN PUBLIC KEY-----
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnfKBDG/K0TnGT7Xu8q1N
+45sNWvIK91SqNg8nvN2uVeKoHADTcsus5Xn3id5+8Q9TuMFsW9kIEeXiaPKXQa9r
+yfSNDhWDWloNkpGEeWif2BnHUu46Abu1UBWb0mH6VwcG1PR4qHruLis1odjQ1qnV
+DNfSEASVIppEBYjDX203ypmURIzU6h53GRRRlf1BLWkbVn9ysmDeR57Xw5Rsx/+t
+BlcnMrkv/40DSUkehQIl2JmlFrl2Caik+gU4pd20apA/pNLjBZF0OmGoS08AIR5N
+Md0KFa6CwZUUSHJqH5GFy5Y2yl4lg8K0klAS9q7L7aXI+eFQZhkwidjpxXnHPyxI
+GQIDAQAB
+-----END PUBLIC KEY-----
+",
+ samlServicePublicKeySig => "-----BEGIN PUBLIC KEY-----
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtR/wgDqWB4Maho5V6Tjc
+L/NbNfjgIh7GcgkrB5RZcVT1GTejJlMjUQdgBKBuZXQN+7/29P6UcGq1kYalURq6
+S8SpeJ1ofp5rBEoD/TIkvU0JOcid65wp+fdzXGXsfiZvHraU74jSCgjP/wqfVGRy
+BIQzB0SIxSpnrsigqNsE1E94toDMx4wovjHu/9ABAImREV7Sz83OeFF00/sghrjT
+EJOD/gHf04JCn9MgNOqvSTysr9LXWg/oUKQDEYeTq9ux6pq/oqv1MxwONbSZPtN5
+yD41mi+hT8Rh+W8Je8rsiML4VMxzsb1l9303asw6suo5bLTISKNSbu1nt1NkpNxz
+ywIDAQAB
+-----END PUBLIC KEY-----
+",
+ samlSPMetaDataXML => {
+ "sp.com" => {
+ samlSPMetaDataXML =>
+ samlSPMetaDataXML( 'sp', 'HTTP-POST' )
+ },
+ },
+ }
+ }
+ );
+}
+
+sub sp {
+ return LLNG::Manager::Test->new( {
+ ini => {
+ logLevel => $debug,
+ domain => 'sp.com',
+ portal => 'http://auth.sp.com',
+ authentication => 'SAML',
+ userDB => 'Same',
+ checkUser => 1,
+ issuerDBSAMLActivation => 0,
+ restSessionServer => 1,
+ samlIDPMetaDataExportedAttributes => {
+ idp => {
+ mail => "0;mail;;",
+ uid => "1;uid",
+ cn => "0;cn"
+ }
+ },
+ samlIDPMetaDataOptions => {
+ idp => {
+ samlIDPMetaDataOptionsEncryptionMode => 'none',
+ samlIDPMetaDataOptionsSSOBinding => 'post',
+ samlIDPMetaDataOptionsSLOBinding => 'post',
+ samlIDPMetaDataOptionsSignSSOMessage => 1,
+ samlIDPMetaDataOptionsSignSLOMessage => 1,
+ samlIDPMetaDataOptionsCheckSSOMessageSignature => 1,
+ samlIDPMetaDataOptionsCheckSLOMessageSignature => 1,
+ samlIDPMetaDataOptionsForceUTF8 => 1,
+ }
+ },
+ samlIDPMetaDataExportedAttributes => {
+ idp => {
+ "uid" => "0;uid;;",
+ "cn" => "1;cn;;",
+ },
+ },
+ samlIDPMetaDataXML => {
+ idp => {
+ samlIDPMetaDataXML =>
+ samlIDPMetaDataXML( 'idp', 'HTTP-POST' )
+ }
+ },
+ samlOrganizationDisplayName => "SP",
+ samlOrganizationName => "SP",
+ samlOrganizationURL => "http://www.sp.com",
+ samlServicePublicKeySig => "-----BEGIN PUBLIC KEY-----
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAu4iToYAEmWQxgZDihGVz
+MMql1elPn37domWcvXeU2E4yt2hh5jkQHiFjgodfOlNeRIw5QJVlUBwr+CQvbaKR
+FXd7BrOhQIDC0TZPRVB0XHarUtsCuDekN4/2GKSzHsoToKUVPWq9thsuek3xkpsJ
+GZNX7bglfEc9+QQpYTqN1rkdN1PVU0epNMokFFGho5pLRqLUV5+I/QXAL49jfTja
+Sxsp4UndTI8/+mGSRSq+nrT2zyQRM/vkj5vR9ZVz67HO/+Wk3Mx6RAwkVcMdgMAq
+Cq8odmbI0yCRZiTL9ybKWRKqWJoKJ0p5+Q2fPEBPupQZR09Jt/JPuLVSsGfCxi9N
+qwIDAQAB
+-----END PUBLIC KEY-----
+",
+ samlServicePrivateKeyEnc => "-----BEGIN RSA PRIVATE KEY-----
+MIIEogIBAAKCAQEAsRaod2RZ8hMFBl+VhsnhyPM8l/Fj1obnBxfQIaWuHFIFfXiG
+e/CYHuZ5QJQLnZxHMJX6LL3Sh+Usog3p0jpijpcg0QgfBSEkfopKTgReYN8DiDIl
+l0rV1XdTni7E85Nd1YyNy3ui/ZD+UShWwqu6jLVLR+QUm+/1LIKYb3OCBTvOlY7x
+HoP6NSU1+Mr+YzGBUacdO2vnNxe/PQhxIeP1zO0njuqGHkwEpy8rUWRZbbDn31Tm
+Kjqlhgtsz5HPhbRaYEExhyepKgBiNz+RyxtYXVhuG8OrWQDoS5gYHSjdw1CTJyix
+eJwyoqA9RGYguG5nh9zndi3LWAh7Z0lx+tIz+wIDAQABAoIBAEkZrk8iiJKJ0WAx
+IrsyKNbXuWKLTYgnxcRCyzKofrfID+YcU39j8JeI0fKbajQUZ7qhnlTLwtU//+2h
+SqzyVu6/add/v7ZRWQw3L7cGzKK2THHzKVtLk/t7N3QroDdf1LMrQvkFP2HmcWS0
+/yN62hXtXHb/qpY4Nn+6JQyUpM5dkv8S/QjDl2NTdyWrXKzWp+4I3QLQ20f4zym+
+ir7RennziMc0HlQNcTjGAUbFULtdqEfSFWhNK7UjiRY+S0XV2xJIbGjnxUQH62fS
+w1ZzYsF7sBtoSckvfL4WfGbylhOVnliU05RLU2c67PRjj1Gskoslq1Ow/3DHR7rI
+BSBpV8ECgYEA1eHfcog7xQGDkW+cshJtFPFx+9MegB58gFW1rl0rn+tfbexvoSEA
+7G7EOTyaU6OAI+8StiRT6AYTgEU7PMM9zDykdGIWj3h0OpHGA86xhEiiaaM2DDRv
+/DEKRVlEdmRLLLY28pJVHOMYomia3mb2VKZGg2VfGtSfjg1GXD3I8OECgYEA0/X0
+U55KjZ1JQTPUgFc1WK1NxX9MaH+NcpDaolEUy3Qf3QTbfws+a9K3vwCn7EpQhrfs
+I6RVUtwFdCyfl/jzBY9Gykkg03sMgW7Qw2SCCsSt05M+jDtBbNJ7esP6PAeKFvXZ
+ZWhdeiAa4kM/P6gtvZXQ4tY4LkSbcd6b0SzzFFsCgYBjMsusFzuBd95JyfZnMNye
+5gzzu0teKMWd0CLfqB7foQ81sH9lwCTpg8ZGtbDuMdrwz6ViDR9NceQBjhqXaAZ1
+f3rW79d+22Ms9wdcJLV4oSeSzzv2FSwLT8NvvqNeNc4YArshbnVDXKDEUrfhhueh
+Ay2ZK58clpkaDVYg2hckgQKBgG3KuhtSI/YE4fwXN9yez7A2XNGPZem/IGqWo9lu
+PGJCrXqT2IqPLW82gB083r6jo+CUhonTxqqb82tA7g4PUvqvQ5Dmnk1NMKYe255K
+gp3HUO8GF2EWFIak5Hcr6oOLuDi6cjh3/euTk7ld8fYsTD0mzEOjiQhWW1p5X6bT
+LLp/AoGAHvkxA1NM1HJ3myAREbwNXxRy/nhNt4mwMkZ6hPQsW/Eg/3r7j6MJOFrm
+U8AJJjDGKe6nlXhhnMoQfJzAc0cYNgjktmJXW27fHGIwt/2QwYNFHPK3s7HTrfH6
+7T4XKT3yGeeeyC2soKJQPlGB+ETdIUnXa7eo9KVWtMTgISyx1Qk=
+-----END RSA PRIVATE KEY-----
+",
+ samlServicePrivateKeySig => "-----BEGIN RSA PRIVATE KEY-----
+MIIEpAIBAAKCAQEAu4iToYAEmWQxgZDihGVzMMql1elPn37domWcvXeU2E4yt2hh
+5jkQHiFjgodfOlNeRIw5QJVlUBwr+CQvbaKRFXd7BrOhQIDC0TZPRVB0XHarUtsC
+uDekN4/2GKSzHsoToKUVPWq9thsuek3xkpsJGZNX7bglfEc9+QQpYTqN1rkdN1PV
+U0epNMokFFGho5pLRqLUV5+I/QXAL49jfTjaSxsp4UndTI8/+mGSRSq+nrT2zyQR
+M/vkj5vR9ZVz67HO/+Wk3Mx6RAwkVcMdgMAqCq8odmbI0yCRZiTL9ybKWRKqWJoK
+J0p5+Q2fPEBPupQZR09Jt/JPuLVSsGfCxi9NqwIDAQABAoIBABE0Cjb6g3F+23vD
+SsRSeiqzrFrfOEqtXK+VGrfWzHS7V7Ozg6eW/H+HGJXUzUuQcklfg7EFA3JB41a0
+GxW3oA+UElkfCV/dcAG5NbRqGQKScEz9glZb5FikgDLqiPP+HabS/gvQSu71t2HI
+3KxSRJdwCNTp26Z28pxxYUpmELTtxd9vlHjffit2Mnt2uc8hOtFHdNavfYwvYH7o
+bmlckp7b/JVOy2Yy21O94ZWkE498jXyn71Gr+V1cnJ0RrmYbhQqIvFpFHj98Pf4O
+if3c4YmBcZ4t7PUsZUYF3ooWt8k/mdigQC3D6p80OKe+wUTYKcCN0ZdFbiURv9pg
+CsqLh+ECgYEA9vA+9QfzvXC7S5yXgTkuRiusPlNye/AiyA/0oGjmjFZ1YNsT7awH
+6BjW6WE+rS4elKJu1GaefM/cDguH4ZmJc+eKgi4LDCqYw9rr9les3aneBc8demd3
+O/Ej1Pud1QxXArBNfBYo08vEqwST9P89clJC5090U6bGK2E0rTVu1w0CgYEAwmpG
+9LbOFeGCPmwX7Avuk7tQQfRSV6q9TFZo+HxDfKYvxec846l1vBenY2rrgYhtolYJ
+YS795LYgbSWRxGfgr1GuIbP5GsjHy6/1o6bS8M++GJ7KHArb0QLAYyQweqqb164A
+NvHJkveueWnxzeOlD9j2fcjEnBHwTnqjG+17CZcCgYEAqMXawa4FsNxzpmIISpHC
+RsNindZ60Kp3mzUMhPYtXI1a/C+/lxmU7dTMTgXgyIxU6lF6XkEk4TlPtWm8HTzK
+7SS7Te4aLt6OOo5N57hUtct7q4y7IQXGQHm3e8HdRdeBQJ0u2Dhs/xSt/hTK6w/n
+91Kx11Y+s02w88UkM53pe6ECgYAF/UYwVc1liSv9BlF6WSfBb1zam09KGh1405Sq
+SxG9LlV8cFJE5TyWTdg/TNTyiaRvAt2JG+yAdkfrdOPXvCeE3yxRJ30+IP9evA4C
+O6p19sBxe7rYQFFjUAVjSIMh1E22yEqDZtGB8JV0chob8K5uHY4CdAPylu7jTA3o
+V1maAwKBgQCSGQ3yzsk4EGN2xd/JdgGDzhKyTZTQKMWYqQcsYxRAQ7Paj7u+Wkgv
+dBeKcI0HwgpLy5ZohSd2erqieIsW0pEbJWCmos4IcO8tgNfEOa5WXYdyLbj5tFwt
+ctu4/BJdijqfpMAtG8pv6k09gYjfASVytXmydGcs/0rVKYCRQA8Tow==
+-----END RSA PRIVATE KEY-----
+",
+ samlServicePublicKeyEnc => "-----BEGIN PUBLIC KEY-----
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsRaod2RZ8hMFBl+Vhsnh
+yPM8l/Fj1obnBxfQIaWuHFIFfXiGe/CYHuZ5QJQLnZxHMJX6LL3Sh+Usog3p0jpi
+jpcg0QgfBSEkfopKTgReYN8DiDIll0rV1XdTni7E85Nd1YyNy3ui/ZD+UShWwqu6
+jLVLR+QUm+/1LIKYb3OCBTvOlY7xHoP6NSU1+Mr+YzGBUacdO2vnNxe/PQhxIeP1
+zO0njuqGHkwEpy8rUWRZbbDn31TmKjqlhgtsz5HPhbRaYEExhyepKgBiNz+RyxtY
+XVhuG8OrWQDoS5gYHSjdw1CTJyixeJwyoqA9RGYguG5nh9zndi3LWAh7Z0lx+tIz
++wIDAQAB
+-----END PUBLIC KEY-----
+",
+ samlSPSSODescriptorAuthnRequestsSigned => 1,
+ },
+ }
+ );
+}
diff --git a/lemonldap-ng-portal/t/67-CheckUser.t b/lemonldap-ng-portal/t/67-CheckUser.t
index 019d2e241..3e3fb88a7 100644
--- a/lemonldap-ng-portal/t/67-CheckUser.t
+++ b/lemonldap-ng-portal/t/67-CheckUser.t
@@ -57,6 +57,21 @@ ok( $res->[2]->[0] =~ m%An error occurs, you're going to be redirected to%,
count(1);
$client->logout($id);
+## Try to authenticate
+ok(
+ $res = $client->_post(
+ '/',
+ IO::String->new('user=rtyler&password=rtyler'),
+ length => 27,
+ accept => 'text/html',
+ ),
+ 'Auth query'
+);
+count(1);
+
+$id = expectCookie($res);
+expectRedirection( $res, 'http://auth.example.com/' );
+
## Try to authenticate
ok(
$res = $client->_post(
@@ -85,7 +100,6 @@ ok(
);
count(1);
-# Request with bad VH
my ( $host, $url, $query ) =
expectForm( $res, undef, '/checkuser', 'user', 'url' );
ok( $res->[2]->[0] =~ m%%, 'Found trspan="checkUser"' )
@@ -99,8 +113,7 @@ ok( $res->[2]->[0] =~ m%dwho | %, 'Found value dwho' )
or explain( $res->[2]->[0], 'Value dwho' );
count(2);
-$query =~ s/user=dwho/user=rtyler/;
-$query =~ s/url=/url=http%3A%2F%2Ftry.example.com/;
+$query =~ s/url=/url=http%3A%2F%2Ftest1.example.com/;
ok(
$res = $client->_post(
'/checkuser',
@@ -113,6 +126,39 @@ ok(
);
count(1);
+( $host, $url, $query ) =
+ expectForm( $res, undef, '/checkuser', 'user', 'url' );
+ok( $res->[2]->[0] =~ m%%, 'Found trspan="checkUser"' )
+ or explain( $res->[2]->[0], 'trspan="checkUser"' );
+
+count(2);
+ok( $res->[2]->[0] =~ m%Auth-User | %,
+ 'Found Auth-User' )
+ or explain( $res->[2]->[0], 'Header Key: Auth-User' );
+ok( $res->[2]->[0] =~ m%dwho | %, 'Found dwho' )
+ or explain( $res->[2]->[0], 'Header Value: dwho' );
+ok( $res->[2]->[0] =~ m%_whatToTrace | %,
+ 'Found _whatToTrace' )
+ or explain( $res->[2]->[0], 'Macro Key _whatToTrace' );
+ok( $res->[2]->[0] =~ m%dwho | %, 'Found dwho' )
+ or explain( $res->[2]->[0], 'Macro Value dwho' );
+count(3);
+
+$query =~ s/user=dwho/user=rtyler/;
+$query =~ s/url=http%3A%2F%2Ftest1.example.com/url=http%3A%2F%2Ftry.example.com/;
+ok(
+ $res = $client->_post(
+ '/checkuser',
+ IO::String->new($query),
+ cookie => "lemonldap=$id",
+ length => length($query),
+ accept => 'text/html',
+ ),
+ 'POST checkuser'
+);
+count(1);
+
+# Request with bad VH
( $host, $url, $query ) =
expectForm( $res, undef, '/checkuser', 'user', 'url' );
ok( $res->[2]->[0] =~ m%%,
@@ -252,7 +298,7 @@ m%
%,
count(2);
# Request an unknown identity
-$query =~ s/user=msmith/user=davros/;
+$query =~ s/user=msmith/user=dalek/;
ok(
$res = $client->_post(
'/checkuser',
diff --git a/lemonldap-ng-portal/t/lmConf-1.json b/lemonldap-ng-portal/t/lmConf-1.json
index a78140ea8..f65043f34 100644
--- a/lemonldap-ng-portal/t/lmConf-1.json
+++ b/lemonldap-ng-portal/t/lmConf-1.json
@@ -75,7 +75,7 @@
"key": "qwertyui",
"locationRules": {
"auth.example.com" : {
- "(?#checkUser)^/checkuser" : "$uid eq \"dwho\" or $uid eq \"msmith\"",
+ "(?#checkUser)^/checkuser" : "$uid eq \"dwho\" or $uid eq \"msmith\" or $uid eq \"french\"",
"(?#errors)^/lmerror/": "accept",
"default" : "accept"
},
diff --git a/lemonldap-ng-portal/t/test-lib.pm b/lemonldap-ng-portal/t/test-lib.pm
index be537f0f7..222c8dfc6 100644
--- a/lemonldap-ng-portal/t/test-lib.pm
+++ b/lemonldap-ng-portal/t/test-lib.pm
@@ -547,6 +547,11 @@ has ini => (
cn => 'Frédéric Accents',
mail => 'fa@badwolf.org',
};
+ $Lemonldap::NG::Portal::UserDB::Demo::demoAccounts{davros} = {
+ uid => 'davros',
+ cn => 'Bad Guy',
+ mail => 'davros@badguy.org',
+ };
$Lemonldap::NG::Portal::UserDB::Demo::demoAccounts{russian} = {
uid => 'russian',
cn => 'Русский',