Merge branch 'master' into manager-SFA-module

This commit is contained in:
Christophe Maudoux 2018-03-14 22:09:45 +01:00
commit e3b839ee95
33 changed files with 188 additions and 125 deletions

View File

@ -13,7 +13,7 @@ Copyright: 2005-2018, Xavier Guimard <x.guimard@free.fr>
2012-2015, David Coutadeur <david.coutadeur@gmail.com> 2012-2015, David Coutadeur <david.coutadeur@gmail.com>
2018, Christophe Maudoux <chrmdx@gmail.com> 2018, Christophe Maudoux <chrmdx@gmail.com>
2006-2015, LINAGORA <info@linagora.com> 2006-2015, LINAGORA <info@linagora.com>
2015-2017, Savoir-faire Linux <contac@savoirfairelinux.com> 2015-2018, Savoir-faire Linux <contac@savoirfairelinux.com>
License: GPL-2+ License: GPL-2+
Files: lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Auth/PAM.pm Files: lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Auth/PAM.pm

12
RELEASE
View File

@ -21,16 +21,22 @@ Before release
-------------- --------------
- Update documentation: - Update documentation:
$ make documentation
$ ./script/parameters-for-wiki.pl >/tmp/prmlist.txt
Replace https://lemonldap-ng.org/documentation/X.X/parameterlist by
/tmp/prmlist.txt content
$ make documentation
- Translate documentation - Translate documentation
$ make fr-doc $ make fr-doc
- Update debian/changelog - Update debian/changelog
launch just `dch -r` and force save (":w" and ot ":x") launch just `dch -r` and force save (":w" and ot ":x")
- Check Debian packages quality - Check Debian packages quality
$ cme check dpkg $ cme check dpkg
For minor release For minor release
----------------- -----------------

View File

@ -1,3 +1,4 @@
* Minimal authn level system (choice only) * Minimal authn level system (choice only)
* (2ndF/OTP mail) * (2ndF/OTP mail)
* Combination/Choice for password (using session data) * Combination/Choice for password (using session data)

View File

@ -46,7 +46,7 @@ Log : $conf->{cfgLog}
sub updateCache { sub updateCache {
my $self = shift; my $self = shift;
my $conf = $self->confAccess->getConf( { noCache => 1, raw => 1 } ); my $conf = $self->confAccess->getConf( { noCache => 2 } );
die "Must not be launched as root" unless ($>); die "Must not be launched as root" unless ($>);
print STDERR print STDERR
qq{Cache updated to configuration $conf->{cfgNum} for user $>\n}; qq{Cache updated to configuration $conf->{cfgNum} for user $>\n};

View File

@ -200,7 +200,7 @@ sub getConf {
# Store modified configuration in cache # Store modified configuration in cache
$self->setLocalConf($r) $self->setLocalConf($r)
if ( $self->{refLocalStorage} if ( $self->{refLocalStorage}
and not( $args->{noCache} or $args->{raw} ) ); and not( $args->{noCache} == 1 or $args->{raw} ) );
} }
} }

View File

