Merge branch 'feature-userdb-password-restserver' into v2.0

This commit is contained in:
Maxime Besson 2020-05-09 20:07:24 +02:00
commit db9e862843
24 changed files with 431 additions and 22 deletions

View File

@ -79,6 +79,11 @@
Require all denied
</Location>
# REST/SOAP functions for proxy auth and password reset (disabled by default)
<Location /index.fcgi/proxy>
Require all denied
</Location>
# REST/SOAP functions for sessions access (disabled by default)
<Location /index.fcgi/sessions>
Require all denied

View File

@ -86,6 +86,17 @@
</IfVersion>
</Location>
# REST/SOAP functions for proxy auth and password reset (disabled by default)
<Location /index.fcgi/proxy>
<IfVersion >= 2.3>
Require all denied
</IfVersion>
<IfVersion < 2.3>
Order Deny,Allow
Deny from all
</IfVersion>
</Location>
# REST/SOAP functions for sessions access (disabled by default)
<Location /index.fcgi/sessions>
<IfVersion >= 2.3>

View File

@ -72,6 +72,12 @@
Deny from all
</Location>
# REST/SOAP functions for proxy auth and password reset (disabled by default)
<Location /index.fcgi/proxy>
Order deny,allow
Deny from all
</Location>
# REST/SOAP functions for sessions access (disabled by default)
<Location /index.fcgi/sessions>
Order deny,allow

View File

@ -59,6 +59,12 @@ server {
deny all;
}
# REST/SOAP functions for proxy auth and password reset (disabled by default)
location ~ ^/index.psgi/proxy {
fastcgi_pass llng_portal_upstream;
deny all;
}
# REST/SOAP functions for sessions access (disabled by default)
location ~ ^/index.psgi/sessions {
fastcgi_pass llng_portal_upstream;

View File

@ -24,7 +24,7 @@ use constant MANAGERSECTION => "manager";
use constant SESSIONSEXPLORERSECTION => "sessionsExplorer";
use constant APPLYSECTION => "apply";
our $hashParameters = qr/^(?:(?:l(?:o(?:ca(?:lSessionStorageOption|tionRule)|goutService)|dapExportedVar|wp(?:Ssl)?Opt)|(?:(?:d(?:emo|bi)|facebook|webID)ExportedVa|exported(?:Heade|Va)|issuerDBGetParamete)r|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)|s(?:aml(?:S(?:PMetaData(?:(?:ExportedAttribute|Option|Macro)s|Node|XML)|torageOptions)|IDPMetaData(?:(?:ExportedAttribute|Option)s|Node|XML))|essionDataToRemember|laveExportedVars|fExtra)|c(?:as(?:A(?:ppMetaData(?:(?:ExportedVar|Option|Macro)s|Node)|ttributes)|S(?:rvMetaData(?:(?:ExportedVar|Option)s|Node)|torageOptions))|(?:ustom(?:Plugins|Add)Param|ombModule)s)|p(?:ersistentStorageOptions|o(?:rtalSkinRules|st))|a(?:ut(?:hChoiceMod|oSigninR)ules|pplicationList)|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)|oap(?:Session|Config)Server|t(?:ayConnecte|orePasswor)d|kipRenewConfirmation|fRemovedUseNotif|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)|GeneratePassword|PasswordPolicy)|ErrorOn(?:ExpiredSession|MailNotFound)|(?:CheckLogin|Statu)s|OpenLinkInNewWindow|ForceAuthn|AntiFrame)|roxyUseSoap)|l(?:dap(?:(?:Group(?:DecodeSearchedValu|Recursiv)|UsePasswordResetAttribut)e|(?:AllowResetExpired|Set)Password|ChangePasswordAsUser|PpolicyControl|ITDS)|oginHistoryEnabled)|c(?:a(?:ptcha_(?:register|login|mail)_enabled|sSrvMetaDataOptions(?:Gateway|Renew))|o(?:ntextSwitchingStopWithLogout|mpactConf|rsEnabled)|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)|br(?:uteForceProtection(?:IncrementalTempo)?|owsersDontStorePassword)|re(?:st(?:(?:Session|Config)Server|ExportSecretKeys)|freshSessions)|(?:mai(?:lOnPasswordChang|ntenanc)|vhostMaintenanc)e|d(?:isablePersistentStorage|biDynamicHashEnabled)|g(?:roupsBeforeMacros|lobalLogoutTimer)|h(?:ideOldPassword|ttpOnly)|yubikey2fUserCanRemoveKey|(?:activeTim|wsdlServ)er|krb(?:RemoveDomain|ByJs))$/;
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)|oap(?:Session|Config)Server|t(?:ayConnecte|orePasswor)d|kipRenewConfirmation|fRemovedUseNotif|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)|GeneratePassword|PasswordPolicy)|ErrorOn(?:ExpiredSession|MailNotFound)|(?:CheckLogin|Statu)s|OpenLinkInNewWindow|ForceAuthn|AntiFrame)|roxyUseSoap)|l(?:dap(?:(?:Group(?:DecodeSearchedValu|Recursiv)|UsePasswordResetAttribut)e|(?:AllowResetExpired|Set)Password|ChangePasswordAsUser|PpolicyControl|ITDS)|oginHistoryEnabled)|c(?:a(?:ptcha_(?:register|login|mail)_enabled|sSrvMetaDataOptions(?:Gateway|Renew))|o(?:ntextSwitchingStopWithLogout|mpactConf|rsEnabled)|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)|(?:mai(?:lOnPasswordChang|ntenanc)|vhostMaintenanc)e|d(?:isablePersistentStorage|biDynamicHashEnabled)|g(?:roupsBeforeMacros|lobalLogoutTimer)|h(?:ideOldPassword|ttpOnly)|yubikey2fUserCanRemoveKey|(?:activeTim|wsdlServ)er|krb(?:RemoveDomain|ByJs))$/;
our @sessionTypes = ( 'remoteGlobal', 'global', 'localSession', 'persistent', 'saml', 'oidc', 'cas' );

