Merge branch 'v2.0' into findUser

This commit is contained in:
Christophe Maudoux 2021-01-14 22:38:35 +01:00
commit cd37ccc35c
36 changed files with 296 additions and 47 deletions

2
.ctags Normal file
View File

@ -0,0 +1,2 @@
--exclude=*/blib/*
--exclude=*.js

View File

@ -28,6 +28,21 @@
auth_request_set $headervalue14 $upstream_http_headervalue14;
auth_request_set $headername15 $upstream_http_headername15;
auth_request_set $headervalue15 $upstream_http_headervalue15;
auth_request_set $deleteheader1 $upstream_http_deleteheader1;
auth_request_set $deleteheader2 $upstream_http_deleteheader2;
auth_request_set $deleteheader3 $upstream_http_deleteheader3;
auth_request_set $deleteheader4 $upstream_http_deleteheader4;
auth_request_set $deleteheader5 $upstream_http_deleteheader5;
auth_request_set $deleteheader6 $upstream_http_deleteheader6;
auth_request_set $deleteheader7 $upstream_http_deleteheader7;
auth_request_set $deleteheader8 $upstream_http_deleteheader8;
auth_request_set $deleteheader9 $upstream_http_deleteheader9;
auth_request_set $deleteheader10 $upstream_http_deleteheader10;
auth_request_set $deleteheader11 $upstream_http_deleteheader11;
auth_request_set $deleteheader12 $upstream_http_deleteheader12;
auth_request_set $deleteheader13 $upstream_http_deleteheader13;
auth_request_set $deleteheader14 $upstream_http_deleteheader14;
auth_request_set $deleteheader15 $upstream_http_deleteheader15;
auth_request_set $lmcookie $upstream_http_cookie;
access_by_lua '
local i = 1
@ -38,7 +53,16 @@
else
break
end
i = i +1
i = i + 1
end
i = 1
while true do
if ngx.var["deleteheader"..i] ~= nil then
ngx.req.clear_header(ngx.var["deleteheader"..i])
else
break
end
i = i + 1
end
';

View File

@ -207,7 +207,11 @@ Password
reset (default: TRUE).
- **Allow a user to reset his expired password**: if activated, the
user will be prompted to change password if his password is expired
(default: 0)
(default: disabled)
- **Search for user before password change**: this option forces the password
change module to search for the user again, refreshing its DN. This feature
is only useful in rare cases when you use LDAP as the password module, but
not as the UserDB module. (default: enabled)
- **IBM Tivoli DS support**: enable this option if you use ITDS. LL::NG
will then scan error message to return a more precise error to the
user.

View File

@ -192,6 +192,33 @@ In this example we have:
'exportedHeaders/test.example.com' 'Auth-User' '$uid' \
'exportedHeaders/test.example.com' 'Auth-Mail' '$mail'
Configure form replay
---------------------
To add form replay on a host, you need to set the catched URI and
the variables to post.
In this example we have:
- Host: test.example.com
- Catched URI: /login.php
- jQuery URL: default
- Variables:
- login: $uid
- password: $_password
::
/usr/share/lemonldap-ng/bin/lemonldap-ng-cli -yes 1 -sep , \
addKey \
post,test.example.com,'/login.php' jqueryUrl default
/usr/share/lemonldap-ng/bin/lemonldap-ng-cli -yes 1 -sep , \
addPostVars \
post,test.example.com,'/login.php' login '$uid' \
post,test.example.com,'/login.php' password '$_password'
Configure LDAP authentication backend
-------------------------------------

View File

@ -20,6 +20,13 @@ backups and a rollback plan ready!
2.0.10
------
A vulnerability affecting LemonLDAP::NG installations has been found out when ALL following criteria apply:
* Your handler server uses Nginx
* Your virtual host configuration contains per-URL 'skip' or 'unprotect' access rule
In this situation, you have to update your LUA configuration file like ``/etc/nginx/nginx-lua-headers.conf``
- New dependency: IO::Socket::Timeout
- TOTP check tolerates forward AND backward clock drift (totp2fRange)
- Avoid assignment in expressions option is disabled by default

View File

@ -30,7 +30,7 @@ use constant DEFAULTCONFBACKENDOPTIONS => (
dirName => '/usr/local/lemonldap-ng/data/conf',
);
our $hashParameters = qr/^(?:(?:l(?:o(?:ca(?:lSessionStorageOption|tionRule)|goutService)|dapExportedVar|wp(?:Ssl)?Opt)|(?:(?:d(?:emo|bi)|webID)ExportedVa|exported(?:Heade|Va)|issuerDBGetParamete)r|f(?:indUser(?:Exclud|Search)ingAttribute|acebookExportedVar)|re(?:moteGlobalStorageOption|st2f(?:Verify|Init)Arg|loadUrl)|g(?:r(?:antSessionRule|oup)|lobalStorageOption)|n(?:otificationStorageOption|ginxCustomHandler)|macro)s|o(?:idc(?:S(?:ervice(?:DynamicRegistrationEx(?:portedVar|traClaim)s|MetaDataAuthnContext)|torageOptions)|RPMetaData(?:(?:Option(?:sExtraClaim)?|ExportedVar|Macro)s|Node)|OPMetaData(?:(?:ExportedVar|Option)s|J(?:SON|WKS)|Node))|penIdExportedVars)|c(?:as(?:A(?:ppMetaData(?:(?:ExportedVar|Option|Macro)s|Node)|ttributes)|S(?:rvMetaData(?:(?:ExportedVar|Option)s|Node)|torageOptions))|(?:ustom(?:Plugins|Add)Param|heckUserHiddenHeader|ombModule)s)|s(?:aml(?:S(?:PMetaData(?:(?:ExportedAttribute|Option|Macro)s|Node|XML)|torageOptions)|IDPMetaData(?:(?:ExportedAttribute|Option)s|Node|XML))|essionDataToRemember|laveExportedVars|fExtra)|a(?:(?:daptativeAuthenticationLevelR|ut(?:hChoiceMod|oSigninR))ules|pplicationList)|p(?:ersistentStorageOptions|o(?:rtalSkinRules|st))|v(?:hostOptions|irtualHost)|S(?:MTPTLSOpts|SLVarIf))$/;
our $boolKeys = qr/^(?:s(?:aml(?:IDP(?:MetaDataOptions(?:(?:Check(?:S[LS]OMessageSignatur|Audienc|Tim)|IsPassiv)e|A(?:llow(?:LoginFromIDP|ProxiedAuthn)|daptSessionUtime)|Force(?:Authn|UTF8)|StoreSAMLToken|RelayStateURL)|SSODescriptorWantAuthnRequestsSigned)|S(?:P(?:MetaDataOptions(?:(?:CheckS[LS]OMessageSignatur|OneTimeUs)e|EnableIDPInitiatedURL|ForceUTF8)|SSODescriptor(?:WantAssertion|AuthnRequest)sSigned)|erviceUseCertificateInResponse)|DiscoveryProtocol(?:Activation|IsPassive)|CommonDomainCookieActivation|UseQueryStringSpecific|MetadataForceUTF8)|f(?:RemovedUseNotif|OnlyUpgrade)|kip(?:Upgrade|Renew)Confirmation|oap(?:Session|Config)Server|t(?:ayConnecte|orePasswor)d|laveDisplayLogo|howLanguages|slByAjax)|o(?:idc(?:RPMetaDataOptions(?:Allow(?:PasswordGrant|Offline)|Re(?:freshToken|quirePKCE)|LogoutSessionRequired|IDTokenForceClaims|BypassConsent|Public)|ServiceAllow(?:(?:AuthorizationCode|Implicit|Hybrid)Flow|DynamicRegistration)|OPMetaDataOptions(?:(?:CheckJWTSignatur|UseNonc)e|StoreIDToken))|ldNotifFormat)|p(?:ortal(?:Display(?:Re(?:freshMyRights|setPassword|gister)|CertificateResetByMail|GeneratePassword|PasswordPolicy)|ErrorOn(?:ExpiredSession|MailNotFound)|(?:CheckLogin|Statu)s|OpenLinkInNewWindow|ForceAuthn|AntiFrame)|roxyUseSoap)|c(?:o(?:ntextSwitching(?:Allowed2fModifications|StopWithLogout)|mpactConf|rsEnabled)|a(?:ptcha_(?:register|login|mail)_enabled|sSrvMetaDataOptions(?:Gateway|Renew))|heck(?:State|User|XSS)|da)|l(?:dap(?:(?:Group(?:DecodeSearchedValu|Recursiv)|UsePasswordResetAttribut)e|(?:AllowResetExpired|Set)Password|ChangePasswordAsUser|PpolicyControl|ITDS)|oginHistoryEnabled)|no(?:tif(?:ication(?:Server(?:(?:POS|GE)T|DELETE)?|sExplorer)?|y(?:Deleted|Other))|AjaxHook)|i(?:ssuerDB(?:OpenID(?:Connect)?|SAML|CAS|Get)Activation|mpersonationSkipEmptyValues)|to(?:tp2f(?:UserCan(?:Chang|Remov)eKey|DisplayExistingSecret)|kenUseGlobalStorage)|u(?:se(?:RedirectOn(?:Forbidden|Error)|SafeJail)|2fUserCanRemoveKey|pgradeSession)|re(?:st(?:(?:Password|Session|Config|Auth)Server|ExportSecretKeys)|freshSessions)|br(?:uteForceProtection(?:IncrementalTempo)?|owsersDontStorePassword)|d(?:is(?:ablePersistentStorage|playSessionId)|biDynamicHashEnabled)|(?:mai(?:lOnPasswordChang|ntenanc)|vhostMaintenanc)e|g(?:roupsBeforeMacros|lobalLogoutTimer)|a(?:voidAssignment|ctiveTimer)|h(?:ideOldPassword|ttpOnly)|yubikey2fUserCanRemoveKey|krb(?:RemoveDomain|ByJs)|(?:wsdlServ|findUs)er)$/;
our $boolKeys = qr/^(?:s(?:aml(?:IDP(?:MetaDataOptions(?:(?:Check(?:S[LS]OMessageSignatur|Audienc|Tim)|IsPassiv)e|A(?:llow(?:LoginFromIDP|ProxiedAuthn)|daptSessionUtime)|Force(?:Authn|UTF8)|StoreSAMLToken|RelayStateURL)|SSODescriptorWantAuthnRequestsSigned)|S(?:P(?:MetaDataOptions(?:(?:CheckS[LS]OMessageSignatur|OneTimeUs)e|EnableIDPInitiatedURL|ForceUTF8)|SSODescriptor(?:WantAssertion|AuthnRequest)sSigned)|erviceUseCertificateInResponse)|DiscoveryProtocol(?:Activation|IsPassive)|CommonDomainCookieActivation|UseQueryStringSpecific|MetadataForceUTF8)|f(?:RemovedUseNotif|OnlyUpgrade)|kip(?:Upgrade|Renew)Confirmation|oap(?:Session|Config)Server|t(?:ayConnecte|orePasswor)d|laveDisplayLogo|howLanguages|slByAjax)|o(?:idc(?:RPMetaDataOptions(?:Allow(?:PasswordGrant|Offline)|Re(?:freshToken|quirePKCE)|LogoutSessionRequired|IDTokenForceClaims|BypassConsent|Public)|ServiceAllow(?:(?:AuthorizationCode|Implicit|Hybrid)Flow|DynamicRegistration)|OPMetaDataOptions(?:(?:CheckJWTSignatur|UseNonc)e|StoreIDToken))|ldNotifFormat)|p(?:ortal(?:Display(?:Re(?:freshMyRights|setPassword|gister)|CertificateResetByMail|GeneratePassword|PasswordPolicy)|ErrorOn(?:ExpiredSession|MailNotFound)|(?:CheckLogin|Statu)s|OpenLinkInNewWindow|ForceAuthn|AntiFrame)|roxyUseSoap)|l(?:dap(?:(?:G(?:roup(?:DecodeSearchedValu|Recursiv)|etUserBeforePasswordChang)|UsePasswordResetAttribut)e|(?:AllowResetExpired|Set)Password|ChangePasswordAsUser|PpolicyControl|ITDS)|oginHistoryEnabled)|c(?:o(?:ntextSwitching(?:Allowed2fModifications|StopWithLogout)|mpactConf|rsEnabled)|a(?:ptcha_(?:register|login|mail)_enabled|sSrvMetaDataOptions(?:Gateway|Renew))|heck(?:State|User|XSS)|da)|no(?:tif(?:ication(?:Server(?:(?:POS|GE)T|DELETE)?|sExplorer)?|y(?:Deleted|Other))|AjaxHook)|i(?:ssuerDB(?:OpenID(?:Connect)?|SAML|CAS|Get)Activation|mpersonationSkipEmptyValues)|to(?:tp2f(?:UserCan(?:Chang|Remov)eKey|DisplayExistingSecret)|kenUseGlobalStorage)|u(?:se(?:RedirectOn(?:Forbidden|Error)|SafeJail)|2fUserCanRemoveKey|pgradeSession)|re(?:st(?:(?:Password|Session|Config|Auth)Server|ExportSecretKeys)|freshSessions)|br(?:uteForceProtection(?:IncrementalTempo)?|owsersDontStorePassword)|d(?:is(?:ablePersistentStorage|playSessionId)|biDynamicHashEnabled)|(?:mai(?:lOnPasswordChang|ntenanc)|vhostMaintenanc)e|g(?:roupsBeforeMacros|lobalLogoutTimer)|a(?:voidAssignment|ctiveTimer)|h(?:ideOldPassword|ttpOnly)|yubikey2fUserCanRemoveKey|krb(?:RemoveDomain|ByJs)|(?:wsdlServ|findUs)er)$/;
our @sessionTypes = ( 'remoteGlobal', 'global', 'localSession', 'persistent', 'saml', 'oidc', 'cas' );

View File

@ -45,7 +45,7 @@ our $authParameters = {
githubParams => [qw(githubAuthnLevel githubClientID githubClientSecret githubUserField githubScope)],
gpgParams => [qw(gpgAuthnLevel gpgDb)],
kerberosParams => [qw(krbAuthnLevel krbKeytab krbByJs krbRemoveDomain krbAllowedDomains)],
ldapParams => [qw(ldapAuthnLevel ldapExportedVars ldapServer ldapPort ldapVerify ldapBase managerDn managerPassword ldapTimeout ldapIOTimeout ldapVersion ldapRaw ldapCAFile ldapCAPath LDAPFilter AuthLDAPFilter mailLDAPFilter ldapSearchDeref ldapGroupBase ldapGroupObjectClass ldapGroupAttributeName ldapGroupAttributeNameUser ldapGroupAttributeNameSearch ldapGroupDecodeSearchedValue ldapGroupRecursive ldapGroupAttributeNameGroup ldapPpolicyControl ldapSetPassword ldapChangePasswordAsUser ldapPwdEnc ldapUsePasswordResetAttribute ldapPasswordResetAttribute ldapPasswordResetAttributeValue ldapAllowResetExpiredPassword ldapITDS)],
ldapParams => [qw(ldapAuthnLevel ldapExportedVars ldapServer ldapPort ldapVerify ldapBase managerDn managerPassword ldapTimeout ldapIOTimeout ldapVersion ldapRaw ldapCAFile ldapCAPath LDAPFilter AuthLDAPFilter mailLDAPFilter ldapSearchDeref ldapGroupBase ldapGroupObjectClass ldapGroupAttributeName ldapGroupAttributeNameUser ldapGroupAttributeNameSearch ldapGroupDecodeSearchedValue ldapGroupRecursive ldapGroupAttributeNameGroup ldapPpolicyControl ldapSetPassword ldapChangePasswordAsUser ldapPwdEnc ldapUsePasswordResetAttribute ldapPasswordResetAttribute ldapPasswordResetAttributeValue ldapAllowResetExpiredPassword ldapGetUserBeforePasswordChange ldapITDS)],
linkedinParams => [qw(linkedInAuthnLevel linkedInClientID linkedInClientSecret linkedInFields linkedInUserField linkedInScope)],
nullParams => [qw(nullAuthnLevel)],
oidcParams => [qw(oidcAuthnLevel oidcRPCallbackGetParam oidcRPStateTimeout)],

View File

@ -22,7 +22,10 @@ for ( my $i = 0 ; $i < @ARGV ; $i++ ) {
$action ||= "help";
if ( $action =~ /^(?:[gs]et|del|(?:add|del)Key|save|restore|rollback)$/ ) {
if ( $action =~
/^(?:[gs]et|del|(?:add|del)Key|(?:add|del)PostVars|save|restore|rollback)$/
)
{
eval { require Lemonldap::NG::Manager::Cli; };
die "Manager libraries not available, aborting ($@)" if ($@);
Lemonldap::NG::Manager::Cli->run(@ARGV);
@ -40,19 +43,21 @@ sub help {
print STDERR qq{Usage: $0 <options> action <parameters>
Available actions:
- help : print this
- info : get currentconfiguration info
- update-cache : force configuration cache to be updated
- test-email <destination> : send a test email
- get <keys> : get values of parameters
- set <key> <value> : set parameter(s) value(s)
- del <keys> : delete parameters
- addKey <key> <subkey> <value> : add or set a subkey in a parameter
- delKey <key> <subkey> : delete subkey of a parameter
- save : export configuration to STDOUT
- restore - : import configuration from STDIN
- restore <file> : import configuration from file
- rollback : restore previous configuration
- help : print this
- info : get currentconfiguration info
- update-cache : force configuration cache to be updated
- test-email <destination> : send a test email
- get <key> : get values of parameters
- set <key> <value> : set parameter(s) value(s)
- del <key> : delete parameters
- addKey <key> <subkey> <value> : add or set a subkey in a parameter
- delKey <key> <subkey> : delete subkey of a parameter
- addPostVars <host> <uri> <key> <value> : add post vars for form replay
- delPostVars <host> <uri> <key> : delete post vars for form replay
- save : export configuration to STDOUT
- restore - : import configuration from STDIN
- restore <file> : import configuration from file
- rollback : restore previous configuration
Options:
- yes <0|1> : accept confirmation prompt automatically

View File

@ -228,6 +228,10 @@ sub run {
elsif ( $protection == $class->MAYSKIP
and $class->grant( $req, $session, $uri, $cond ) eq '999_SKIP' )
{
$class->logger->debug("Access control skipped");
$class->updateStatus( $req, 'SKIP' );
$class->hideCookie($req);
$class->cleanHeaders($req);
return $class->OK;
}
@ -764,6 +768,7 @@ sub cleanHeaders {
my ( $class, $req ) = @_;
my $vhost = $class->resolveAlias($req);
if ( defined( $class->tsv->{headerList}->{$vhost} ) ) {
$class->logger->debug("Remove headers relative to $vhost");
$class->unset_header_in( $req,
@{ $class->tsv->{headerList}->{$vhost} } );
}

View File

@ -5,7 +5,7 @@ package Lemonldap::NG::Handler::Server::Main;
use strict;
our $VERSION = '2.0.6';
our $VERSION = '2.0.10';
use base 'Lemonldap::NG::Handler::PSGI::Main';
@ -25,13 +25,27 @@ sub set_header_in {
push @{ $req->{respHeaders} }, %headers;
}
## @method void unset_header_in(array headers)
# deletes request headers and push headers that will be removed by LUA
# @param headers array containing header names
sub unset_header_in {
my ( $class, $req, $header ) = @_;
$req->{respHeaders} = [ grep { $_ ne $header and $_ ne cgiName($header) }
@{ $req->{respHeaders} } ];
delete $req->{env}->{ cgiName($header) };
$header =~ s/-/_/g;
delete $req->{env}->{$header};
my ( $class, $req, @headers ) = @_;
$req->data->{deleteIndex} //= 1;
my $i = $req->data->{deleteIndex};
foreach my $header (@headers) {
$class->logger->debug("Delete header $header");
$req->{respHeaders} =
[ grep { $_ ne $header and $_ ne cgiName($header) }
@{ $req->{respHeaders} } ];
delete $req->{env}->{ cgiName($header) };
push @{ $req->{respHeaders} }, "Deleteheader$i", $header;
$i++;
push @{ $req->{respHeaders} }, "Deleteheader$i", cgiName($header);
$header =~ s/-/_/g;
delete $req->{env}->{$header};
$i++;
}
$req->data->{deleteIndex} = $i;
}
# Inheritence is broken in this case with Debian >= jessie

View File

@ -70,7 +70,7 @@ sub handler {
( 'Content-Length' => 0, Cookie => ( $req->env->{HTTP_COOKIE} // '' ) );
my $i = 0;
while ( my ( $k, $v ) = splice( @{ $req->{respHeaders} }, 0, 2 ) ) {
if ( $k =~ /^(?:Lm-Remote-(?:User|Custom)|Cookie)$/ ) {
if ( $k =~ /^(?:Deleteheader\d+|Lm-Remote-(?:User|Custom)|Cookie)$/ ) {
push @convertedHdrs, $k, $v;
}
else {

View File

@ -204,11 +204,7 @@ count(3);
ok( $res = $client->_get( '/skipif/za', undef, 'test1.example.com' ),
'Test skip() rule 1' );
ok( $res->[0] == 302, ' Code is 302' ) or explain( $res, 302 );
$SKIPUSER = 1;
ok( $res = $client->_get( '/skipif/zz', undef, 'test1.example.com' ),
'Test skip() rule 2' );
ok( $res->[0] == 200, ' Code is 200' ) or explain( $res, 200 );
count(4);
count(2);
# Wildcards
ok(
@ -219,6 +215,20 @@ ok(
ok( $res->[0] == 200, ' Code is 200' ) or explain( $res, 200 );
count(2);
# SKIP TESTS
$SKIPUSER = 1;
ok( $res = $client->_get( '/skipif/zz', undef, 'test1.example.com' ),
'Test skip() rule 2' );
ok( $res->[0] == 200, ' Code is 200' ) or explain( $res, 200 );
count(2);
# Forged headers
ok( $res = $client->_get( '/skipif/zz', undef, 'test1.example.com', undef, HTTP_AUTH_USER => 'rtyler' ),
'Test skip() with forged header' );
ok( $res->[0] == 200, ' Code is 200' ) or explain( $res, 200 );
count(2);
ok(
$res =
$client->_get( '/', undef, 'foo.example.fr', "lemonldap=$sessionId" ),
@ -295,10 +305,14 @@ clean();
sub Lemonldap::NG::Handler::PSGI::handler {
my ( $self, $req ) = @_;
unless ($SKIPUSER) {
if ($SKIPUSER) {
ok( !$req->env->{HTTP_AUTH_USER}, 'No HTTP_AUTH_USER' )
or explain( $req->env->{HTTP_AUTH_USER}, '<empty>' );
}
else {
ok( $req->env->{HTTP_AUTH_USER} eq 'dwho', 'Header is given to app' )
or explain( $req->env->{HTTP_AUTH_USER}, 'dwho' );
count(1);
}
count(1);
return [ 200, [ 'Content-Type', 'text/plain' ], ['Hello'] ];
}

View File

@ -191,6 +191,28 @@ ok(
);
count(3);
# Clean headers
ok(
$res = $client->_get(
'/skipif/zz', undef, 'test1.example.com', undef,
HTTP_AUTH_USER => 'rtyler'
),
'Test skip() with forged header'
);
ok( $res->[0] == 200, 'Code is 200' ) or explain( $res, 200 );
count(2);
%h = @{ $res->[1] };
my %delete;
foreach ( keys %h ) {
/^Deleteheader\d$/ and $delete{ $h{$_} }++;
}
foreach (qw(Cookie HTTP_COOKIE Auth-User HTTP_AUTH_USER)) {
ok( $delete{$_}, "Delete command for $_" )
or explain( \%h, 'Delete* headers' );
ok( !$h{$_}, "$_ is deleted" ) or explain( \%h, 'Delete* headers' );
count(2);
}
done_testing( count() );
clean();

View File

@ -5,7 +5,6 @@ sub accessToTrace {
my $custom = $hash->{custom};
my $req = $hash->{req};
my $vhost = $hash->{vhost};
my $custom = $hash->{custom};
my $params = $hash->{params};
my $session = $hash->{session};

View File

@ -166,6 +166,16 @@ sub updateOidcRp {
return $self->sendError( $req, "Invalid input: " . $req->error, 400 )
unless ($update);
if ( $update->{redirectUris} ) {
if ( ref( $update->{redirectUris} ) eq "ARRAY" ) {
$update->{options}->{redirectUris} = $update->{redirectUris};
}
else {
return $self->sendError( $req,
'Invalid input: redirectUris must be an array', 400 );
}
}
$self->logger->debug(
"[API] OIDC RP $confKey configuration update requested");
@ -294,7 +304,11 @@ sub _getOidcRpByConfKey {
my $macros = $conf->{oidcRPMetaDataMacros}->{$confKey} || {};
# Redirect URIs, filled later
my $redirectUris;
my $redirectUris = $self->_translateValueConfToApi(
'oidcRPMetaDataOptionsRedirectUris',
$conf->{oidcRPMetaDataOptions}->{$confKey}
->{oidcRPMetaDataOptionsRedirectUris}
);
# Get options
my $options = {};

View File

@ -1685,6 +1685,10 @@ qr/^(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.])*(?:[a-zA-Z][-a-zA-Z0-
'LDAPFilter' => {
'type' => 'text'
},
'ldapGetUserBeforePasswordChange' => {
'default' => 0,
'type' => 'bool'
},
'ldapGroupAttributeName' => {
'default' => 'member',
'type' => 'text'

View File

@ -3324,6 +3324,10 @@ m{^(?:ldapi://[^/]*/?|\w[\w\-\.]*(?::\d{1,5})?|ldap(?:s|\+tls)?://\w[\w\-\.]*(?:
default => 0,
type => 'bool',
},
ldapGetUserBeforePasswordChange => {
default => 0,
type => 'bool',
},
ldapSearchDeref => {
type => 'select',
select => [

View File

@ -302,6 +302,7 @@ sub tree {
'ldapPasswordResetAttribute',
'ldapPasswordResetAttributeValue',
'ldapAllowResetExpiredPassword',
'ldapGetUserBeforePasswordChange',
'ldapITDS'
]
},

View File

@ -7,7 +7,7 @@ use Data::Dumper;
use JSON;
use Lemonldap::NG::Common::Conf::ReConstants;
our $VERSION = '2.0.9';
our $VERSION = '2.0.10';
$Data::Dumper::Useperl = 1;
extends('Lemonldap::NG::Manager::Cli::Lib');
@ -43,6 +43,23 @@ sub get {
print "$key has the following keys:\n";
print " $_\n" foreach ( sort keys %$value );
}
elsif ( ref $value eq 'ARRAY' ) {
print "$key is an array with values:\n";
foreach my $avalue (@$value) {
if ( ref $avalue eq 'HASH' ) {
print "\tHash with following keys:\n";
print "\t\t$_\n" foreach ( sort keys %$avalue );
}
elsif ( ref $value eq 'ARRAY' ) {
print "\tArray with following keys:\n";
print "\t\t$_\n" foreach (@$avalue);
}
else {
$avalue //= '';
print "\tValue = $avalue\n";
}
}
}
else {
$value //= '';
print "$key = $value\n";
@ -230,6 +247,68 @@ sub delKey {
return $self->_save($new);
}
sub addPostVars {
my $self = shift;
unless ( @_ % 4 == 0 ) {
die 'usage: "addPostVars (?:vhost uri key value)+';
}
my @list;
while (@_) {
my $vhost = shift;
my $uri = shift;
my $key = shift;
my $value = shift;
$self->logger->info(
"CLI: Append post vars $key $value to URI $uri for vhost $vhost");
push @list, [ $vhost, $uri, $key, $value ];
}
require Clone;
my $new = Clone::clone( $self->mgr->hLoadedPlugins->{conf}->currentConf );
foreach my $el (@list) {
$new->{post}->{ $el->[0] }->{ $el->[1] }->{vars} = []
unless ( defined $new->{post}->{ $el->[0] }->{ $el->[1] }->{vars} );
push(
@{ $new->{post}->{ $el->[0] }->{ $el->[1] }->{vars} },
[ $el->[2], $el->[3] ]
);
}
return $self->_save($new);
}
sub delPostVars {
my $self = shift;
unless ( @_ % 3 == 0 ) {
die 'usage: "delPostVars (?:vhost uri key)+';
}
my @list;
while (@_) {
my $vhost = shift;
my $uri = shift;
my $key = shift;
$self->logger->info(
"CLI: Delete post vars $key from URI $uri for vhost $vhost");
push @list, [ $vhost, $uri, $key ];
}
require Clone;
my $new = Clone::clone( $self->mgr->hLoadedPlugins->{conf}->currentConf );
foreach my $el (@list) {
$new->{post}->{ $el->[0] }->{ $el->[1] }->{vars} = []
unless ( defined $new->{post}->{ $el->[0] }->{ $el->[1] }->{vars} );
for (
my $i = 0 ;
$i <= $#{ $new->{post}->{ $el->[0] }->{ $el->[1] }->{vars} } ;
$i++
)
{
delete( $new->{post}->{ $el->[0] }->{ $el->[1] }->{vars}->[$i] )
if (
$new->{post}->{ $el->[0] }->{ $el->[1] }->{vars}->[$i]->[0] eq
$el->[2] );
}
}
return $self->_save($new);
}
sub lastCfg {
my ($self) = @_;
$self->logger->info("CLI: Retrieve last conf.");
@ -337,7 +416,8 @@ sub _setKey {
sub _save {
my ( $self, $new ) = @_;
require Lemonldap::NG::Manager::Conf::Parser;
my $parser = Lemonldap::NG::Manager::Conf::Parser->new( {
my $parser = Lemonldap::NG::Manager::Conf::Parser->new(
{
newConf => $new,
refConf => $self->mgr->hLoadedPlugins->{conf}->currentConf,
req => $self->req
@ -432,11 +512,12 @@ sub run {
die 'nothing to do, aborting';
}
my $action = shift;
unless (
$action =~ /^(?:get|set|del|addKey|delKey|save|restore|rollback)$/ )
unless ( $action =~
/^(?:get|set|del|addKey|delKey|addPostVars|delPostVars|save|restore|rollback)$/
)
{
die
"Unknown action $action. Only get, set, del, addKey, delKey, save, restore, rollback allowed";
"Unknown action $action. Only get, set, del, addKey, delKey, addPostVars, delPostVars, save, restore, rollback allowed";
}
unless ( $action eq "restore" ) {

View File

@ -443,6 +443,7 @@
"ldapFilters":"فلتر",
"LDAPFilter":"فلتر الاعْتيادي",
"ldapGroupAttributeName":"السمات المستهدف",
"ldapGetUserBeforePasswordChange":"Search for user before password change",
"ldapGroupAttributeNameGroup":"سمات مصدر المجموعة",
"ldapGroupAttributeNameSearch":"السمات التي تم البحث عنها",
"ldapGroupAttributeNameUser":"سمة مصدر المستخدم",

View File

@ -442,6 +442,7 @@
"ldapExportedVars":"Exported variables",
"ldapFilters":"Filters",
"LDAPFilter":"Default filter",
"ldapGetUserBeforePasswordChange":"Search for user before password change",
"ldapGroupAttributeName":"Target attribute",
"ldapGroupAttributeNameGroup":"Group source attribute",
"ldapGroupAttributeNameSearch":"Searched attributes",

View File

@ -442,6 +442,7 @@
"ldapExportedVars":"Exported variables",
"ldapFilters":"Filters",
"LDAPFilter":"Default filter",
"ldapGetUserBeforePasswordChange":"Search for user before password change",
"ldapGroupAttributeName":"Target attribute",
"ldapGroupAttributeNameGroup":"Group source attribute",
"ldapGroupAttributeNameSearch":"Searched attributes",

View File

@ -442,6 +442,7 @@
"ldapExportedVars":"Variables exportadas",
"ldapFilters":"Filtros",
"LDAPFilter":"Filtro por defecto",
"ldapGetUserBeforePasswordChange":"Search for user before password change",
"ldapGroupAttributeName":"Atributo objetivo",
"ldapGroupAttributeNameGroup":"Group source attribute",
"ldapGroupAttributeNameSearch":"Atributos buscados",

View File

@ -442,6 +442,7 @@
"ldapExportedVars":"Variables exportées",
"ldapFilters":"Filtres",
"LDAPFilter":"Filtre par défaut",
"ldapGetUserBeforePasswordChange":"Rechercher l'utilisateur avant le changement de mot de passe",
"ldapGroupAttributeName":"Attribut cible",
"ldapGroupAttributeNameGroup":"Attribut source groupe",
"ldapGroupAttributeNameSearch":"Attributs recherchés",

View File

@ -442,6 +442,7 @@
"ldapExportedVars":"Variabili esportate",
"ldapFilters":"Filtri",
"LDAPFilter":"Filtro predefinito",
"ldapGetUserBeforePasswordChange":"Search for user before password change",
"ldapGroupAttributeName":"Attributo target",
"ldapGroupAttributeNameGroup":"Attributo del gruppo sorgente",
"ldapGroupAttributeNameSearch":"Attributi ricercati",

View File

@ -442,6 +442,7 @@
"ldapExportedVars":"Wyeksportowane zmienne",
"ldapFilters":"Filtry",
"LDAPFilter":"Domyślny filtr",
"ldapGetUserBeforePasswordChange":"Search for user before password change",
"ldapGroupAttributeName":"Atrybut docelowy",
"ldapGroupAttributeNameGroup":"Atrybut źródła grupy",
"ldapGroupAttributeNameSearch":"Szukane atrybuty",

View File

@ -442,6 +442,7 @@
"ldapExportedVars":"Dışa aktarılan değişkenler",
"ldapFilters":"Filtreler",
"LDAPFilter":"Varsayılan filtre",
"ldapGetUserBeforePasswordChange":"Search for user before password change",
"ldapGroupAttributeName":"Hedef nitelik",
"ldapGroupAttributeNameGroup":"Grup kaynağı niteliği",
"ldapGroupAttributeNameSearch":"Aranan nitelikler",

View File

@ -442,6 +442,7 @@
"ldapExportedVars":"Biến đã được xuất",
"ldapFilters":"Bộ lọc",
"LDAPFilter":"Bộ lọc mặc định",
"ldapGetUserBeforePasswordChange":"Search for user before password change",
"ldapGroupAttributeName":"Thuộc tính đích",
"ldapGroupAttributeNameGroup":"Thuộc tính nguồn nhóm",
"ldapGroupAttributeNameSearch":"Thuộc tính đã tìm kiếm",

View File

@ -442,6 +442,7 @@
"ldapExportedVars":"Exported variables",
"ldapFilters":"Filters",
"LDAPFilter":"Default filter",
"ldapGetUserBeforePasswordChange":"Search for user before password change",
"ldapGroupAttributeName":"Target attribute",
"ldapGroupAttributeNameGroup":"Group source attribute",
"ldapGroupAttributeNameSearch":"Searched attributes",

View File

@ -442,6 +442,7 @@
"ldapExportedVars":"已匯出的變數",
"ldapFilters":"過濾器",
"LDAPFilter":"預設過濾器",
"ldapGetUserBeforePasswordChange":"Search for user before password change",
"ldapGroupAttributeName":"目標屬性",
"ldapGroupAttributeNameGroup":"群組來源屬性",
"ldapGroupAttributeNameSearch":"已搜尋的屬性",

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -21,7 +21,7 @@ use Lemonldap::NG::Portal::Main::Constants qw(
PE_UNAUTHORIZEDPARTNER
);
our $VERSION = '2.0.9';
our $VERSION = '2.0.10';
extends 'Lemonldap::NG::Portal::Main::Issuer',
'Lemonldap::NG::Portal::Lib::SAML';
@ -680,20 +680,20 @@ sub run {
unless ( defined $value ) {
if ($mandatory) {
$self->logger->error(
"Session key $_ is required to set SAML $name attribute"
"Session key $_ is required to set SAML $name attribute ($sp)"
);
return PE_ISSUERMISSINGREQATTR;
}
else {
$self->logger->debug(
"SAML2 attribute $name has no value but is not mandatory, skip it"
"SAML2 attribute $name has no value but is not mandatory ($sp), skip it"
);
next;
}
}
$self->logger->debug(
"SAML2 attribute $name will be set with $_ session key");
"SAML2 attribute $name will be set with $_ session key ($sp)");
# SAML2 attribute
my $attribute =

View File

@ -30,6 +30,12 @@ sub confirm {
sub modifyPassword {
my ( $self, $req, $pwd, $useMail ) = @_;
# If the password change is done in a different backend,
# we need to reload the correct DN
$self->getUser( $req, useMail => $useMail )
if $self->conf->{ldapGetUserBeforePasswordChange};
my $dn = $req->data->{dn} || $req->sessionInfo->{_dn};
unless ($dn) {
$self->logger->error('"dn" is not set, abort password modification');

View File

@ -37,7 +37,7 @@ sub init {
$tmp =
$self->loadModule( "::Password::$mod->{type}", $mod->{over} );
unless ($tmp) {
$self->notice("Unable to load Password::$mod->{type}");
$self->logger->notice("Unable to load Password::$mod->{type}");
next;
}
}

View File

@ -32,6 +32,11 @@ sub modifyPassword {
my $dn;
my $requireOldPassword;
# If the password change is done in a different backend,
# we need to reload the correct DN
$self->getUser( $req, useMail => $useMail )
if $self->conf->{ldapGetUserBeforePasswordChange};
if ( $req->data->{dn} ) {
$dn = $req->data->{dn};
$requireOldPassword = $self->requireOldPwdRule->( $req, $req->userData );