Merge branch '2683' into 'v2.0'
Append rule to allow StayConnected plugin (#2683) See merge request lemonldap-ng/lemonldap-ng!243
This commit is contained in:
commit
04c29e5c71
|
@ -39,7 +39,7 @@ You can then create rules with these fields:
|
|||
|
||||
.. tip::
|
||||
|
||||
By example, to add 3 to authentication level for users from 192.168.0.0/24 network:
|
||||
By example, to add 3 to authentication level for users from 192.168.0.0/16 network:
|
||||
|
||||
- Rule: ``$env->{REMOTE_ADDR} =~ /^192\.168\./``
|
||||
- Value: ``+3``
|
||||
|
|
|
@ -11,7 +11,13 @@ Just enable it in the manager (section “plugins”).
|
|||
|
||||
- **Parameters**:
|
||||
|
||||
- **Activation**: Enable / Disable this plugin
|
||||
- **Do not check fingerprint**: Enable / Disable browser fingerprint checking
|
||||
- **Activation**: Rule to enable/disable this plugin
|
||||
- **Do not check fingerprint**: Enable/Disable browser fingerprint checking
|
||||
- **Expiration time**: Persistent session connection and cookie timeout
|
||||
- **Cookie name**: Persistent connection cookie name
|
||||
- **Cookie name**: Persistent connection cookie name
|
||||
|
||||
.. tip::
|
||||
|
||||
By example, you can allow users from 192.168.0.0/16 private network to register a fingerprinting:
|
||||
|
||||
- Rule: ``$env->{REMOTE_ADDR} =~ /^192\.168\./``
|
|
@ -31,7 +31,7 @@ use constant DEFAULTCONFBACKENDOPTIONS => (
|
|||
);
|
||||
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|ScopeRule|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 $arrayParameters = qr/^mySessionAuthorizedRWKeys$/;
|
||||
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)|t(?:ayConnected(?:BypassFG)?|orePassword)|f(?:RemovedUseNotif|OnlyUpgrade)|kip(?:Upgrade|Renew)Confirmation|oap(?:Session|Config)Server|laveDisplayLogo|howLanguages|slByAjax)|o(?:idc(?:RPMetaDataOptions(?:A(?:llow(?:(?:ClientCredentials|Password)Grant|Offline)|ccessToken(?:Claims|JWT))|Re(?:freshToken|quirePKCE)|LogoutSessionRequired|IDTokenForceClaims|BypassConsent|Public)|ServiceAllow(?:(?:AuthorizationCode|Implicit|Hybrid)Flow|DynamicRegistration|OnlyDeclaredScopes)|OPMetaDataOptions(?:(?:CheckJWTSignatur|UseNonc)e|StoreIDToken))|ldNotifFormat)|c(?:a(?:sS(?:rvMetaDataOptions(?:Gateway|Renew)|trictMatching)|ptcha_(?:register|login|mail)_enabled)|heck(?:DevOps(?:D(?:isplayNormalizedHeaders|ownload)|CheckSessionAttributes)?|State|User|XSS)|o(?:ntextSwitching(?:Allowed2fModifications|StopWithLogout)|mpactConf|rsEnabled)|rowdsec|da)|p(?:ortal(?:Display(?:Re(?:freshMyRights|setPassword|gister)|CertificateResetByMail|GeneratePassword|PasswordPolicy)|E(?:rrorOn(?:ExpiredSession|MailNotFound)|nablePasswordDisplay)|(?:CheckLogin|Statu)s|OpenLinkInNewWindow|ForceAuthn|AntiFrame)|roxy(?:AuthServiceImpersonation|UseSoap))|l(?:dap(?:(?:G(?:roup(?:DecodeSearchedValu|Recursiv)|etUserBeforePasswordChang)|UsePasswordResetAttribut)e|(?:AllowResetExpired|Set)Password|ChangePasswordAsUser|PpolicyControl|ITDS)|oginHistoryEnabled)|n(?:o(?:tif(?:ication(?:Server(?:(?:POS|GE)T|DELETE)?|sExplorer)?|y(?:Deleted|Other))|AjaxHook)|ewLocationWarning)|i(?:ssuerDB(?:OpenID(?:Connect)?|SAML|CAS|Get)Activation|mpersonationSkipEmptyValues)|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)|to(?:tp2f(?:UserCanRemoveKey|EncryptSecret)|kenUseGlobalStorage)|(?: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)|t(?:ayConnectedBypassFG|orePassword)|f(?:RemovedUseNotif|OnlyUpgrade)|kip(?:Upgrade|Renew)Confirmation|oap(?:Session|Config)Server|laveDisplayLogo|howLanguages|slByAjax)|o(?:idc(?:RPMetaDataOptions(?:A(?:llow(?:(?:ClientCredentials|Password)Grant|Offline)|ccessToken(?:Claims|JWT))|Re(?:freshToken|quirePKCE)|LogoutSessionRequired|IDTokenForceClaims|BypassConsent|Public)|ServiceAllow(?:(?:AuthorizationCode|Implicit|Hybrid)Flow|DynamicRegistration|OnlyDeclaredScopes)|OPMetaDataOptions(?:(?:CheckJWTSignatur|UseNonc)e|StoreIDToken))|ldNotifFormat)|c(?:a(?:sS(?:rvMetaDataOptions(?:Gateway|Renew)|trictMatching)|ptcha_(?:register|login|mail)_enabled)|heck(?:DevOps(?:D(?:isplayNormalizedHeaders|ownload)|CheckSessionAttributes)?|State|User|XSS)|o(?:ntextSwitching(?:Allowed2fModifications|StopWithLogout)|mpactConf|rsEnabled)|rowdsec|da)|p(?:ortal(?:Display(?:Re(?:freshMyRights|setPassword|gister)|CertificateResetByMail|GeneratePassword|PasswordPolicy)|E(?:rrorOn(?:ExpiredSession|MailNotFound)|nablePasswordDisplay)|(?:CheckLogin|Statu)s|OpenLinkInNewWindow|ForceAuthn|AntiFrame)|roxy(?:AuthServiceImpersonation|UseSoap))|l(?:dap(?:(?:G(?:roup(?:DecodeSearchedValu|Recursiv)|etUserBeforePasswordChang)|UsePasswordResetAttribut)e|(?:AllowResetExpired|Set)Password|ChangePasswordAsUser|PpolicyControl|ITDS)|oginHistoryEnabled)|n(?:o(?:tif(?:ication(?:Server(?:(?:POS|GE)T|DELETE)?|sExplorer)?|y(?:Deleted|Other))|AjaxHook)|ewLocationWarning)|i(?:ssuerDB(?:OpenID(?:Connect)?|SAML|CAS|Get)Activation|mpersonationSkipEmptyValues)|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)|to(?:tp2f(?:UserCanRemoveKey|EncryptSecret)|kenUseGlobalStorage)|(?: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' );
|
||||
|
||||
|
|
|
@ -361,6 +361,7 @@ sub defaultValues {
|
|||
'SSLAuthnLevel' => 5,
|
||||
'SSLVar' => 'SSL_CLIENT_S_DN_Email',
|
||||
'SSLVarIf' => {},
|
||||
'stayConnected' => 0,
|
||||
'stayConnectedCookieName' => 'llngconnection',
|
||||
'stayConnectedTimeout' => 2592000,
|
||||
'successLoginNumber' => 5,
|
||||
|
|
|
@ -4175,7 +4175,7 @@ qr/^(?:(?:(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.])*(?:[a-zA-Z][-a-
|
|||
},
|
||||
'stayConnected' => {
|
||||
'default' => 0,
|
||||
'type' => 'bool'
|
||||
'type' => 'boolOrExpr'
|
||||
},
|
||||
'stayConnectedBypassFG' => {
|
||||
'default' => 0,
|
||||
|
|
|
@ -436,9 +436,9 @@ sub attributes {
|
|||
flags => 'hmp',
|
||||
},
|
||||
stayConnected => {
|
||||
type => 'bool',
|
||||
type => 'boolOrExpr',
|
||||
default => 0,
|
||||
documentation => 'Enable StayConnected plugin',
|
||||
documentation => 'Stay connected activation rule',
|
||||
},
|
||||
stayConnectedBypassFG => {
|
||||
type => 'bool',
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -709,6 +709,7 @@ t/62-UpgradeSession.t
|
|||
t/63-History.t
|
||||
t/64-StayConnected-with-2F-and-History.t
|
||||
t/64-StayConnected-with-History.t
|
||||
t/64-StayConnected-with-rule.t
|
||||
t/64-StayConnected-without-fingerprint-checking.t
|
||||
t/65-AutoSignin.t
|
||||
t/65-CheckState.t
|
||||
|
|
|
@ -13,6 +13,7 @@ use URI;
|
|||
has isPP => ( is => 'rw' );
|
||||
has speChars => ( is => 'rw' );
|
||||
has skinRules => ( is => 'rw' );
|
||||
has stayConnected => ( is => 'rw', default => sub { 0 } );
|
||||
has requireOldPwd => ( is => 'rw', default => sub { 1 } );
|
||||
|
||||
sub displayInit {
|
||||
|
@ -28,17 +29,24 @@ sub displayInit {
|
|||
else {
|
||||
$self->logger->error(
|
||||
qq(Skin rule "$skinRule" returns an error: )
|
||||
. HANDLER->tsv->{jail}->error );
|
||||
. HANDLER->tsv->{jail}->error || 'Unable to compile rule' );
|
||||
}
|
||||
}
|
||||
}
|
||||
my $rule = HANDLER->buildSub(
|
||||
HANDLER->substitute( $self->conf->{portalRequireOldPassword} ) );
|
||||
unless ($rule) {
|
||||
my $error = HANDLER->tsv->{jail}->error || '???';
|
||||
$self->logger->error( "Bad requireOldPwd rule: " . $error );
|
||||
my $error = HANDLER->tsv->{jail}->error || 'Unable to compile rule';
|
||||
$self->logger->error( "Bad requireOldPwd rule: $error" );
|
||||
}
|
||||
$self->requireOldPwd($rule);
|
||||
$rule =
|
||||
HANDLER->buildSub( HANDLER->substitute( $self->conf->{stayConnected} ) );
|
||||
unless ($rule) {
|
||||
my $error = HANDLER->tsv->{jail}->error || 'Unable to compile rule';
|
||||
$self->logger->error( "Bad stayConnected rule: $error" );
|
||||
}
|
||||
$self->stayConnected($rule);
|
||||
|
||||
my $speChars =
|
||||
$self->conf->{passwordPolicySpecialChar} eq '__ALL__'
|
||||
|
@ -247,8 +255,6 @@ sub display {
|
|||
LOGOUT_URL => $self->conf->{portal} . "?logout=1",
|
||||
APPSLIST_ORDER => $req->{sessionInfo}->{'_appsListOrder'},
|
||||
PING => $self->conf->{portalPingInterval},
|
||||
REQUIRE_OLDPASSWORD =>
|
||||
$self->requireOldPwd->( $req, $req->userData ),
|
||||
DONT_STORE_PASSWORD => $self->conf->{browsersDontStorePassword},
|
||||
HIDE_OLDPASSWORD => 0,
|
||||
PPOLICY_NOPOLICY => !$self->isPP(),
|
||||
|
@ -258,6 +264,11 @@ sub display {
|
|||
PPOLICY_MINUPPER => $self->conf->{passwordPolicyMinUpper},
|
||||
PPOLICY_MINDIGIT => $self->conf->{passwordPolicyMinDigit},
|
||||
PPOLICY_MINSPECHAR => $self->conf->{passwordPolicyMinSpeChar},
|
||||
(
|
||||
$self->requireOldPwd->( $req, $req->userData )
|
||||
? ( REQUIRE_OLDPASSWORD => 1 )
|
||||
: ()
|
||||
),
|
||||
(
|
||||
$self->conf->{passwordPolicyMinSpeChar} || $self->speChars()
|
||||
? ( PPOLICY_ALLOWEDSPECHAR => $self->speChars() )
|
||||
|
@ -391,16 +402,20 @@ sub display {
|
|||
MAIL_URL => $self->conf->{mailUrl},
|
||||
REGISTER_URL => $self->conf->{registerUrl},
|
||||
HIDDEN_INPUTS => $self->buildHiddenForm($req),
|
||||
STAYCONNECTED => $self->conf->{stayConnected},
|
||||
IMPERSONATION => $self->conf->{impersonationRule}
|
||||
|| $self->conf->{proxyAuthServiceImpersonation},
|
||||
ENABLE_PASSWORD_DISPLAY =>
|
||||
$self->conf->{portalEnablePasswordDisplay},
|
||||
(
|
||||
$self->stayConnected->( $req, $req->sessionInfo )
|
||||
? ( STAYCONNECTED => 1 )
|
||||
: ()
|
||||
),
|
||||
(
|
||||
$req->data->{customScript}
|
||||
? ( CUSTOM_SCRIPT => $req->data->{customScript} )
|
||||
: ()
|
||||
),
|
||||
ENABLE_PASSWORD_DISPLAY =>
|
||||
$self->conf->{portalEnablePasswordDisplay},
|
||||
);
|
||||
|
||||
# External links
|
||||
|
|
|
@ -20,8 +20,8 @@ use constant beforeAuth => 'check';
|
|||
use constant beforeLogout => 'logout';
|
||||
|
||||
# INITIALIZATION
|
||||
|
||||
has ott => (
|
||||
has rule => ( is => 'rw', default => sub { 0 } );
|
||||
has ott => (
|
||||
is => 'rw',
|
||||
lazy => 1,
|
||||
default => sub {
|
||||
|
@ -52,6 +52,11 @@ sub init {
|
|||
my ($self) = @_;
|
||||
$self->addAuthRoute( registerbrowser => 'storeBrowser', ['POST'] );
|
||||
|
||||
# Parse activation rule
|
||||
$self->rule(
|
||||
$self->p->buildRule( $self->conf->{stayConnected}, 'stayConnected' ) );
|
||||
return 0 unless $self->rule;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -64,7 +69,9 @@ sub newDevice {
|
|||
my $checkLogins = $req->param('checkLogins');
|
||||
$self->logger->debug("StayConnected: checkLogins set") if $checkLogins;
|
||||
|
||||
if ( $req->param('stayconnected') ) {
|
||||
if ( $req->param('stayconnected')
|
||||
&& $self->rule->( $req, $req->sessionInfo ) )
|
||||
{
|
||||
my $token = $self->ott->createToken( {
|
||||
name => $req->sessionInfo->{ $self->conf->{whatToTrace} },
|
||||
(
|
||||
|
@ -96,53 +103,59 @@ sub storeBrowser {
|
|||
my ( $self, $req ) = @_;
|
||||
$req->urldc( $req->param('url') );
|
||||
$req->mustRedirect(1);
|
||||
if ( my $token = $req->param('token') ) {
|
||||
if ( my $tmp = $self->ott->getToken($token) ) {
|
||||
my $uid = $req->userData->{ $self->conf->{whatToTrace} };
|
||||
if ( $tmp->{name} eq $uid ) {
|
||||
if ( my $fg = $req->param('fg') ) {
|
||||
my $ps = Lemonldap::NG::Common::Session->new(
|
||||
storageModule => $self->conf->{globalStorage},
|
||||
storageModuleOptions =>
|
||||
$self->conf->{globalStorageOptions},
|
||||
kind => "SSO",
|
||||
info => {
|
||||
_utime => time + $self->timeout,
|
||||
_session_uid => $uid,
|
||||
_connectedSince => time,
|
||||
dataKeep => $req->data->{dataToKeep},
|
||||
fingerprint => $fg,
|
||||
},
|
||||
);
|
||||
if ( $self->rule->( $req, $req->sessionInfo ) ) {
|
||||
if ( my $token = $req->param('token') ) {
|
||||
if ( my $tmp = $self->ott->getToken($token) ) {
|
||||
my $uid = $req->userData->{ $self->conf->{whatToTrace} };
|
||||
if ( $tmp->{name} eq $uid ) {
|
||||
if ( my $fg = $req->param('fg') ) {
|
||||
my $ps = Lemonldap::NG::Common::Session->new(
|
||||
storageModule => $self->conf->{globalStorage},
|
||||
storageModuleOptions =>
|
||||
$self->conf->{globalStorageOptions},
|
||||
kind => "SSO",
|
||||
info => {
|
||||
_utime => time + $self->timeout,
|
||||
_session_uid => $uid,
|
||||
_connectedSince => time,
|
||||
dataKeep => $req->data->{dataToKeep},
|
||||
fingerprint => $fg,
|
||||
},
|
||||
);
|
||||
|
||||
# Cookie available 30 days by default
|
||||
$req->addCookie(
|
||||
$self->p->cookie(
|
||||
name => $self->cookieName,
|
||||
value => $ps->id,
|
||||
max_age => $self->timeout,
|
||||
secure => $self->conf->{securedCookie},
|
||||
)
|
||||
);
|
||||
$req->sessionInfo->{_loginHistory} = $tmp->{history}
|
||||
if exists $tmp->{history};
|
||||
# Cookie available 30 days by default
|
||||
$req->addCookie(
|
||||
$self->p->cookie(
|
||||
name => $self->cookieName,
|
||||
value => $ps->id,
|
||||
max_age => $self->timeout,
|
||||
secure => $self->conf->{securedCookie},
|
||||
)
|
||||
);
|
||||
$req->sessionInfo->{_loginHistory} = $tmp->{history}
|
||||
if exists $tmp->{history};
|
||||
}
|
||||
else {
|
||||
$self->logger->warn(
|
||||
"Browser did not return fingerprint");
|
||||
}
|
||||
}
|
||||
else {
|
||||
$self->logger->warn("Browser did not return fingerprint");
|
||||
$self->userLogger->error(
|
||||
"StayConnected: mismatch UID ($tmp->{name} / $uid)");
|
||||
}
|
||||
}
|
||||
else {
|
||||
$self->userLogger->error(
|
||||
"StayConnected: mismatch UID ($tmp->{name} / $uid)");
|
||||
"StayConnected called with an expired token");
|
||||
}
|
||||
}
|
||||
else {
|
||||
$self->userLogger->error(
|
||||
"StayConnected called with an expired token");
|
||||
$self->userLogger->error('StayConnected called without token');
|
||||
}
|
||||
}
|
||||
else {
|
||||
$self->userLogger->error('StayConnected called without token');
|
||||
$self->userLogger->error('StayConnected not allowed');
|
||||
}
|
||||
|
||||
# Return persistent connection cookie
|
||||
|
@ -156,39 +169,26 @@ sub storeBrowser {
|
|||
# Then delete authentication methods from "steps" array.
|
||||
sub check {
|
||||
my ( $self, $req ) = @_;
|
||||
if ( my $cid = $req->cookies->{ $self->cookieName } ) {
|
||||
my $ps = Lemonldap::NG::Common::Session->new(
|
||||
storageModule => $self->conf->{globalStorage},
|
||||
storageModuleOptions => $self->conf->{globalStorageOptions},
|
||||
kind => "SSO",
|
||||
id => $cid,
|
||||
);
|
||||
if ( $ps
|
||||
and my $uid = $ps->data->{_session_uid}
|
||||
and time() < $ps->data->{_utime} )
|
||||
{
|
||||
$self->logger->debug('Persistent connection found');
|
||||
if ( my $fg = $req->param('fg')
|
||||
and my $token = $req->param('token') )
|
||||
if ( $self->rule->( $req, $req->sessionInfo ) ) {
|
||||
if ( my $cid = $req->cookies->{ $self->cookieName } ) {
|
||||
my $ps = Lemonldap::NG::Common::Session->new(
|
||||
storageModule => $self->conf->{globalStorage},
|
||||
storageModuleOptions => $self->conf->{globalStorageOptions},
|
||||
kind => "SSO",
|
||||
id => $cid,
|
||||
);
|
||||
if ( $ps
|
||||
and my $uid = $ps->data->{_session_uid}
|
||||
and time() < $ps->data->{_utime} )
|
||||
{
|
||||
if ( my $prm = $self->ott->getToken($token) ) {
|
||||
$req->data->{dataKeep} = $ps->data->{dataKeep};
|
||||
$self->logger->debug('Persistent connection found');
|
||||
if ( $self->conf->{stayConnectedBypassFG} ) {
|
||||
$req->user($uid);
|
||||
my @steps =
|
||||
grep {
|
||||
!ref $_
|
||||
and $_ !~ /^(?:extractFormInfo|authenticate)$/
|
||||
} @{ $req->steps };
|
||||
$req->steps( \@steps );
|
||||
$self->userLogger->notice(
|
||||
"$uid connected by StayConnected cookie without fingerprint checking"
|
||||
);
|
||||
return PE_OK;
|
||||
}
|
||||
else {
|
||||
if ( $fg eq $ps->data->{fingerprint} ) {
|
||||
$self->logger->debug('Persistent connection found');
|
||||
if ( my $fg = $req->param('fg')
|
||||
and my $token = $req->param('token') )
|
||||
{
|
||||
if ( my $prm = $self->ott->getToken($token) ) {
|
||||
$req->data->{dataKeep} = $ps->data->{dataKeep};
|
||||
$self->logger->debug('Persistent connection found');
|
||||
if ( $self->conf->{stayConnectedBypassFG} ) {
|
||||
$req->user($uid);
|
||||
my @steps =
|
||||
grep {
|
||||
|
@ -197,48 +197,68 @@ sub check {
|
|||
} @{ $req->steps };
|
||||
$req->steps( \@steps );
|
||||
$self->userLogger->notice(
|
||||
"$uid connected by StayConnected cookie");
|
||||
"$uid connected by StayConnected cookie without fingerprint checking"
|
||||
);
|
||||
return PE_OK;
|
||||
}
|
||||
else {
|
||||
$self->userLogger->warn(
|
||||
"Fingerprint changed for $uid");
|
||||
$ps->remove;
|
||||
$self->logout($req);
|
||||
if ( $fg eq $ps->data->{fingerprint} ) {
|
||||
$req->user($uid);
|
||||
my @steps =
|
||||
grep {
|
||||
!ref $_
|
||||
and $_ !~
|
||||
/^(?:extractFormInfo|authenticate)$/
|
||||
} @{ $req->steps };
|
||||
$req->steps( \@steps );
|
||||
$self->userLogger->notice(
|
||||
"$uid connected by StayConnected cookie");
|
||||
return PE_OK;
|
||||
}
|
||||
else {
|
||||
$self->userLogger->warn(
|
||||
"Fingerprint changed for $uid");
|
||||
$ps->remove;
|
||||
$self->logout($req);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
$self->userLogger->notice(
|
||||
"StayConnected: expired token for $uid");
|
||||
}
|
||||
}
|
||||
else {
|
||||
$self->userLogger->notice(
|
||||
"StayConnected: expired token for $uid");
|
||||
my $token = $self->ott->createToken( $req->parameters );
|
||||
$req->response(
|
||||
$self->p->sendHtml(
|
||||
$req,
|
||||
'../common/registerBrowser',
|
||||
params => {
|
||||
TOKEN => $token,
|
||||
ACTION => '#',
|
||||
}
|
||||
)
|
||||
);
|
||||
return PE_SENDRESPONSE;
|
||||
}
|
||||
}
|
||||
else {
|
||||
my $token = $self->ott->createToken( $req->parameters );
|
||||
$req->response(
|
||||
$self->p->sendHtml(
|
||||
$req,
|
||||
'../common/registerBrowser',
|
||||
params => {
|
||||
TOKEN => $token,
|
||||
ACTION => '#',
|
||||
}
|
||||
)
|
||||
);
|
||||
return PE_SENDRESPONSE;
|
||||
}
|
||||
}
|
||||
else {
|
||||
$self->userLogger->notice('Persistent connection expired');
|
||||
unless ( $ps->{error} ) {
|
||||
$self->logger->debug(
|
||||
'Persistent connection session id = ' . $ps->{id} );
|
||||
$self->logger->debug( 'Persistent connection session _utime = '
|
||||
. $ps->data->{_utime} );
|
||||
$ps->remove;
|
||||
$self->userLogger->notice('Persistent connection expired');
|
||||
unless ( $ps->{error} ) {
|
||||
$self->logger->debug(
|
||||
'Persistent connection session id = ' . $ps->{id} );
|
||||
$self->logger->debug(
|
||||
'Persistent connection session _utime = '
|
||||
. $ps->data->{_utime} );
|
||||
$ps->remove;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
$self->userLogger->error('StayConnected not allowed');
|
||||
}
|
||||
return PE_OK;
|
||||
}
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ my $client = LLNG::Manager::Test->new( {
|
|||
ini => {
|
||||
logLevel => 'error',
|
||||
useSafeJail => 1,
|
||||
stayConnected => 1,
|
||||
stayConnected => '$env->{REMOTE_ADDR} eq "127.0.0.1"',
|
||||
loginHistoryEnabled => 1,
|
||||
securedCookie => 1,
|
||||
stayConnectedTimeout => 1000,
|
||||
|
|
|
@ -0,0 +1,64 @@
|
|||
use Test::More;
|
||||
use strict;
|
||||
use IO::String;
|
||||
|
||||
require 't/test-lib.pm';
|
||||
|
||||
my $res;
|
||||
my $client = LLNG::Manager::Test->new( {
|
||||
ini => {
|
||||
logLevel => 'error',
|
||||
useSafeJail => 1,
|
||||
requireToken => 1,
|
||||
stayConnected => '$env->{REMOTE_ADDR} =~ /^127\.0\.0/'
|
||||
}
|
||||
}
|
||||
);
|
||||
ok( $res = $client->_get( '/', accept => 'text/html' ), 'Firt access' );
|
||||
my ( $host, $url, $query ) =
|
||||
expectForm( $res, undef, undef, 'user', 'password', 'stayconnected',
|
||||
'checkLogins', 'token' );
|
||||
ok( $res = $client->_get( '/', ip => '10.10.10.10', accept => 'text/html' ),
|
||||
'Access from external LAN' );
|
||||
( $host, $url, $query ) =
|
||||
expectForm( $res, undef, undef, 'user', 'password', 'checkLogins', 'token' );
|
||||
count(2);
|
||||
|
||||
$query =~ s/user=/user=dwho/;
|
||||
$query =~ s/password=/password=dwho/;
|
||||
|
||||
# Try to authenticate
|
||||
# -------------------
|
||||
ok(
|
||||
$res = $client->_post(
|
||||
'/',
|
||||
IO::String->new($query),
|
||||
ip => '10.10.10.10',
|
||||
accept => 'text/html',
|
||||
length => length($query)
|
||||
),
|
||||
'Auth query'
|
||||
);
|
||||
count(1);
|
||||
my $id = expectCookie($res);
|
||||
expectRedirection( $res, 'http://auth.example.com/' );
|
||||
|
||||
# Try to push fingerprint
|
||||
$query =~ s/fg=/fg=aaa/;
|
||||
ok(
|
||||
$res = $client->_post(
|
||||
'/registerbrowser',
|
||||
IO::String->new($query),
|
||||
length => length($query),
|
||||
cookie => "lemonldap=$id",
|
||||
accept => 'text/html',
|
||||
),
|
||||
'Post fingerprint'
|
||||
);
|
||||
count(1);
|
||||
expectRedirection( $res, 'http://auth.example.com/' );
|
||||
|
||||
$client->logout($id);
|
||||
clean_sessions();
|
||||
done_testing( count() );
|
||||
|
Loading…
Reference in New Issue