Commit 2efb4455 authored by Christophe Maudoux's avatar Christophe Maudoux 🐛

Merge branch 'v2.0'

parents 9231711a c01c26af
Pipeline #7009 canceled with stage
......@@ -129,7 +129,7 @@
.\" ========================================================================
.\"
.IX Title "llng-fastcgi-server 8"
.TH llng-fastcgi-server 8 "2019-09-24" "perl v5.28.1" "User Contributed Perl Documentation"
.TH llng-fastcgi-server 8 "2019-10-30" "perl v5.26.1" "User Contributed Perl Documentation"
.\" For nroff, turn off justification. Always turn off hyphenation; it makes
.\" way too many mistakes in technical documents.
.if n .ad l
......
......@@ -305,9 +305,10 @@ languages = en, fr, vi, it, ar, de, fi
; Read Lemonldap::NG::Portal::Main::Plugin(3pm) man page.
;customPlugins = My::Package1, My::Package2
; To avoid bad/expired OTT if authssl and auth are served by different Load Balancers
; you can override OTT configuration to store Upgrade OTT into global storage
; To avoid bad/expired OTT if "authssl" and "auth" are served by different Load Balancers
; you can override OTT configuration to store Upgrade or Issuer OTT into global storage
;forceGlobalStorageUpgradeOTT = 1
;forceGlobalStorageIssuerOTT = 1
[handler]
......
......@@ -260,8 +260,7 @@ sub defaultValues {
'samlAuthnContextMapPassword' => 2,
'samlAuthnContextMapPasswordProtectedTransport' => 3,
'samlAuthnContextMapTLSClient' => 5,
'samlEntityID' => '#PORTAL#/saml/metadata',
'samlIdPResolveCookie' => 'lemonldapidp',
'samlEntityID' => '#PORTAL#/saml/metadata',
'samlIDPSSODescriptorArtifactResolutionServiceArtifact' =>
'1;0;urn:oasis:names:tc:SAML:2.0:bindings:SOAP;#PORTAL#/saml/artifact',
'samlIDPSSODescriptorSingleLogoutServiceHTTPPost' =>
......
......@@ -195,9 +195,11 @@ sub virtualHosts {
type => 'keyText',
};
# If rule contains a comment, split it
# If rule contains a comment or an AuthLevel, split them
if ( $query eq 'locationRules' ) {
$res->{comment} = '';
$res->{level} = '';
$res->{level} = $1 if ( $r =~ s/\(\?#AuthnLevel=(-?\d+)\)// );
if ( $r =~ s/\(\?#(.*?)\)// ) {
$res->{title} = $res->{comment} = $1;
}
......
......@@ -67,7 +67,7 @@ our $issuerParameters = {
issuerDBSAML => [qw(issuerDBSAMLActivation issuerDBSAMLPath issuerDBSAMLRule)],
issuerOptions => [qw(issuersTimeout)],
};
our $samlServiceParameters = [qw(samlEntityID samlServicePrivateKeySig samlServicePrivateKeySigPwd samlServicePublicKeySig samlServicePrivateKeyEnc samlServicePrivateKeyEncPwd samlServicePublicKeyEnc samlServiceUseCertificateInResponse samlServiceSignatureMethod samlNameIDFormatMapEmail samlNameIDFormatMapX509 samlNameIDFormatMapWindows samlNameIDFormatMapKerberos samlAuthnContextMapPassword samlAuthnContextMapPasswordProtectedTransport samlAuthnContextMapTLSClient samlAuthnContextMapKerberos samlOrganizationDisplayName samlOrganizationName samlOrganizationURL samlSPSSODescriptorAuthnRequestsSigned samlSPSSODescriptorWantAssertionsSigned samlSPSSODescriptorSingleLogoutServiceHTTPRedirect samlSPSSODescriptorSingleLogoutServiceHTTPPost samlSPSSODescriptorSingleLogoutServiceSOAP samlSPSSODescriptorAssertionConsumerServiceHTTPArtifact samlSPSSODescriptorAssertionConsumerServiceHTTPPost samlSPSSODescriptorArtifactResolutionServiceArtifact samlIDPSSODescriptorWantAuthnRequestsSigned samlIDPSSODescriptorSingleSignOnServiceHTTPRedirect samlIDPSSODescriptorSingleSignOnServiceHTTPPost samlIDPSSODescriptorSingleSignOnServiceHTTPArtifact samlIDPSSODescriptorSingleLogoutServiceHTTPRedirect samlIDPSSODescriptorSingleLogoutServiceHTTPPost samlIDPSSODescriptorSingleLogoutServiceSOAP samlIDPSSODescriptorArtifactResolutionServiceArtifact samlAttributeAuthorityDescriptorAttributeServiceSOAP samlIdPResolveCookie samlMetadataForceUTF8 samlStorage samlStorageOptions samlRelayStateTimeout samlUseQueryStringSpecific samlCommonDomainCookieActivation samlCommonDomainCookieDomain samlCommonDomainCookieReader samlCommonDomainCookieWriter samlDiscoveryProtocolActivation samlDiscoveryProtocolURL samlDiscoveryProtocolPolicy samlDiscoveryProtocolIsPassive samlOverrideIDPEntityID)];
our $samlServiceParameters = [qw(samlEntityID samlServicePrivateKeySig samlServicePrivateKeySigPwd samlServicePublicKeySig samlServicePrivateKeyEnc samlServicePrivateKeyEncPwd samlServicePublicKeyEnc samlServiceUseCertificateInResponse samlServiceSignatureMethod samlNameIDFormatMapEmail samlNameIDFormatMapX509 samlNameIDFormatMapWindows samlNameIDFormatMapKerberos samlAuthnContextMapPassword samlAuthnContextMapPasswordProtectedTransport samlAuthnContextMapTLSClient samlAuthnContextMapKerberos samlOrganizationDisplayName samlOrganizationName samlOrganizationURL samlSPSSODescriptorAuthnRequestsSigned samlSPSSODescriptorWantAssertionsSigned samlSPSSODescriptorSingleLogoutServiceHTTPRedirect samlSPSSODescriptorSingleLogoutServiceHTTPPost samlSPSSODescriptorSingleLogoutServiceSOAP samlSPSSODescriptorAssertionConsumerServiceHTTPArtifact samlSPSSODescriptorAssertionConsumerServiceHTTPPost samlSPSSODescriptorArtifactResolutionServiceArtifact samlIDPSSODescriptorWantAuthnRequestsSigned samlIDPSSODescriptorSingleSignOnServiceHTTPRedirect samlIDPSSODescriptorSingleSignOnServiceHTTPPost samlIDPSSODescriptorSingleSignOnServiceHTTPArtifact samlIDPSSODescriptorSingleLogoutServiceHTTPRedirect samlIDPSSODescriptorSingleLogoutServiceHTTPPost samlIDPSSODescriptorSingleLogoutServiceSOAP samlIDPSSODescriptorArtifactResolutionServiceArtifact samlAttributeAuthorityDescriptorAttributeServiceSOAP samlMetadataForceUTF8 samlStorage samlStorageOptions samlRelayStateTimeout samlUseQueryStringSpecific samlCommonDomainCookieActivation samlCommonDomainCookieDomain samlCommonDomainCookieReader samlCommonDomainCookieWriter samlDiscoveryProtocolActivation samlDiscoveryProtocolURL samlDiscoveryProtocolPolicy samlDiscoveryProtocolIsPassive samlOverrideIDPEntityID)];
our $oidcServiceParameters = [qw(oidcServiceMetaDataAuthorizeURI oidcServiceMetaDataTokenURI oidcServiceMetaDataUserInfoURI oidcServiceMetaDataJWKSURI oidcServiceMetaDataRegistrationURI oidcServiceMetaDataIntrospectionURI oidcServiceMetaDataEndSessionURI oidcServiceMetaDataCheckSessionURI oidcServiceMetaDataFrontChannelURI oidcServiceMetaDataBackChannelURI oidcServiceMetaDataAuthnContext oidcServicePrivateKeySig oidcServicePublicKeySig oidcServiceKeyIdSig oidcServiceAllowDynamicRegistration oidcServiceAllowAuthorizationCodeFlow oidcServiceAllowImplicitFlow oidcServiceAllowHybridFlow oidcStorage oidcStorageOptions)];
1;
......@@ -43,11 +43,11 @@ sub run {
# Catch Secure Token parameters
my $localConfig = $class->localConfig;
my $secureTokenMemcachedServers =
our $secureTokenMemcachedServers =
$localConfig->{secureTokenMemcachedServers} || ['127.0.0.1:11211'];
my $secureTokenExpiration = $localConfig->{secureTokenExpiration} || 60;
my $secureTokenAttribute = $localConfig->{secureTokenAttribute} || 'uid';
my $secureTokenUrls = $localConfig->{'secureTokenUrls'} || ['.*'];
our $secureTokenUrls = $localConfig->{'secureTokenUrls'} || ['.*'];
my $secureTokenHeader = $localConfig->{secureTokenHeader} || 'Auth-Token';
my $secureTokenAllowOnError = $localConfig->{'secureTokenAllowOnError'}
// 1;
......
......@@ -281,6 +281,7 @@ sub locationRulesInit {
$class->tsv->{locationProtection}->{$vhost} = [];
$class->tsv->{locationRegexp}->{$vhost} = [];
$class->tsv->{locationConditionText}->{$vhost} = [];
$class->tsv->{locationAuthnLevel}->{$vhost} = [];
foreach my $url ( sort keys %{$rules} ) {
my ( $cond, $prot ) = $class->conditionSub( $rules->{$url} );
......@@ -300,10 +301,14 @@ sub locationRulesInit {
push @{ $class->tsv->{locationCondition}->{$vhost} }, $cond;
push @{ $class->tsv->{locationProtection}->{$vhost} }, $prot;
push @{ $class->tsv->{locationRegexp}->{$vhost} }, qr/$url/;
push @{ $class->tsv->{locationAuthnLevel}->{$vhost} },
$url =~ /\(\?#AuthnLevel=(-?\d+)\)/
? $1
: undef;
push @{ $class->tsv->{locationConditionText}->{$vhost} },
$url =~ /^\(\?#(.*?)\)/ ? $1
: $url =~ /^(.*?)##(.+)$/ ? $2
: $url;
: $url;
$class->tsv->{locationCount}->{$vhost}++;
}
}
......@@ -451,6 +456,7 @@ sub postUrlInit {
# @return array (ref(sub), int)
sub conditionSub {
my ( $class, $cond ) = @_;
$cond =~ s/\(\?#(\d+)\)$//;
my ( $OK, $NOK ) = ( sub { 1 }, sub { 0 } );
# Simple cases : accept and deny
......
......@@ -267,10 +267,31 @@ sub checkMaintenanceMode {
# @return True if the user is granted to access to the current URL
sub grant {
my ( $class, $req, $session, $uri, $cond, $vhost ) = @_;
my $level;
return $cond->( $req, $session ) if ($cond);
$vhost ||= $class->resolveAlias($req);
if ( my $level = $class->tsv->{authnLevel}->{$vhost} ) {
# Using URL authentification level if exists
for (
my $i = 0 ;
$i < ( $class->tsv->{locationCount}->{$vhost} || 0 ) ;
$i++
)
{
if ( $uri =~ $class->tsv->{locationRegexp}->{$vhost}->[$i] ) {
$level = $class->tsv->{locationAuthnLevel}->{$vhost}->[$i];
last;
}
}
$level
? $class->logger->debug(
'Found AuthnLevel=' . $level . ' for "' . "$vhost$uri" . '"' )
: $class->logger->debug("No URL authentication level found...");
# Using VH authentification level if exists
if ( $level ||= $class->tsv->{authnLevel}->{$vhost} ) {
if ( $session->{authenticationLevel} < $level ) {
$class->logger->debug(
"User authentication level = $session->{authenticationLevel}");
......
......@@ -10,6 +10,7 @@ init('Lemonldap::NG::Handler::PSGI');
my $res;
# Unauthentified query
# --------------------
ok( $res = $client->_get('/'), 'Unauthentified query' );
ok( ref($res) eq 'ARRAY', 'Response is an array' ) or explain( $res, 'array' );
ok( $res->[0] == 302, 'Code is 302' ) or explain( $res->[0], 302 );
......@@ -24,17 +25,14 @@ ok(
'Location => http://auth.example.com/?url='
. encode_base64( 'http://test1.example.com/', '' )
);
count(4);
# Authentified queries
# --------------------
# Authorized query
ok( $res = $client->_get( '/', undef, undef, "lemonldap=$sessionId" ),
'Authentified query' );
ok( $res->[0] == 200, 'Code is 200' ) or explain( $res, 200 );
count(2);
ok( $res = $client->_get( '/user_dwho/', undef, undef, "lemonldap=$sessionId" ),
......@@ -47,7 +45,12 @@ count(2);
ok( $res = $client->_get( '/deny', undef, undef, "lemonldap=$sessionId" ),
'Denied query' );
ok( $res->[0] == 403, 'Code is 403' ) or explain( $res->[0], 403 );
count(2);
# Required AuthnLevel = 1
ok( $res = $client->_get( '/AuthWeak', undef, undef, "lemonldap=$sessionId" ),
'Weak Authentified query' );
ok( $res->[0] == 200, 'Code is 200' ) or explain( $res, 200 );
count(2);
ok( $res = $client->_get( '/user_rtyler/', undef, undef, "lemonldap=$sessionId" ),
......@@ -56,6 +59,25 @@ ok( $res->[0] == 403, 'Code is 403' ) or explain( $res, 403 );
count(2);
# Required AuthnLevel = 5
ok(
$res = $client->_get( '/AuthStrong', undef, undef, "lemonldap=$sessionId" ),
'Strong Authentified query'
);
ok( $res->[0] == 302, 'Code is 302' ) or explain( $res, 302 );
%h = @{ $res->[1] };
ok(
$h{Location} eq 'http://auth.example.com//upgradesession?url='
. encode_base64( 'http://test1.example.com/AuthStrong', '' ),
'Redirection points to http://test1.example.com/AuthStrong'
)
or explain(
\%h,
'http://auth.example.com//upgradesession?url='
. encode_base64( 'http://test1.example.com/AuthStrong', '' )
);
count(3);
# Bad cookie
ok(
$res = $client->_get(
......@@ -70,9 +92,38 @@ ok( $res->[0] == 302, 'Code is 302' ) or explain( $res->[0], 302 );
unlink(
't/sessions/lock/Apache-Session-e5eec18ebb9bc96352595e2d8ce962e8ecf7af7c9a98cb9a43f9cd181cf4b545.lock'
);
count(2);
# Required AuthnLevel = 1
ok(
$res = $client->_get(
'/AuthWeak', undef, 'test2.example.com', "lemonldap=$sessionId"
),
'Weak Authentified query'
);
ok( $res->[0] == 200, 'Code is 200' ) or explain( $res, 200 );
count(2);
# Required AuthnLevel = 5
ok(
$res =
$client->_get( '/', undef, 'test2.example.com', "lemonldap=$sessionId" ),
'Default Authentified query'
);
ok( $res->[0] == 302, 'Code is 302' ) or explain( $res, 302 );
%h = @{ $res->[1] };
ok(
$h{Location} eq 'http://auth.example.com//upgradesession?url='
. encode_base64( 'http://test2.example.com/', '' ),
'Redirection points to http://test2.example.com/'
)
or explain(
\%h,
'http://auth.example.com//upgradesession?url='
. encode_base64( 'http://test2.example.com/', '' )
);
count(3);
done_testing( count() );
clean();
......
......@@ -9,6 +9,7 @@ init('Lemonldap::NG::Handler::Server');
my $res;
# Unauthentified query
# --------------------
ok( $res = $client->_get('/'), 'Unauthentified query' );
ok( ref($res) eq 'ARRAY', 'Response is an array' ) or explain( $res, 'array' );
ok( $res->[0] == 302, 'Code is 302' ) or explain( $res->[0], 302 );
......@@ -23,17 +24,14 @@ ok(
'Location => http://auth.example.com/?url='
. encode_base64( 'http://test1.example.com/', '' )
);
count(4);
# Authentified queries
# --------------------
# Authorized query
ok( $res = $client->_get( '/', undef, undef, "lemonldap=$sessionId" ),
'Authentified query' );
ok( $res->[0] == 200, 'Code is 200' ) or explain( $res->[0], 200 );
count(2);
# Check headers
......@@ -46,9 +44,33 @@ count(1);
ok( $res = $client->_get( '/deny', undef, undef, "lemonldap=$sessionId" ),
'Denied query' );
ok( $res->[0] == 403, 'Code is 403' ) or explain( $res->[0], 403 );
count(2);
# Required AuthnLevel = 1
ok( $res = $client->_get( '/AuthWeak', undef, undef, "lemonldap=$sessionId" ),
'Weak Authentified query' );
ok( $res->[0] == 200, 'Code is 200' ) or explain( $res, 200 );
count(2);
# Required AuthnLevel = 5
ok(
$res = $client->_get( '/AuthStrong', undef, undef, "lemonldap=$sessionId" ),
'Strong Authentified query'
);
ok( $res->[0] == 302, 'Code is 302' ) or explain( $res, 302 );
%h = @{ $res->[1] };
ok(
$h{Location} eq 'http://auth.example.com//upgradesession?url='
. encode_base64( 'http://test1.example.com/AuthStrong', '' ),
'Redirection points to http://test1.example.com/AuthStrong'
)
or explain(
\%h,
'http://auth.example.com//upgradesession?url='
. encode_base64( 'http://test1.example.com/AuthStrong', '' )
);
count(3);
# Bad cookie
ok(
$res = $client->_get(
......@@ -63,9 +85,38 @@ ok( $res->[0] == 302, 'Code is 302' ) or explain( $res->[0], 302 );
unlink(
't/sessions/lock/Apache-Session-e5eec18ebb9bc96352595e2d8ce962e8ecf7af7c9a98cb9a43f9cd181cf4b545.lock'
);
count(2);
# Required AuthnLevel = 1
ok(
$res = $client->_get(
'/AuthWeak', undef, 'test2.example.com', "lemonldap=$sessionId"
),
'Weak Authentified query'
);
ok( $res->[0] == 200, 'Code is 200' ) or explain( $res, 200 );
count(2);
# Required AuthnLevel = 5
ok(
$res =
$client->_get( '/', undef, 'test2.example.com', "lemonldap=$sessionId" ),
'Default Authentified query'
);
ok( $res->[0] == 302, 'Code is 302' ) or explain( $res, 302 );
%h = @{ $res->[1] };
ok(
$h{Location} eq 'http://auth.example.com//upgradesession?url='
. encode_base64( 'http://test2.example.com/', '' ),
'Redirection points to http://test2.example.com/'
)
or explain(
\%h,
'http://auth.example.com//upgradesession?url='
. encode_base64( 'http://test2.example.com/', '' )
);
count(3);
done_testing( count() );
clean();
......@@ -34,7 +34,6 @@ count(4);
ok( $res = $client->_get( '/', undef, undef, "lemonldap=$sessionId" ),
'Authentified query' );
ok( $res->[0] == 200, 'Code is 200' ) or explain( $res->[0], 200 );
count(2);
# Check headers
......@@ -49,9 +48,33 @@ count(2);
ok( $res = $client->_get( '/deny', undef, undef, "lemonldap=$sessionId" ),
'Denied query' );
ok( $res->[0] == 403, 'Code is 403' ) or explain( $res->[0], 403 );
count(2);
# Required AuthnLevel = 1
ok( $res = $client->_get( '/AuthWeak', undef, undef, "lemonldap=$sessionId" ),
'Weak Authentified query' );
ok( $res->[0] == 200, 'Code is 200' ) or explain( $res, 200 );
count(2);
# Required AuthnLevel = 5
ok(
$res = $client->_get( '/AuthStrong', undef, undef, "lemonldap=$sessionId" ),
'Strong Authentified query'
);
ok( $res->[0] == 401, 'Code is 401' ) or explain( $res, 401 );
%h = @{ $res->[1] };
ok(
$h{Location} eq 'http://auth.example.com//upgradesession?url='
. encode_base64( 'http://test1.example.com/AuthStrong', '' ),
'Redirection points to http://test1.example.com/AuthStrong'
)
or explain(
\%h,
'http://auth.example.com//upgradesession?url='
. encode_base64( 'http://test1.example.com/AuthStrong', '' )
);
count(3);
# Bad cookie
ok(
$res = $client->_get(
......@@ -66,9 +89,38 @@ ok( $res->[0] == 401, 'Code is 401' ) or explain( $res->[0], 401 );
unlink(
't/sessions/lock/Apache-Session-e5eec18ebb9bc96352595e2d8ce962e8ecf7af7c9a98cb9a43f9cd181cf4b545.lock'
);
count(2);
# Required AuthnLevel = 1
ok(
$res = $client->_get(
'/AuthWeak', undef, 'test2.example.com', "lemonldap=$sessionId"
),
'Weak Authentified query'
);
ok( $res->[0] == 200, 'Code is 200' ) or explain( $res, 200 );
count(2);
# Required AuthnLevel = 5
ok(
$res =
$client->_get( '/', undef, 'test2.example.com', "lemonldap=$sessionId" ),
'Default Authentified query'
);
ok( $res->[0] == 401, 'Code is 401' ) or explain( $res, 401 );
%h = @{ $res->[1] };
ok(
$h{Location} eq 'http://auth.example.com//upgradesession?url='
. encode_base64( 'http://test2.example.com/', '' ),
'Redirection points to http://test2.example.com/'
)
or explain(
\%h,
'http://auth.example.com//upgradesession?url='
. encode_base64( 'http://test2.example.com/', '' )
);
count(3);
done_testing( count() );
clean();
......@@ -41,12 +41,15 @@
"default": "$uid eq \"dwho\""
},
"test1.example.com": {
"^/AuthStrong(?#AuthnLevel=5)": "accept",
"^/AuthWeak(?#AuthnLevel=1)": "accept",
"^/logout": "logout_sso",
"^/deny": "deny",
"^/user_(\\w+)/": "$uid eq $_rulematch[1]",
"default": "accept"
},
"test2.example.com": {
"^/AuthWeak(?#AuthnLevel=1)": "accept",
"^/logout": "logout_sso",
"default": "accept"
},
......@@ -61,5 +64,10 @@
"portal": "http://auth.example.com/",
"reloadUrls": {},
"userDB": "Demo",
"vhostOptions": {
"test2.example.com": {
"vhostAuthnLevel": 5
}
},
"whatToTrace": "_whatToTrace"
}
......@@ -1254,6 +1254,9 @@ qr/^(?:\*\.)?(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.])*(?:[a-zA-Z][
'default' => 3,
'type' => 'int'
},
'forceGlobalStorageIssuerOTT' => {
'type' => 'bool'
},
'forceGlobalStorageUpgradeOTT' => {
'type' => 'bool'
},
......@@ -3032,10 +3035,6 @@ qr/(?:(?:https?):\/\/(?:(?:(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.]
},
'type' => 'file'
},
'samlIdPResolveCookie' => {
'default' => 'lemonldapidp',
'type' => 'text'
},
'samlIDPSSODescriptorArtifactResolutionServiceArtifact' => {
'default' =>
'1;0;urn:oasis:names:tc:SAML:2.0:bindings:SOAP;#PORTAL#/saml/artifact',
......
......@@ -553,6 +553,11 @@ sub attributes {
documentation =>
'Avoid asking confirmation when an Issuer asks to renew auth',
},
forceGlobalStorageIssuerOTT => {
type => 'bool',
documentation =>
'Force Issuer tokens be stored into Global Storage',
},
handlerInternalCache => {
type => 'int',
default => 15,
......@@ -1532,7 +1537,7 @@ sub attributes {
forceGlobalStorageUpgradeOTT => {
type => 'bool',
documentation =>
'Force upgrade tokens be stored into Global Storage',
'Force Upgrade tokens be stored into Global Storage',
},
# 2F
......@@ -2319,11 +2324,6 @@ sub attributes {
documentation =>
'Use certificate instead of public key in SAML responses',
},
samlIdPResolveCookie => {
type => 'text',
default => 'lemonldapidp',
documentation => 'SAML IDP resolution cookie',
},
samlMetadataForceUTF8 => {
default => 1,
type => 'bool',
......
......@@ -1138,7 +1138,6 @@ sub tree {
title => 'samlAdvanced',
help => 'samlservice.html#advanced',
nodes => [
'samlIdPResolveCookie',
'samlMetadataForceUTF8',
'samlStorage',
'samlStorageOptions',
......
......@@ -266,6 +266,7 @@ sub _scanNodes {
$leaf->{comment}
? "(?#$leaf->{comment})$leaf->{re}"
: $leaf->{re};
$k .= "(?#AuthnLevel=$leaf->{level})" if $leaf->{level};
$self->set( $target, $key, $k, $leaf->{data} );
}
else {
......
......@@ -592,6 +592,10 @@ llapp.controller 'TreeCtrl', [
if a.template
a._nodes = templates a.template, a.title
node.nodes.push a
if a.type.match /^rule$/
console.log "Parse rule AuthnLevel as integer"
if a.level and typeof a.level == 'string'
a.level = parseInt(a.level, 10)
d.resolve 'OK'
$scope.waiting = false
, (response) ->
......
......@@ -17,6 +17,10 @@
<th><span trspan="rule"></span></th>
<td><textarea rows="3" id="hashvalueinput" class="form-control" ng-model="currentNode.data"/></td>
</tr>
<tr ng-if="currentNode.re!='default'">
<th><span trspan="ruleAuthnLevel"></span></th>
<td><input id="ruleAuthnLevel" type="number" class="form-control" ng-model="currentNode.level"/></td>
</tr>
</table>
</div>
<script type="text/menu">
......
......@@ -7,7 +7,8 @@
<tr>
<th width="20%" trspan="comments"></th>
<th width="30%" trspan="regexps"></th>
<th width="50%" trspan="rules"></th>
<th width="40%" trspan="rules"></th>
<th width="7%" trspan="rulesAuthnLevel"></th>
<th />
</tr>
</thead>
......@@ -28,6 +29,12 @@
<td>
<input class="form-control" ng-model="s.data"/>
</td>
<td ng-if="s.re!='default'">
<input type="number" class="form-control" ng-model="s.level"/>
</td>
<td ng-if="s.re=='default'">
<input class="form-control" placeholder="defaultLevel" readonly/>
</td>
<td>
<span ng-if="s.re!='default'" class="link text-danger glyphicon glyphicon-minus-sign" ng-click="del(currentNode.nodes,$index)"/>
<span ng-if="$last" class="link text-success glyphicon glyphicon-plus-sign" ng-click="menuClick({title:'newRule'})"/>
......
(function(){var C,o,f,g,e;e=function(e,t){return $("#msg").html(window.translate(e)),$("#color").removeClass("message-positive message-warning alert-success alert-warning"),$("#color").addClass("message-"+t),"positive"===t&&(t="success"),$("#color").addClass("alert-"+t)},g={_whatToTrace:[function(e,t){return"groupBy=substr("+e+",1)"},function(e,t){return e+"="+t+"*"}]},f={_whatToTrace:function(e,t,n,a){return console.log("overSchema => level",n,"over",a),1===n&&t.length>a?e+"="+t+"*&groupBy=substr("+e+","+(n+a+1)+")":null}},C={dateTitle:["_utime","_startTime","_updateTime"],sfaTitle:["_2fDevices"]},o={home:[]},angular.module("llngSessionsExplorer",["ui.tree","ui.bootstrap","llApp"]).controller("SessionsExplorerCtrl",["$scope","$translator","$location","$q","$http",function(k,t,e,n,a){var p,r,i,d;return k.links=links,k.menulinks=menulinks,k.staticPrefix=staticPrefix,k.scriptname=scriptname,k.formPrefix=formPrefix,k.availableLanguages=availableLanguages,k.waiting=!0,k.showM=!1,k.showT=!0,k.data=[],k.currentScope=null,k.currentSession=null,k.menu=o,k.searchString="",k.U2FCheck="1",k.TOTPCheck="1",k.UBKCheck="1",k.translateP=t.translateP,k.translate=t.translate,k.translateTitle=function(e){return t.translateField(e,"title")},d="persistent",k.menuClick=function(e){if(e.popup)window.open(e.popup);else switch(e.action||(e.action=e.title),typeof e.action){case"function":e.action(k.currentNode,k),k[e.action]();break;case"string":k[e.action]();break;default:console.log(typeof e.action)}return k.showM=!1},k.search2FA=function(e){return e&&(k.searchString=""),k.currentSession=null,k.data=[],k.updateTree2("",k.data,0,0)},k.delete2FA=function(e,t){return angular.element(".data-"+t).remove(),k.waiting=!0,a.delete(scriptname+"sfa/"+d+"/"+k.currentSession.id+"?type="+e+"&epoch="+t).then(function(e){return k.waiting=!1},function(e){return k.waiting=!1}),k.showT=!1},k.stoggle=function(e){var t;return 0===(t=e.$modelValue).nodes.length&&k.updateTree(t.value,t.nodes,t.level,t.over,t.query,t.count),e.toggle()},k.displaySession=function(e){var t,n;return n=function(o){var e,t,n,a,r,i,s,u,l,c,h,p,d,f,g,m,T,w,v,y,_;for(h in e=function(e){return e},function(e,t){var n,a,r,i;for(n in r=[],a=new RegExp(e),o)i=o[n],n.match(a)&&i&&(r.push({title:n,value:i}),delete o[n]);if(0<r.length)return m.push({title:t,nodes:r})},v=o._utime,l=o._session_id,o)(_=o[h])?("string"==typeof o&&_.match(/; /)&&(o[h]=_.split("; ")),"object"!=typeof o[h]&&("_password".match(new RegExp("\b"+h+"\b"))?o[h]="********":h.match(/^(_utime|_lastAuthnUTime|_lastSeen|notification)$/)?o[h]=k.localeDate(_):h.match(/^(_startTime|_updateTime)$/)&&(g=/^(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})$/,n=(_=e(_)).match(g),o[h]=n[3]+"/"+n[2]+"/"+n[1]+" à "+n[4]+":"+n[5]+":"+n[6]))):delete o[h];for(i in m=[],C){for(w=[],u=0,p=(r=C[i]).length;u<p;u++)if(a=r[u],o[a])if(o[a].toString().match(/"type":\s*"(?:TOTP|U2F|UBK)"/)){for(w.push({title:"type",value:"name",epoch:"date"}),c=0,d=(t=JSON.parse(o[a])).length;c<d;c++){for(h in T=t[c])_=T[h],"type"===h&&(y=_),"name"===h&&(f=_),"epoch"===h&&(s=_);w.push({title:y,value:f,epoch:s})}delete o[a]}else o[a].toString().match(/\w+/)&&w.push({title:a,value:o[a]}),delete o[a];else delete o[a];0<w.length&&m.push({title:"__"+i+"__",nodes:w})}return{_utime:v,id:l,nodes:m}},t=(k.currentScope=e).$modelValue.session,a.get(scriptname+"sfa/"+d+"/"+t).then(function(e){return k.currentSession=n(e.data)}),k.showT=!1},k.localeDate=function(e){return new Date(1e3*e).toLocaleString()},k.getLanguage=function(e){return k.lang=e,k.form="white",k.init(),k.showM=!1},i=function(e,t,n){var a;return null!==(a=t.match(/#!?\/(\w+)/))&&!a[1].match(/^(persistent)$/)||(k.type="_session_uid"),k.init()},k.$on("$locationChangeSuccess",i),p=0,k.updateTree=function(o,s,u,l,e,t){var c,h,n;return k.waiting=!0,h=g[k.type]?g[k.type]:g._whatToTrace,c=h[u](k.type,o,e),25<t&&f[k.type]&&(n=f[k.type](k.type,o,u,l,e))?(l++,c=n,u-=1):l=0,a.get(scriptname+"sfa/"+d+"?"+c+"&U2FCheck="+k.U2FCheck+"&TOTPCheck="+k.TOTPCheck+"&UBKCheck="+k.UBKCheck).then(function(e){var t,n,a,r,i;if((t=e.data).result){for(n=0,a=(i=t.values).length;n<a;n++)r=i[n],p++,r.id="node"+p,u<h.length-1&&(r.nodes=[],r.level=u+1,r.query=c,r.over=l),s.push(r);""===o&&(k.total=t.total)}return k.waiting=!1},function(e){return k.waiting=!1})},k.updateTree2=function(o,s,u,l,e,t){var c,h,n;return k.waiting=!0,h=g[k.type]?g[k.type]:"_updateTime"===k.type?g._startTime:g._whatToTrace,c=h[u](k.type,o,e),25<t&&f[k.type]&&(n=f[k.type](k.type,o,u,l,e))?(l++,c=n,u-=1):l=0,a.get(scriptname+"sfa/"+d+"?_session_uid="+k.searchString+"*&groupBy=substr(_session_uid,"+k.searchString.length+")&U2FCheck="+k.U2FCheck+"&TOTPCheck="+k.TOTPCheck+"&UBKCheck="+k.UBKCheck).then(function(e){var t,n,a,r,i;if((t=e.data).result){for(n=0,a=(i=t.values).length;n<a;n++)r=i[n],p++,r.id="node"+p,u<h.length-1&&(r.nodes=[],r.level=u+1,r.query=c,r.over=l),s.push(r);""===o&&(k.total=t.total)}return k.waiting=!1},function(e){return k.waiting=!1})},k.init=function(){return k.waiting=!0,k.data=[],n.all([t.init(k.lang),k.updateTree("",k.data,0,0)]).then(function(){return k.waiting=!1},function(e){return k.waiting=!1}),k.activeModule="2ndFA",k.myStyle={color:"#ffb84d"}},r=e.path().match(/^\/(\w+)/),k.type=r?r[1]:"_whatToTrace"}])}).call(this);
\ No newline at end of file
(function(){var C,o,f,g,e;e=function(e,t){return $("#msg").html(window.translate(e)),$("#color").removeClass("message-positive message-warning alert-success alert-warning"),$("#color").addClass("message-"+t),"positive"===t&&(t="success"),$("#color").addClass("alert-"+t)},g={_whatToTrace:[function(e,t){return"groupBy=substr("+e+",1)"},function(e,t){return e+"="+t+"*"}]},f={_whatToTrace:function(e,t,n,a){return console.log("overSchema => level",n,"over",a),1===n&&t.length>a?e+"="+t+"*&groupBy=substr("+e+","+(n+a+1)+")":null}},C={dateTitle:["_utime","_startTime","_updateTime"],sfaTitle:["_2fDevices"]},o={home:[]},angular.module("llngSessionsExplorer",["ui.tree","ui.bootstrap","llApp"]).controller("SessionsExplorerCtrl",["$scope","$translator","$location","$q","$http",function(k,t,e,n,a){var p,r,i,d;return k.links=links,k.menulinks=menulinks,k.staticPrefix=staticPrefix,k.scriptname=scriptname,k.formPrefix=formPrefix,k.availableLanguages=availableLanguages,k.waiting=!0,k.showM=!1,k.showT=!0,k.data=[],k.currentScope=null,k.currentSession=null,k.menu=o,k.searchString="",k.U2FCheck="1",k.TOTPCheck="1",k.UBKCheck="1",k.translateP=t.translateP,k.translate=t.translate,k.translateTitle=function(e){return t.translateField(e,"title")},d="persistent",k.menuClick=function(e){if(e.popup)window.open(e.popup);else switch(e.action||(e.action=e.title),typeof e.action){case"function":e.action(k.currentNode,k),k[e.action]();break;case"string":k[e.action]();break;default:console.log(typeof e.action)}return k.showM=!1},k.search2FA=function(e){return e&&(k.searchString=""),k.currentSession=null,k.data=[],k.updateTree2("",k.data,0,0)},k.delete2FA=function(e,t){return angular.element(".data-"+t).remove(),k.waiting=!0,a.delete(scriptname+"sfa/"+d+"/"+k.currentSession.id+"?type="+e+"&epoch="+t).then(function(e){return k.waiting=!1},function(e){return k.waiting=!1}),k.showT=!1},k.stoggle=function(e){var t;return 0===(t=e.$modelValue).nodes.length&&k.updateTree(t.value,t.nodes,t.level,t.over,t.query,t.count),e.toggle()},k.displaySession=function(e){var t,n;return n=function(o){var e,t,n,a,r,i,s,u,l,c,h,p,d,f,g,m,T,w,v,y,_;for(h in e=function(e){return e},function(e,t){var n,a,r,i;for(n in r=[],a=new RegExp(e),o)i=o[n],n.match(a)&&i&&(r.push({title:n,value:i}),delete o[n]);if(0<r.length)return m.push({title:t,nodes:r})},v=o._utime,l=o._session_id,o)(_=o[h])?("string"==typeof o&&_.match(/; /)&&(o[h]=_.split("; ")),"object"!=typeof o[h]&&("_password".match(new RegExp("\b"+h+"\b"))?o[h]="********":h.match(/^(_utime|_lastAuthnUTime|_lastSeen|notification)$/)?o[h]=k.localeDate(_):h.match(/^(_startTime|_updateTime)$/)&&(g=/^(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})$/,n=(_=e(_)).match(g),o[h]=n[3]+"/"+n[2]+"/"+n[1]+" à "+n[4]+":"+n[5]+":"+n[6]))):delete o[h];for(i in m=[],C){for(w=[],u=0,p=(r=C[i]).length;u<p;u++)if(a=r[u],o[a])if(o[a].toString().match(/"type":\s*"(?:TOTP|U2F|UBK)"/)){for(w.push({title:"type",value:"name",epoch:"date"}),c=0,d=(t=JSON.parse(o[a])).length;c<d;c++){for(h in T=t[c])_=T[h],"type"===h&&(y=_),"name"===h&&(f=_),"epoch"===h&&(s=_);w.push({title:y,value:f,epoch:s})}delete o[a]}else o[a].toString().match(/\w+/)&&w.push({title:a,value:o[a]}),delete o[a];else delete o[a];0<w.length&&m.push({title:"__"+i+"__",nodes:w})}return{_utime:v,id:l,nodes:m}},t=(k.currentScope=e).$modelValue.session,a.get(scriptname+"sfa/"+d+"/"+t).then(function(e){return k.currentSession=n(e.data)}),k.showT=!1},k.localeDate=function(e){return new Date(1e3*e).toLocaleString()},k.getLanguage=function(e){return k.lang=e,k.form="white",k.init(),k.showM=!1},i=function(e,t,n){var a;return(null===(a=t.match(/#!?\/(\w+)/))||a[1].match(/^(persistent)$/))&&(k.type="_session_uid"),k.init()},k.$on("$locationChangeSuccess",i),p=0,k.updateTree=function(o,s,u,l,e,t){var c,h,n;return k.waiting=!0,h=g[k.type]?g[k.type]:g._whatToTrace,c=h[u](k.type,o,e),25<t&&f[k.type]&&(n=f[k.type](k.type,o,u,l,e))?(l++,c=n,u-=1):l=0,a.get(scriptname+"sfa/"+d+"?"+c+"&U2FCheck="+k.U2FCheck+"&TOTPCheck="+k.TOTPCheck+"&UBKCheck="+k.UBKCheck).then(function(e){var t,n,a,r,i;if((t=e.data).result){for(n=0,a=(i=t.values).length;n<a;n++)r=i[n],p++,r.id="node"+p,u<h.length-1&&(r.nodes=[],r.level=u+1,r.query=c,r.over=l),s.push(r);""===o&&(k.total=t.total)}return k.waiting=!1},function(e){return k.waiting=!1})},k.updateTree2=function(o,s,u,l,e,t){var c,h,n;return k.waiting=!0,h=g[k.type]?g[k.type]:"_updateTime"===k.type?g._startTime:g._whatToTrace,c=h[u](k.type,o,e),25<t&&f[k.type]&&(n=f[k.type](k.type,o,u,l,e))?(l++,c=n,u-=1):l=0,a.get(scriptname+"sfa/"+d+"?_session_uid="+k.searchString+"*&groupBy=substr(_session_uid,"+k.searchString.length+")&U2FCheck="+k.U2FCheck+"&TOTPCheck="+k.TOTPCheck+"&UBKCheck="+k.UBKCheck).then(function(e){var t,n,a,r,i;if((t=e.data).result){for(n=0,a=(i=t.values).length;n<a;n++)r=i[n],p++,r.id="node"+p,u<h.length-1&&(r<