Merge branch 'feature-userdb-password-restserver' into v2.0
This commit is contained in:
commit
db9e862843
|
@ -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
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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' );
|
||||
|
||||
|
|
|
@ -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'
|
||||
},
|
||||
|
|
|
@ -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' },
|
||||
|
|
|
@ -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',
|
||||
]
|
||||
},
|
||||
{
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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
|
@ -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;
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
|
|
@ -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 ($@) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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} || {};
|
||||
|
|
|
@ -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',
|
||||
}
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
Loading…
Reference in New Issue