View File

@ -2863,6 +2863,10 @@ qr/(?:(?:https?):\/\/(?:(?:(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.]
'default' => 2,
'type' => 'int'
},
'restAuthServer' => {
'default' => 0,
'type' => 'bool'
},
'restAuthUrl' => {
'type' => 'url'
},
@ -2878,6 +2882,10 @@ qr/(?:(?:https?):\/\/(?:(?:(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.]
'default' => 0,
'type' => 'bool'
},
'restPasswordServer' => {
'default' => 0,
'type' => 'bool'
},
'restPwdConfirmUrl' => {
'type' => 'url'
},

View File

@ -1994,6 +1994,16 @@ sub attributes {
type => 'bool',
documentation => 'Enable REST session server',
},
restAuthServer => {
default => 0,
type => 'bool',
documentation => 'Enable REST authentication server',
},
restPasswordServer => {
default => 0,
type => 'bool',
documentation => 'Enable REST password reset server',
},
restExportSecretKeys => {
default => 0,
type => 'bool',
@ -3328,7 +3338,6 @@ m{^(?:ldapi://[^/]*/?|\w[\w\-\.]*(?::\d{1,5})?|ldap(?:s|\+tls)?://\w[\w\-\.]*(?:
restAuthUrl => { type => 'url' },
restUserDBUrl => { type => 'url' },
# TODO: add restMailDBUrl
restPwdConfirmUrl => { type => 'url' },
restPwdModifyUrl => { type => 'url' },
@ -3970,8 +3979,7 @@ m{^(?:ldapi://[^/]*/?|\w[\w\-\.]*(?::\d{1,5})?|ldap(?:s|\+tls)?://\w[\w\-\.]*(?:
oidcRPMetaDataOptionsIDTokenExpiration => { type => 'int' },
oidcRPMetaDataOptionsIDTokenForceClaims =>
{ type => 'bool', default => 0 },
oidcRPMetaDataOptionsAdditionalAudiences =>
{ type => 'text' },
oidcRPMetaDataOptionsAdditionalAudiences => { type => 'text' },
oidcRPMetaDataOptionsAccessTokenExpiration => { type => 'int' },
oidcRPMetaDataOptionsAuthorizationCodeExpiration => { type => 'int' },
oidcRPMetaDataOptionsOfflineSessionExpiration => { type => 'int' },

View File

@ -600,10 +600,11 @@ sub tree {
help => 'portalservers.html',
form => 'simpleInputContainer',
nodes => [
'wsdlServer', 'restSessionServer',
'restExportSecretKeys', 'restClockTolerance',
'restConfigServer', 'soapSessionServer',
'soapConfigServer', 'exportedAttr',
'wsdlServer', 'restExportSecretKeys',
'restClockTolerance', 'restSessionServer',
'restConfigServer', 'restAuthServer',
'restPasswordServer', 'soapSessionServer',
'soapConfigServer', 'exportedAttr',
]
},
{

View File

@ -806,7 +806,9 @@
"restPwdConfirmUrl":"عنوان اليو آر إل لتأكيد كلمة المرور",
"restPwdModifyUrl":"عنوان اليو آر إل لتغيير كلمة المرور",
"restSessionServer":"خادم جلسة ريست",
"restAuthServer":"REST authentication server",
"restClockTolerance":"REST server clock tolerance",
"restPasswordServer":"REST password reset server",
"restUserDBUrl":"عنوان يو آر إل لبيانات المستخدم",
"returnUrl":"إرجاع اليو آر إل",
"rp":"Relying Party",

View File

@ -806,7 +806,9 @@
"restPwdConfirmUrl":"Password confirmation URL",
"restPwdModifyUrl":"Password change URL",
"restSessionServer":"REST session server",
"restAuthServer":"REST authentication server",
"restClockTolerance":"REST server clock tolerance",
"restPasswordServer":"REST password reset server",
"restUserDBUrl":"User data URL",
"returnUrl":"Return URL",
"rp":"Relying Party",

View File

@ -806,7 +806,9 @@
"restPwdConfirmUrl":"Password confirmation URL",
"restPwdModifyUrl":"Password change URL",
"restSessionServer":"REST session server",
"restAuthServer":"REST authentication server",
"restClockTolerance":"REST server clock tolerance",
"restPasswordServer":"REST password reset server",
"restUserDBUrl":"User data URL",
"returnUrl":"Return URL",
"rp":"Relying Party",

View File

@ -806,7 +806,9 @@
"restPwdConfirmUrl":"URL de confirmation de mot-de-passe",
"restPwdModifyUrl":"URL de modification de mot-de-passe",
"restSessionServer":"Serveur de sessions REST",
"restAuthServer":"Serveur d'authentification REST",
"restClockTolerance":"Tolérance aux écarts d'horloge",
"restPasswordServer":"Serveur de réinitialisation de mdp REST",
"restUserDBUrl":"URL de données utilisateurs",
"returnUrl":"URL de retour",
"rp":"Client",

View File

@ -806,7 +806,9 @@
"restPwdConfirmUrl":"URL di conferma password",
"restPwdModifyUrl":"URL di modifica password",
"restSessionServer":"Server di sessione REST",
"restAuthServer":"REST authentication server",
"restClockTolerance":"REST server clock tolerance",
"restPasswordServer":"REST password reset server",
"restUserDBUrl":"URL dei dati utente",
"returnUrl":"URL di ritorno",
"rp":"Parte facente affidamento",

View File

@ -789,7 +789,9 @@
"requireToken":"Formlar için jeton gerekir",
"restAuthnLevel":"Doğrulama seviyesi",
"restAuthUrl":"Doğrulama URL'si",
"restAuthServer":"REST authentication server",
"restConfigServer":"REST konfigürasyon sunucusu",
"restPasswordServer":"REST password reset server",
"restore":"Geri yükle",
"restoreConf":"Yapılandırmayı geri yükle",
"rest2f":"REST ile ikinci faktör",
@ -1131,4 +1133,4 @@
"samlRelayStateTimeout":"RelayState oturum zaman aşımı",
"samlUseQueryStringSpecific":"Spesifik query_string metodu kullan",
"samlOverrideIDPEntityID":"IDP olarak davrandığında Varlık ID'yi geçersiz kıl"
}
}

View File

@ -806,7 +806,9 @@
"restPwdConfirmUrl":"URL xác nhận mật khẩu",
"restPwdModifyUrl":"URL thay đổi mật khẩu",
"restSessionServer":"Máy chủ phiên REST",
"restAuthServer":"REST authentication server",
"restClockTolerance":"REST server clock tolerance",
"restPasswordServer":"REST password reset server",
"restUserDBUrl":"URL dữ liệu người dùng",
"returnUrl":"Trả lại URL",
"rp":"Relying Party",

View File

@ -806,7 +806,9 @@
"restPwdConfirmUrl":"Password confirmation URL",
"restPwdModifyUrl":"Password change URL",
"restSessionServer":"REST session server",
"restAuthServer":"REST authentication server",
"restClockTolerance":"REST server clock tolerance",
"restPasswordServer":"REST password reset server",
"restUserDBUrl":"User data URL",
"returnUrl":"Return URL",
"rp":"Relying Party",

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -28,7 +28,7 @@ sub confirm {
sub modifyPassword {
my ( $self, $req, $pwd ) = @_;
my $dn = $req->userData->{_dn} || $req->sessionInfo->{_dn};
my $dn = $req->data->{dn} || $req->sessionInfo->{_dn};
unless ($dn) {
$self->logger->error('"dn" is not set, aborting password modification');
return PE_ERROR;

View File

@ -38,8 +38,8 @@ sub modifyPassword {
unless ($rule) {
my $error = $self->p->HANDLER->tsv->{jail}->error || '???';
}
if ( $req->userData->{_dn} ) {
$dn = $req->userData->{_dn};
if ( $req->data->{dn} ) {
$dn = $req->data->{dn};
$requireOldPassword = $rule->( $req, $req->userData );
$self->logger->debug("Get DN from request data: $dn");
}

View File

@ -2,6 +2,7 @@ package Lemonldap::NG::Portal::Password::REST;
use strict;
use Mouse;
use JSON;
use Lemonldap::NG::Portal::Main::Constants qw(
PE_ERROR
PE_PASSWORD_OK
@ -39,11 +40,15 @@ sub confirm {
}
sub modifyPassword {
my ( $self, $req, $pwd ) = @_;
my ( $self, $req, $pwd, $useMail ) = @_;
my $res = eval {
$self->restCall(
$self->conf->{restPwdModifyUrl},
{ user => $req->user, password => $pwd }
{
( $useMail ? 'mail' : 'user' ) => $req->user,
useMail => ( $useMail ? JSON::true : JSON::false ),
password => $pwd,
}
);
};
if ($@) {

View File

@ -35,6 +35,11 @@
# * GET /error/<lang>/<errNum> : get <errNum> message reference and errors file <lang>.json
# Return 'en' error file if no <lang> specified
#
# - Endpoints for proxy auth/userdb/password
# * POST /proxy/getUser : get user attributes (restAuthServer)
# * POST /proxy/pwdReset : reset password (restPasswordServer)
# * POST /proxy/pwdConfirm : check password (restAuthServer or restPasswordServer)
#
# - Authorizations for connected users (always):
# * GET /mysession/?whoami : get "my" uid
# * GET /mysession/?authorizationfor=<base64-encoded-url>: ask if url is
@ -53,6 +58,11 @@ use strict;
use Mouse;
use JSON qw(from_json to_json);
use MIME::Base64;
use Lemonldap::NG::Portal::Main::Constants qw(
portalConsts
PE_OK
PE_PASSWORD_OK
);
our $VERSION = '2.0.8';
@ -188,6 +198,33 @@ sub init {
);
}
if ( $self->conf->{restPasswordServer} ) {
$self->addUnauthRoute(
proxy => {
'pwdReset' => 'pwdReset',
},
['POST']
);
}
if ( $self->conf->{restAuthServer} or $self->conf->{restPasswordServer} ) {
$self->addUnauthRoute(
proxy => {
'pwdConfirm' => 'pwdConfirm',
},
['POST']
);
}
if ( $self->conf->{restAuthServer} ) {
$self->addUnauthRoute(
proxy => {
'getUser' => 'getUser',
},
['POST']
);
}
# Methods always available
$self->addAuthRoute(
mysession => { '*' => 'mysession' },
@ -594,4 +631,148 @@ sub sendCaptcha {
{ newtoken => $token, newimage => $image } );
}
sub pwdReset {
my ( $self, $req ) = @_;
$self->logger->debug("Entering REST pwdReset method");
unless ( $self->p->_passwordDB ) {
$self->logger->error(
"No Password module configured on this server, "
. "cannot execute password change" );
return $self->p->sendJSONresponse( $req, { 'result' => JSON::false } );
}
my $jsonBody = eval { from_json( $req->content ) };
if ($@) {
$self->logger->error("Received invalid JSON $@");
return $self->p->sendError( $req, "Invalid JSON", 400 );
}
my $user = $jsonBody->{user};
my $mail = $jsonBody->{mail};
my $password = $jsonBody->{password};
unless ( $user or $mail ) {
$self->logger->error("Missing user or mail argument");
return $self->p->sendError( $req, "Missing user or mail argument",
400 );
}
unless ($password) {
$self->logger->error("Missing password argument");
return $self->p->sendError( $req, "Missing password argument", 400 );
}
my $tmp = $self->conf->{portalRequireOldPassword};
$self->conf->{portalRequireOldPassword} = 0;
$req->user( $user || $mail );
$req->steps( ['getUser'] );
my $result = $self->p->process( $req, ( $mail ? ( useMail => 1 ) : () ) );
if ($result) {
$self->logger->error( "Error while looking up user: $result ("
. portalConsts->{$result}
. ")" );
return $self->p->sendError( $req, "User not found", 400 );
}
$result =
$self->p->_passwordDB->modifyPassword( $req, $password, $mail ? 1 : 0 );
$req->{user} = undef;
$self->conf->{portalRequireOldPassword} = $tmp;
if ( $result == PE_PASSWORD_OK or $result == PE_OK ) {
return $self->p->sendJSONresponse( $req, { 'result' => JSON::true } );
}
else {
$self->logger->error( "Error while changing user password: $result ("
. portalConsts->{$result}
. ")" );
return $self->p->sendJSONresponse( $req, { 'result' => JSON::false } );
}
}
sub pwdConfirm {
my ( $self, $req ) = @_;
$self->logger->debug("Entering REST pwdConfirm method");
my $jsonBody = eval { from_json( $req->content ) };
if ($@) {
$self->logger->error("Received invalid JSON $@");
return $self->p->sendError( $req, "Invalid JSON", 400 );
}
my $user = $jsonBody->{user};
my $password = $jsonBody->{password};
unless ( $user and $password ) {
$self->logger->error("Missing required arguments");
return $self->p->sendError( $req, "Missing arguments user or password",
400 );
}
$req->user($user);
$req->data->{password} = $password;
if ( $self->p->_userDB ) {
$req->steps( [ 'getUser', 'authenticate' ] );
my $result = $self->p->process($req);
if ( $result == PE_PASSWORD_OK or $result == PE_OK ) {
return $self->p->sendJSONresponse( $req,
{ 'result' => JSON::true } );
}
else {
$self->logger->error(
"Process returned $result (" . portalConsts->{$result} . ")" );
return $self->p->sendJSONresponse( $req,
{ 'result' => JSON::false } );
}
}
}
sub getUser {
my ( $self, $req ) = @_;
$self->logger->debug("Entering REST getUser method");
my $jsonBody = eval { from_json( $req->content ) };
if ($@) {
$self->logger->error("Received invalid JSON $@");
return $self->p->sendError( $req, "Invalid JSON", 400 );
}
my $user = $jsonBody->{user};
my $mail = $jsonBody->{mail};
unless ( $user or $mail ) {
$self->logger->error("Missing user or mail argument");
return $self->p->sendError( $req, "Missing user or mail argument",
400 );
}
$req->user( $user || $mail );
# Search user in database
$req->steps( [
'getUser', 'setSessionInfo',
'setMacros', 'setGroups',
'setLocalGroups'
]
);
my $error = $self->p->process( $req, ( $mail ? ( useMail => 1 ) : () ) );
if ( $error == PE_OK ) {
return $self->p->sendJSONresponse(
$req,
{
'result' => JSON::true,
'info' => $req->sessionInfo,
}
);
}
else {
return $self->p->sendJSONresponse( $req, { 'result' => JSON::false } );
}
}
1;

View File

@ -2,6 +2,7 @@ package Lemonldap::NG::Portal::UserDB::REST;
use strict;
use Mouse;
use JSON;
use Lemonldap::NG::Portal::Main::Constants qw(
PE_ERROR
PE_OK
@ -31,20 +32,23 @@ sub getUser {
my ( $self, $req, %args ) = @_;
my $res;
$res = eval {
$self->restCall( (
$args{useMail}
? $self->conf->{restMailDBUrl} || $self->conf->{restUserDBUrl}
: $self->conf->{restUserDBUrl}
),
{ user => $req->user }
$self->restCall(
$self->conf->{restUserDBUrl},
{
( $args{useMail} ? 'mail' : 'user' ) => $req->user,
'useMail' => ( $args{useMail} ? JSON::true : JSON::false ),
}
);
};
if ($@) {
$self->logger->error("UserDB REST error: $@");
eval { $self->p->_authentication->setSecurity($req) };
return PE_ERROR;
}
unless ( $res->{result} ) {
$self->userLogger->warn( 'User ' . $req->user . ' not found' );
eval { $self->p->_authentication->setSecurity($req) };
return PE_BADCREDENTIALS;
}
$req->data->{restUserDBInfo} = $res->{info} || {};

View File

@ -0,0 +1,158 @@
use lib 'inc';
use Test::More;
use strict;
use IO::String;
use LWP::UserAgent;
use LWP::Protocol::PSGI;
BEGIN {
require 't/test-lib.pm';
}
my $debug = 'error';
my ( $issuer, $sp, $res, $spId, $idpId );
my %handlerOR = ( issuer => [], sp => [] );
LWP::Protocol::PSGI->register(
sub {
my $req = Plack::Request->new(@_);
ok( $req->uri =~ m#http://auth.idp.com([^\?]*?)(?:\?(.*))?$#,
' @ REST request (' . $req->method . " $1)" );
count(1);
my $url = $1;
my $query = $2;
my $res;
my $s = $req->content;
if ( $req->method =~ /^(post|put)$/i ) {
my $mth = '_' . lc($1);
my $s = $req->content;
ok(
$res = $issuer->$mth(
$url,
IO::String->new($s),
( $query ? ( query => $query ) : () ),
length => length($s),
type => $req->header('Content-Type'),
),
' Post request'
);
count(1);
}
elsif ( $req->method =~ /^(get|delete)$/i ) {
my $mth = '_' . lc($1);
ok(
$res = $issuer->$mth(
$url,
( $query ? ( query => $query ) : () ),
accept => $req->header('Accept'),
cookie => $req->header('Cookie')
),
' Execute request'
);
count(1);
}
return $res;
}
);
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;
count(2);
# Simple SP access
ok(
$res = $sp->_get(
'/', accept => 'text/html',
),
'Unauth SP request'
);
expectOK($res);
# Try to auth
ok(
$res = $sp->_post(
'/', IO::String->new('user=dwho&password=dwho'),
length => 23,
accept => 'text/html'
),
'Post user/password'
);
count(2);
expectRedirection( $res, 'http://auth.sp.com' );
$spId = expectCookie($res);
# Logout
ok(
$res = $sp->_get(
'/',
query => 'logout',
accept => 'text/html',
cookie => "lemonldap=$spId"
),
'Ask for logout'
);
count(1);
expectOK($res);
# Test if user is reject on IdP
ok(
$res = $sp->_get(
'/', cookie => "lemonldap=$spId",
),
'Test if user is reject on IdP'
);
count(1);
expectReject($res);
clean_sessions();
done_testing( count() );
# Redefine LWP methods for tests
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',
secret => 'abc',
userDB => 'Same',
restSessionServer => 1,
restConfigServer => 1,
}
}
);
}
sub sp {
return LLNG::Manager::Test->new( {
ini => {
logLevel => $debug,
domain => 'sp.com',
portal => 'http://auth.sp.com',
authentication => 'Proxy',
userDB => 'Same',
secret => 'abc',
proxyAuthService => 'http://auth.idp.com',
proxyUseSoap => 0,
whatToTrace => '_whatToTrace',
globalStorage => 'Lemonldap::NG::Common::Apache::Session::REST',
globalStorageOptions => {
'baseUrl' => 'http://auth.idp.com/sessions/global',
}
},
}
);
}