@ -211,7 +211,7 @@ sub virtualHosts {
} }
return $self->sendJSONresponse( $req, $resp ); return $self->sendJSONresponse( $req, $resp );
} }
elsif ( $query =~ /^vhost(?:(?:Aliase|Http)s|Maintenance|Port|Type)$/ ) { elsif ( $query =~ qr/^$virtualHostKeys$/o ) {
$self->logger->debug("Query for $vh/$query key"); $self->logger->debug("Query for $vh/$query key");
# TODO: verify how this is done actually # TODO: verify how this is done actually

View File

@ -98,10 +98,11 @@ sub unserialize {
unless ( utf8::is_utf8($v) ) { unless ( utf8::is_utf8($v) ) {
$v = encode( 'UTF-8', $v ); $v = encode( 'UTF-8', $v );
} }
$conf->{$k} = $conf->{$k} = (
( $v =~ /./ $v =~ /./
? eval { from_json( $v, { allow_nonref => 1 } ) } ? eval { from_json( $v, { allow_nonref => 1 } ) }
: {} ); : {}
);
if ($@) { if ($@) {
$Lemonldap::NG::Common::Conf::msg .= $Lemonldap::NG::Common::Conf::msg .=
"Unable to decode $k, switching to old format.\n"; "Unable to decode $k, switching to old format.\n";

View File

@ -93,7 +93,7 @@ sub statusInit {
open STDOUT, ">&$fdout"; open STDOUT, ">&$fdout";
my $perl_exec = ( $^X =~ /perl/ ) ? $^X : 'perl'; my $perl_exec = ( $^X =~ /perl/ ) ? $^X : 'perl';
exec $perl_exec, '-MLemonldap::NG::Handler::Lib::Status', exec $perl_exec, '-MLemonldap::NG::Handler::Lib::Status',
map( {"-I$_"} @INC ), map( { "-I$_" } @INC ),
'-e &Lemonldap::NG::Handler::Lib::Status::run()'; '-e &Lemonldap::NG::Handler::Lib::Status::run()';
} }
} }

View File

@ -84,14 +84,18 @@ sub init {
my $portal = $conf->{portal}; my $portal = $conf->{portal};
$portal =~ s#https?://([^/]*).*#$1#; $portal =~ s#https?://([^/]*).*#$1#;
$self->csp( $self->csp(
"default-src 'self' $portal;frame-ancestors 'none';form-action 'self';" "default-src 'self' $portal;frame-ancestors 'none';form-action 'self';"
); );
$self->defaultRoute( $working[0]->defaultRoute ); $self->defaultRoute( $working[0]->defaultRoute );
# Find out more glyphicones at https://www.w3schools.com/icons/bootstrap_icons_glyphicons.asp # Find out more glyphicones at https://www.w3schools.com/icons/bootstrap_icons_glyphicons.asp
my $linksIcons = my $linksIcons = {
{ 'conf' => 'cog', 'sessions' => 'duplicate', 'notifications' => 'bell', '2ndFA' => 'wrench' }; 'conf' => 'cog',
'sessions' => 'duplicate',
'notifications' => 'bell',
'2ndFA' => 'wrench'
};
$self->links( [] ); $self->links( [] );
for ( my $i = 0 ; $i < @links ; $i++ ) { for ( my $i = 0 ; $i < @links ; $i++ ) {

View File

@ -62,7 +62,6 @@ sub addRoutes {
$self->{hiddenAttributes} //= "_password"; $self->{hiddenAttributes} //= "_password";
} }
################### ###################
# II. 2FA METHODS # # II. 2FA METHODS #
################### ###################
@ -84,18 +83,17 @@ sub delete2FAKey {
return $self->deleteTOTPKey( $req, $session, $skey ); return $self->deleteTOTPKey( $req, $session, $skey );
} }
else { else {
return $self->sendError( $req, undef, 666 ); return $self->sendError( $req, undef, 400 );
} }
} }
sub add2FAKey { sub add2FAKey {
my ( $self, $req, $session, $skey ) = @_; my ( $self, $req, $session, $skey ) = @_;
eval 'use Crypt::U2F::Server::Simple'; eval 'use Crypt::U2F::Server::Simple';
if ($@) { if ($@) {
$self->error("Can't load U2F library: $@"); $self->error("Can't load U2F library: $@");
return 0; return 0;
} }
@ -129,7 +127,7 @@ sub sfa {
my $Key = $params->{sessionType}; my $Key = $params->{sessionType};
my $res; my $res;
# Case 2: list of sessions # Case 2: list of sessions
my $whatToTrace = Lemonldap::NG::Handler::PSGI::Main->tsv->{whatToTrace}; my $whatToTrace = Lemonldap::NG::Handler::PSGI::Main->tsv->{whatToTrace};

View File

@ -227,7 +227,7 @@ sub attributes {
}, },
localStorageOptions => { localStorageOptions => {
type => 'keyTextContainer', type => 'keyTextContainer',
documentation => 'Local cache', documentation => 'Local cache parameters',
flags => 'hmp', flags => 'hmp',
}, },
cfgNum => { cfgNum => {
@ -824,7 +824,7 @@ sub attributes {
localSessionStorage => { localSessionStorage => {
type => 'PerlModule', type => 'PerlModule',
default => 'Cache::FileCache', default => 'Cache::FileCache',
documentation => 'Sessions cache module', documentation => 'Local sessions cache module',
}, },
localSessionStorageOptions => { localSessionStorageOptions => {
type => 'keyTextContainer', type => 'keyTextContainer',
@ -1411,8 +1411,12 @@ sub attributes {
}, },
# CAS IDP # CAS IDP
casAttr => { type => 'text', }, casAttr =>
casAttributes => { type => 'keyTextContainer', }, { type => 'text', documentation => 'Pivot attribute for CAS', },
casAttributes => {
type => 'keyTextContainer',
documentation => 'CAS exported attributes',
},
casAccessControlPolicy => { casAccessControlPolicy => {
type => 'select', type => 'select',
select => [ select => [
@ -1423,9 +1427,13 @@ sub attributes {
default => 'none', default => 'none',
documentation => 'CAS access control policy', documentation => 'CAS access control policy',
}, },
casStorage => { type => 'PerlModule', }, casStorage => {
type => 'PerlModule',
documentation => 'Apache::Session module to store CAS user data',
},
casStorageOptions => { casStorageOptions => {
type => 'keyTextContainer', type => 'keyTextContainer',
documentation => 'Apache::Session module parameters',
}, },
issuerDBCASActivation => { issuerDBCASActivation => {
default => 0, default => 0,
@ -1444,7 +1452,10 @@ sub attributes {
}, },
# Partners # Partners
casAppMetaDataOptions => { type => 'subContainer', }, casAppMetaDataOptions => {
type => 'subContainer',
documentation => 'Root of CAS app options',
},
casAppMetaDataExportedVars => { casAppMetaDataExportedVars => {
type => 'keyTextContainer', type => 'keyTextContainer',
default => { cn => 'cn', mail => 'mail', uid => 'uid', }, default => { cn => 'cn', mail => 'mail', uid => 'uid', },
@ -1605,8 +1616,14 @@ sub attributes {
type => 'bool', type => 'bool',
documentation => 'SAML force metadata UTF8 conversion', documentation => 'SAML force metadata UTF8 conversion',
}, },
samlStorage => { type => 'PerlModule', }, samlStorage => {
samlStorageOptions => { type => 'keyTextContainer', }, type => 'PerlModule',
documentation => 'Apache::Session module to store SAML user data',
},
samlStorageOptions => {
type => 'keyTextContainer',
documentation => 'Apache::Session module parameters',
},
samlAuthnContextMapPassword => { samlAuthnContextMapPassword => {
type => 'int', type => 'int',
default => 2, default => 2,
@ -2231,9 +2248,12 @@ m{^(?:ldapi://[^/]*/?|\w[\w\-\.]*(?::\d{1,5})?|ldap(?:s|\+tls)?://\w[\w\-\.]*(?:
default => 'find', default => 'find',
documentation => '"deref" param of Net::LDAP::search()', documentation => '"deref" param of Net::LDAP::search()',
}, },
mailLDAPFilter => { type => 'text', }, mailLDAPFilter =>
LDAPFilter => { type => 'text', }, { type => 'text', documentation => 'LDAP filter for mail search' },
AuthLDAPFilter => { type => 'text', }, LDAPFilter =>
{ type => 'text', documentation => 'Default LDAP filter' },
AuthLDAPFilter =>
{ type => 'text', documentation => 'LDAP filter for auth search' },
ldapGroupRecursive => { ldapGroupRecursive => {
default => 0, default => 0,
type => 'bool', type => 'bool',
@ -2312,7 +2332,10 @@ m{^(?:ldapi://[^/]*/?|\w[\w\-\.]*(?::\d{1,5})?|ldap(?:s|\+tls)?://\w[\w\-\.]*(?:
default => { cn => 'cn', mail => 'mail', uid => 'uid', }, default => { cn => 'cn', mail => 'mail', uid => 'uid', },
documentation => 'CAS exported variables', documentation => 'CAS exported variables',
}, },
casSrvMetaDataOptions => { type => 'subContainer', }, casSrvMetaDataOptions => {
type => 'subContainer',
documentation => 'Root of CAS server options',
},
casSrvMetaDataOptionsGateway => { type => 'bool', }, casSrvMetaDataOptionsGateway => { type => 'bool', },
casSrvMetaDataOptionsProxiedServices => { casSrvMetaDataOptionsProxiedServices => {
type => 'keyTextContainer', type => 'keyTextContainer',
@ -2385,7 +2408,7 @@ m{^(?:ldapi://[^/]*/?|\w[\w\-\.]*(?::\d{1,5})?|ldap(?:s|\+tls)?://\w[\w\-\.]*(?:
ns => ns =>
'http://auth.example.com/Lemonldap/NG/Common/PSGI/SOAPService', 'http://auth.example.com/Lemonldap/NG/Common/PSGI/SOAPService',
}, },
documentation => 'Demo exported variables', documentation => 'Apache::Session module parameters',
}, },
# Proxy # Proxy
@ -2649,6 +2672,7 @@ m{^(?:ldapi://[^/]*/?|\w[\w\-\.]*(?::\d{1,5})?|ldap(?:s|\+tls)?://\w[\w\-\.]*(?:
{ k => 'Custom', v => 'customModule' }, { k => 'Custom', v => 'customModule' },
] ]
], ],
documentation => 'Hash list of Choice strings',
}, },
# Combination # Combination
@ -2831,8 +2855,14 @@ m{^(?:ldapi://[^/]*/?|\w[\w\-\.]*(?::\d{1,5})?|ldap(?:s|\+tls)?://\w[\w\-\.]*(?:
default => 0, default => 0,
documentation => 'OpenID Connect allow hybrid flow', documentation => 'OpenID Connect allow hybrid flow',
}, },
oidcStorage => { type => 'PerlModule', }, oidcStorage => {
oidcStorageOptions => { type => 'keyTextContainer', }, type => 'PerlModule',
documentation => 'Apache::Session module to store OIDC user data',
},
oidcStorageOptions => {
type => 'keyTextContainer',
documentation => 'Apache::Session module parameters',
},
# OpenID Connect metadata nodes # OpenID Connect metadata nodes
oidcOPMetaDataNodes => { oidcOPMetaDataNodes => {

View File

@ -342,16 +342,7 @@ llapp.controller 'TreeCtrl', [
$scope.showModal('prompt.html', name).then -> $scope.showModal('prompt.html', name).then ->
n= $scope.result n= $scope.result
if n if n
node = $scope.addTemplateNode n, 'virtualHost' $scope.addTemplateNode n, 'virtualHost'
delete node.nodes[0].cnodes
node.nodes[0].nodes = [
id: "virtualHosts/new__#{n}/locationRules/default",
type: "rule",
title: "default",
comment: "",
re: "default",
data: "deny"
]
$scope.duplicateVhost = -> $scope.duplicateVhost = ->
name = if $scope.domain then ".#{$scope.domain.data}" else '.example.com' name = if $scope.domain then ".#{$scope.domain.data}" else '.example.com'
@ -398,10 +389,22 @@ llapp.controller 'TreeCtrl', [
title: name title: name
type: type type: type
nodes: templates type, "new__#{name}" nodes: templates type, "new__#{name}"
setDefault t.nodes
cs.$modelValue.nodes.push t cs.$modelValue.nodes.push t
cs.expand() cs.expand()
return t return t
setDefault = (node) ->
for n in node
if n.cnodes and n.default
delete n.cnodes
n._nodes = n.default
if n._nodes
setDefault n._nodes
else if n.default or n.default == 0
n.data = n.default
node
_getAll = (node) -> _getAll = (node) ->
d = $q.defer() d = $q.defer()
d2 = $q.defer() d2 = $q.defer()

View File

@ -1,4 +1,4 @@
// Generated by CoffeeScript 1.10.0 // Generated by CoffeeScript 1.12.7
/* /*
LemonLDAP::NG Manager client LemonLDAP::NG Manager client
@ -23,7 +23,7 @@ This file contains:
llapp.controller('TreeCtrl', [ llapp.controller('TreeCtrl', [
'$scope', '$http', '$location', '$q', '$uibModal', '$translator', '$cookies', '$htmlParams', function($scope, $http, $location, $q, $uibModal, $translator, $cookies, $htmlParams) { '$scope', '$http', '$location', '$q', '$uibModal', '$translator', '$cookies', '$htmlParams', function($scope, $http, $location, $q, $uibModal, $translator, $cookies, $htmlParams) {
var _checkSaveResponse, _download, _getAll, _stoggle, c, id, pathEvent, readError, setHelp; var _checkSaveResponse, _download, _getAll, _stoggle, c, id, pathEvent, readError, setDefault, setHelp;
$scope.links = window.links; $scope.links = window.links;
$scope.menu = $htmlParams.menu; $scope.menu = $htmlParams.menu;
$scope.menulinks = window.menulinks; $scope.menulinks = window.menulinks;
@ -405,21 +405,10 @@ This file contains:
field: 'hostname' field: 'hostname'
}; };
return $scope.showModal('prompt.html', name).then(function() { return $scope.showModal('prompt.html', name).then(function() {
var n, node; var n;
n = $scope.result; n = $scope.result;
if (n) { if (n) {
node = $scope.addTemplateNode(n, 'virtualHost'); return $scope.addTemplateNode(n, 'virtualHost');
delete node.nodes[0].cnodes;
return node.nodes[0].nodes = [
{
id: "virtualHosts/new__" + n + "/locationRules/default",
type: "rule",
title: "default",
comment: "",
re: "default",
data: "deny"
}
];
} }
}); });
}; };
@ -479,10 +468,27 @@ This file contains:
type: type, type: type,
nodes: templates(type, "new__" + name) nodes: templates(type, "new__" + name)
}; };
setDefault(t.nodes);
cs.$modelValue.nodes.push(t); cs.$modelValue.nodes.push(t);
cs.expand(); cs.expand();
return t; return t;
}; };
setDefault = function(node) {
var len, n, o;
for (o = 0, len = node.length; o < len; o++) {
n = node[o];
if (n.cnodes && n["default"]) {
delete n.cnodes;
n._nodes = n["default"];
}
if (n._nodes) {
setDefault(n._nodes);
} else if (n["default"] || n["default"] === 0) {
n.data = n["default"];
}
}
return node;
};
_getAll = function(node) { _getAll = function(node) {
var d, d2; var d, d2;
d = $q.defer(); d = $q.defer();

File diff suppressed because one or more lines are too long

View File

@ -1,4 +1,4 @@
// Generated by CoffeeScript 1.9.3 // Generated by CoffeeScript 1.12.7
/* /*
* Session explorer * Session explorer

View File

@ -106,10 +106,11 @@ sub verify {
# Prepare args # Prepare args
my $args; my $args;
foreach my $k ( keys %{ $self->{vrfyAttrs} } ) { foreach my $k ( keys %{ $self->{vrfyAttrs} } ) {
$args->{$k} = $args->{$k} = (
( $k eq 'code' $k eq 'code'
? $code ? $code
: $req->sessionInfo->{ $self->{vrfyAttrs}->{$k} } ); : $req->sessionInfo->{ $self->{vrfyAttrs}->{$k} }
);
} }
# Launch REST request # Launch REST request

View File

@ -137,24 +137,16 @@ sub selfRegister {
} }
); );
} }
# Get or generate master key # Get or generate master key
elsif ( $action eq 'unregister' ) { elsif ( $action eq 'unregister' ) {
$self->p->updatePersistentSession( $req, $self->p->updatePersistentSession( $req, { _totp2fSecret => '' } );
{ _totp2fSecret => '' }
);
$self->userLogger->notice('TOTP unregistration succeed'); $self->userLogger->notice('TOTP unregistration succeed');
return [ 200, [ 'Content-Type' => 'application/json' ], return [ 200, [ 'Content-Type' => 'application/json' ],
['{"result":1}'] ]; ['{"result":1}'] ];
} }
} }
1; 1;

View File

@ -74,18 +74,16 @@ sub run {
return [ 200, [ 'Content-Type' => 'application/json' ], [$challenge] ]; return [ 200, [ 'Content-Type' => 'application/json' ], [$challenge] ];
} }
if ( $action eq 'unregistration' ) { if ( $action eq 'unregistration' ) {
$self->p->updatePersistentSession( $self->p->updatePersistentSession(
$req, $req,
{ {
_u2fKeyHandle => '', _u2fKeyHandle => '',
_u2fUserKey => '' _u2fUserKey => ''
} }
); );
$self->userLogger->notice('U2F key unregistration succeed'); $self->userLogger->notice('U2F key unregistration succeed');
return [ return [ 200, [ 'Content-Type' => 'application/json' ],
200, [ 'Content-Type' => 'application/json' ], ['{"result":1}'] ];
['{"result":1}']
];
my $err = Crypt::U2F::Server::Simple::lastError(); my $err = Crypt::U2F::Server::Simple::lastError();
$self->userLogger->warn("U2F Unregistration failed: $err"); $self->userLogger->warn("U2F Unregistration failed: $err");
return $self->p->sendError( $req, $err, 200 ); return $self->p->sendError( $req, $err, 200 );
@ -117,6 +115,7 @@ sub run {
} }
my $res = my $res =
( $req->datas->{crypter}->authenticationVerify($resp) ? 1 : 0 ); ( $req->datas->{crypter}->authenticationVerify($resp) ? 1 : 0 );
#$self->userLogger->notice("res=$res"); #$self->userLogger->notice("res=$res");
return [ return [
200, [ 'Content-Type' => 'application/json' ], 200, [ 'Content-Type' => 'application/json' ],

View File

@ -85,6 +85,7 @@ sub verify {
return $self->fail($req); return $self->fail($req);
} }
$self->logger->debug("Get challenge: $challenge"); $self->logger->debug("Get challenge: $challenge");
#eval { $challenge = JSON::from_json($challenge)->{challenge} }; #eval { $challenge = JSON::from_json($challenge)->{challenge} };
if ( not $req->datas->{crypter}->setChallenge($challenge) ) { if ( not $req->datas->{crypter}->setChallenge($challenge) ) {
$self->logger->error( $self->logger->error(

View File

@ -177,8 +177,7 @@ sub try {
# On error, restart authentication with next scheme # On error, restart authentication with next scheme
if ( $res > PE_OK ) { if ( $res > PE_OK ) {
$self->logger->info( $self->logger->info(qq'Scheme "$name" returned $res, trying next');
qq'Scheme "$name" returned $res, trying next');
$req->datas->{dataKeep}->{combinationTry}++; $req->datas->{dataKeep}->{combinationTry}++;
$req->steps( [ @{ $req->datas->{combinationSteps} } ] ); $req->steps( [ @{ $req->datas->{combinationSteps} } ] );
$req->continue(1); $req->continue(1);

View File

@ -33,6 +33,13 @@ sub init {
sub extractFormInfo { sub extractFormInfo {
my ( $self, $req ) = @_; my ( $self, $req ) = @_;
if ( $req->datas->{_krbUser} ) {
$self->logger->debug( 'Kerberos ticket already validated for '
. $req->datas->{_krbUser} );
return PE_OK;
}
my $auth = $req->env->{HTTP_AUTHORIZATION}; my $auth = $req->env->{HTTP_AUTHORIZATION};
unless ($auth) { unless ($auth) {
@ -138,7 +145,7 @@ sub extractFormInfo {
return PE_ERROR; return PE_ERROR;
} }
$self->userLogger->notice("$client_name authentified by Kerberos"); $self->userLogger->notice("$client_name authentified by Kerberos");
$req->{_krbUser} = $client_name; $req->datas->{_krbUser} = $client_name;
if ( $self->conf->{krbRemoveDomain} ) { if ( $self->conf->{krbRemoveDomain} ) {
$client_name =~ s/^(.*)@.*$/$1/; $client_name =~ s/^(.*)@.*$/$1/;
} }
@ -157,7 +164,7 @@ sub authLogout {
sub setAuthSessionInfo { sub setAuthSessionInfo {
my ( $self, $req ) = @_; my ( $self, $req ) = @_;
$req->{sessionInfo}->{authenticationLevel} = $self->conf->{krbAuthnLevel}; $req->{sessionInfo}->{authenticationLevel} = $self->conf->{krbAuthnLevel};
$req->{sessionInfo}->{_krbUser} = $req->{_krbUser}; $req->{sessionInfo}->{_krbUser} = $req->datas->{_krbUser};
PE_OK; PE_OK;
} }

View File

@ -733,7 +733,7 @@ sub extractFormInfo {
my $res = $self->p->deleteSession($req); my $res = $self->p->deleteSession($req);
return ( return (
$res eq PE_LOGOUT_OK ? PE_SENDRESPONSE : $res ); $res eq PE_LOGOUT_OK ? PE_SENDRESPONSE : $res );
} }
] ]
); );

View File

@ -901,7 +901,7 @@ sub token {
unless ($codeSession) { unless ($codeSession) {
$self->logger->error("Unable to find OIDC session $code"); $self->logger->error("Unable to find OIDC session $code");
$self->p->sendError( $req, 'invalid_request', 400 ); return $self->p->sendError( $req, 'invalid_request', 400 );
} }
# Check we have the same redirect_uri value # Check we have the same redirect_uri value
@ -909,7 +909,7 @@ sub token {
{ {
$self->userLogger->error( "Provided redirect_uri is different from " $self->userLogger->error( "Provided redirect_uri is different from "
. $codeSession->{redirect_uri} ); . $codeSession->{redirect_uri} );
$self->p->sendError( $req, 'invalid_request', 400 ); return $self->p->sendError( $req, 'invalid_request', 400 );
} }
# Get user identifier # Get user identifier
@ -921,7 +921,7 @@ sub token {
$self->userLogger->error( $self->userLogger->error(
"Unable to find user session linked to OIDC session $code"); "Unable to find user session linked to OIDC session $code");
$codeSession->remove(); $codeSession->remove();
$self->p->sendError( $req, 'invalid_request', 400 ); return $self->p->sendError( $req, 'invalid_request', 400 );
} }
my $user_id_attribute = my $user_id_attribute =
@ -947,7 +947,7 @@ sub token {
$self->userLogger->error( $self->userLogger->error(
"Unable to create OIDC session for access_token"); "Unable to create OIDC session for access_token");
$codeSession->remove(); $codeSession->remove();
$self->p->sendError( $req, 'invalid_request', 400 ); return $self->p->sendError( $req, 'invalid_request', 400 );
} }
my $access_token = $accessTokenSession->id; my $access_token = $accessTokenSession->id;
@ -1102,7 +1102,7 @@ sub registration {
# Check dynamic registration is allowed # Check dynamic registration is allowed
unless ( $self->conf->{oidcServiceAllowDynamicRegistration} ) { unless ( $self->conf->{oidcServiceAllowDynamicRegistration} ) {
$self->logger->error("Dynamic registration is not allowed"); $self->logger->error("Dynamic registration is not allowed");
$self->p->sendError( $req, 'server_error' ); return $self->p->sendError( $req, 'server_error' );
} }
# Get client metadata # Get client metadata

View File

@ -97,6 +97,7 @@ sub bind {
unless ($self->ldap unless ($self->ldap
and $self->ldap->root_dse( attrs => ['supportedLDAPVersion'] ) ) and $self->ldap->root_dse( attrs => ['supportedLDAPVersion'] ) )
{ {
$self->ldap->DESTROY if ( $self->ldap );
$self->ldap( $self->newLdap ); $self->ldap( $self->newLdap );
} }
return undef unless ( $self->ldap ); return undef unless ( $self->ldap );

View File

@ -95,7 +95,7 @@ sub getToken {
$self->logger->notice("Bad (or expired) token $id"); $self->logger->notice("Bad (or expired) token $id");
return undef; return undef;
} }
$self->cache->remove($id) unless($keep); $self->cache->remove($id) unless ($keep);
return from_json( $data, { allow_nonref => 1 } ); return from_json( $data, { allow_nonref => 1 } );
} }
else { else {

View File

@ -178,24 +178,32 @@ sub sregHook {
# else build message and return 0 # else build message and return 0
else { else {
my (@mopt,@mreq); my ( @mopt, @mreq );
# No choice for requested parameters: just an information # No choice for requested parameters: just an information
foreach my $k (@req) { foreach my $k (@req) {
utf8::decode( $msg{req}->{$k} ); utf8::decode( $msg{req}->{$k} );
push @mreq, {k=>$k,m=>$msg{req}->{$k}}; push @mreq, { k => $k, m => $msg{req}->{$k} };
} }
# For optional parameters: checkboxes are displayed # For optional parameters: checkboxes are displayed
foreach my $k (@opt) { foreach my $k (@opt) {
utf8::decode( $msg{opt}->{$k} ); utf8::decode( $msg{opt}->{$k} );
push @mopt,{k=>$k,m=>$msg{opt}->{$k},c=>( $ag{$k} ? 'checked' : '' )}; push @mopt,
{
k => $k,
m => $msg{opt}->{$k},
c => ( $ag{$k} ? 'checked' : '' )
};
} }
$req->datas->{_openIdTrustExtMsg} .= $self->loadTemplate('openIdTrust',params => { $req->datas->{_openIdTrustExtMsg} .= $self->loadTemplate(
required => \@mreq, 'openIdTrust',
optional => \@mopt, params => {
}); required => \@mreq,
optional => \@mopt,
}
);
$self->logger->debug('Building validation form'); $self->logger->debug('Building validation form');
return ( 0, $prm ); return ( 0, $prm );

View File

@ -101,7 +101,7 @@ sub _redirect {
# Restore urldc if auth doesn't need to dial with browser # Restore urldc if auth doesn't need to dial with browser
$self->restoreRequest( $req, $ir ); $self->restoreRequest( $req, $ir );
return $self->run( @_, @path ); return $self->run( @_, @path );
} }
] ]
); );
} }

View File

@ -24,9 +24,10 @@ sub init {
if ( my $rules = $self->conf->{autoSigninRules} ) { if ( my $rules = $self->conf->{autoSigninRules} ) {
my $safe = Safe->new; my $safe = Safe->new;
foreach my $id ( sort keys %$rules ) { foreach my $id ( sort keys %$rules ) {
my $sub = $safe->reval('sub{my($env)=@_;return ('.$rules->{$id}.')}'); my $sub =
$safe->reval( 'sub{my($env)=@_;return (' . $rules->{$id} . ')}' );
if ($@) { if ($@) {
$self->error('Bad Autologin rule "'.$rules->{$id}.': $@'); $self->error( 'Bad Autologin rule "' . $rules->{$id} . ': $@' );
return 0; return 0;
} }
$id =~ s/^\s*([\w\-\@]+)\s*/$1/; $id =~ s/^\s*([\w\-\@]+)\s*/$1/;

View File

@ -24,8 +24,12 @@ SKIP: {
skip 'DBD::SQLite not found', $maintests; skip 'DBD::SQLite not found', $maintests;
} }
my $dbh = DBI->connect("dbi:SQLite:dbname=t/userdb.db"); my $dbh = DBI->connect("dbi:SQLite:dbname=t/userdb.db");
$dbh->do('CREATE TABLE users (user text,password text,name text,uid text,cn text,mail text)'); $dbh->do(
$dbh->do("INSERT INTO users VALUES ('dwho','dwho','Doctor who','dwho','Doctor who','dwho\@badwolf.org')"); 'CREATE TABLE users (user text,password text,name text,uid text,cn text,mail text)'
);
$dbh->do(
"INSERT INTO users VALUES ('dwho','dwho','Doctor who','dwho','Doctor who','dwho\@badwolf.org')"
);
# Initialization # Initialization
ok( $issuer = issuer(), 'Issuer portal' ); ok( $issuer = issuer(), 'Issuer portal' );

View File

@ -37,11 +37,12 @@ SKIP: {
krbByJs => 1, krbByJs => 1,
} }
} }
); );
ok( $res = $client->_get( '/', accept => 'text/html' ), 'Simple access' ); ok( $res = $client->_get( '/', accept => 'text/html' ), 'Simple access' );
ok( $res->[2]->[0] =~ /script.*kerberos\.js/s, 'Found Kerberos JS' ); ok( $res->[2]->[0] =~ /script.*kerberos\.js/s, 'Found Kerberos JS' );
my ( $host, $url, $query ) = expectForm( $res, '#'); my ( $host, $url, $query ) = expectForm( $res, '#' );
# TODO
# TODO
} }
count($maintests); count($maintests);
clean_sessions(); clean_sessions();

View File

@ -83,7 +83,7 @@ SKIP: {
$pwd = $2; $pwd = $2;
ok( $user eq 'fbar', 'Get good login' ); ok( $user eq 'fbar', 'Get good login' );
my $postString = 'user='.$user.'&password='.$pwd; my $postString = 'user=' . $user . '&password=' . $pwd;
ok( ok(
$res = $client->_post( $res = $client->_post(
'/', IO::String->new($postString), '/', IO::String->new($postString),

View File

@ -34,10 +34,10 @@ SKIP: {
authentication => 'LDAP', authentication => 'LDAP',
userDB => 'Same', userDB => 'Same',
passwordDB => 'LDAP', passwordDB => 'LDAP',
ldapServer => 'ldap://127.0.0.1:19389/', ldapServer => 'ldap://127.0.0.1:19389/',
ldapBase => 'ou=users,dc=example,dc=com', ldapBase => 'ou=users,dc=example,dc=com',
managerDn => 'cn=admin,dc=example,dc=com', managerDn => 'cn=admin,dc=example,dc=com',
managerPassword => 'admin', managerPassword => 'admin',
captcha_mail_enabled => 0, captcha_mail_enabled => 0,
portalDisplayResetPassword => 1, portalDisplayResetPassword => 1,
} }

View File

@ -7,7 +7,7 @@ require './lemonldap-ng-manager/lib/Lemonldap/NG/Manager/Build/Attributes.pm';
require './lemonldap-ng-common/lib/Lemonldap/NG/Common/Conf/ReConstants.pm'; require './lemonldap-ng-common/lib/Lemonldap/NG/Common/Conf/ReConstants.pm';
my $rmg = my $rmg =
"^(?:(?:$Lemonldap::NG::Common::Conf::ReConstants::virtualHostKeys)|(?:$Lemonldap::NG::Common::Conf::ReConstants::casAppMetaDataNodeKeys)|(?:$Lemonldap::NG::Common::Conf::ReConstants::casSrvMetaDataNodeKeys)|(?:$Lemonldap::NG::Common::Conf::ReConstants::oidcOPMetaDataNodeKeys)|(?:$Lemonldap::NG::Common::Conf::ReConstants::oidcRPMetaDataNodeKeys)|(?:$Lemonldap::NG::Common::Conf::ReConstants::samlIDPMetaDataNodeKeys)|(?:$Lemonldap::NG::Common::Conf::ReConstants::samlSPMetaDataNodeKeys)|(?:$Lemonldap::NG::Common::Conf::ReConstants::specialNodeKeys)|(.*Options))\$"; "^(?:(?:$Lemonldap::NG::Common::Conf::ReConstants::virtualHostKeys)|(?:$Lemonldap::NG::Common::Conf::ReConstants::casAppMetaDataNodeKeys)|(?:$Lemonldap::NG::Common::Conf::ReConstants::casSrvMetaDataNodeKeys)|(?:$Lemonldap::NG::Common::Conf::ReConstants::oidcOPMetaDataNodeKeys)|(?:$Lemonldap::NG::Common::Conf::ReConstants::oidcRPMetaDataNodeKeys)|(?:$Lemonldap::NG::Common::Conf::ReConstants::samlIDPMetaDataNodeKeys)|(?:$Lemonldap::NG::Common::Conf::ReConstants::samlSPMetaDataNodeKeys)|(?:$Lemonldap::NG::Common::Conf::ReConstants::specialNodeKeys))\$";
$rmg = qr/$rmg/; $rmg = qr/$rmg/;
my $ignore = qr/^(?:virtualHosts)$/; my $ignore = qr/^(?:virtualHosts)$/;