Merge branch 'v2.0'

This commit is contained in:
Christophe Maudoux 2019-12-19 21:29:18 +01:00
commit ff095ca156
87 changed files with 2397 additions and 1305 deletions

View File

@ -23,7 +23,7 @@ use constant HANDLERSECTION => "handler";
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(?:RPMetaData(?:(?:Option(?:sExtraClaim)?|ExportedVar)s|Node)|OPMetaData(?:(?:ExportedVar|Option)s|J(?:SON|WKS)|Node)|S(?:erviceMetaDataAuthnContext|torageOptions))|penIdExportedVars)|s(?:aml(?:S(?:PMetaData(?:(?:ExportedAttribute|Option)s|Node|XML)|torageOptions)|IDPMetaData(?:(?:ExportedAttribute|Option)s|Node|XML))|essionDataToRemember|laveExportedVars|fExtra)|c(?:as(?:S(?:rvMetaData(?:(?:ExportedVar|Option)s|Node)|torageOptions)|A(?:ppMetaData(?:(?:ExportedVar|Option)s|Node)|ttributes))|(?:ustom(?:Plugins|Add)Param|ombModule)s)|p(?:ersistentStorageOptions|o(?:rtalSkinRules|st))|a(?:ut(?:hChoiceMod|oSigninR)ules|pplicationList)|v(?:hostOptions|irtualHost)|S(?:MTPTLSOpts|SLVarIf))$/;
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(?:RPMetaData(?:(?:Option(?:sExtraClaim)?|ExportedVar|Macro)s|Node)|OPMetaData(?:(?:ExportedVar|Option)s|J(?:SON|WKS)|Node)|S(?:erviceMetaDataAuthnContext|torageOptions))|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)|ingle(?:Session(?:UserByIP)?|(?:UserBy)?IP)|oap(?:Session|Config)Server|t(?:ayConnecte|orePasswor)d|kipRenewConfirmation|fRemovedUseNotif|laveDisplayLogo|howLanguages|slByAjax)|o(?:idc(?:RPMetaDataOptions(?:Re(?:freshToken|quirePKCE)|LogoutSessionRequired|IDTokenForceClaims|BypassConsent|AllowOffline|Public)|ServiceAllow(?:(?:AuthorizationCode|Implicit|Hybrid)Flow|DynamicRegistration)|OPMetaDataOptions(?:(?:CheckJWTSignatur|UseNonc)e|StoreIDToken))|ldNotifFormat)|p(?:ortal(?:Display(?:Re(?:setPassword|gister)|GeneratePassword|PasswordPolicy)|ErrorOn(?:ExpiredSession|MailNotFound)|(?:CheckLogin|Statu)s|OpenLinkInNewWindow|RequireOldPassword|ForceAuthn|AntiFrame)|roxyUseSoap)|c(?:a(?:ptcha_(?:register|login|mail)_enabled|sSrvMetaDataOptions(?:Gateway|Renew))|heck(?:User(?:Display(?:PersistentInfo|EmptyValues))?|State|XSS)|o(?:ntextSwitchingStopWithLogout|rsEnabled)|da)|l(?:dap(?:(?:Group(?:DecodeSearchedValu|Recursiv)|UsePasswordResetAttribut)e|(?:AllowResetExpired|Set)Password|ChangePasswordAsUser|PpolicyControl|ITDS)|oginHistoryEnabled)|i(?:ssuerDB(?:OpenID(?:Connect)?|SAML|CAS|Get)Activation|mpersonationSkipEmptyValues)|no(?:tif(?:ication(?:Server(?:(?:POS|GE)T|DELETE)?)?|y(?:Deleted|Other))|AjaxHook)|to(?:tp2f(?:UserCan(?:Chang|Remov)eKey|DisplayExistingSecret)|kenUseGlobalStorage)|u(?:se(?:RedirectOn(?:Forbidden|Error)|SafeJail)|2fUserCanRemoveKey|pgradeSession)|re(?:st(?:(?:Session|Config)Server|ExportSecretKeys)|freshSessions)|d(?:isablePersistentStorage|biDynamicHashEnabled|ontCompactConf)|(?:mai(?:lOnPasswordChang|ntenanc)|vhostMaintenanc)e|br(?:owsersDontStorePassword|uteForceProtection)|(?:(?:globalLogout|active)Tim|wsdlServ)er|h(?:ideOldPassword|ttpOnly)|yubikey2fUserCanRemoveKey|krb(?:RemoveDomain|ByJs))$/;
our @sessionTypes = ( 'remoteGlobal', 'global', 'localSession', 'persistent', 'saml', 'oidc', 'cas' );

View File

@ -269,7 +269,9 @@ sub _samlMetaDataNodes {
my ( $id, $resp ) = ( 1, [] );
# Return all exported attributes if asked
if ( $query =~ /^saml${type}MetaDataExportedAttributes$/ ) {
if ( $query =~
/^saml${type}MetaDataExportedAttributes|samlSPMetaDataMacros$/ )
{
my $pk =
eval { $self->getConfKey( $req, $query )->{$partner} } // {};
return $self->sendError( $req, undef, 400 ) if ( $req->error );
@ -380,7 +382,7 @@ sub _oidcMetaDataNodes {
# Return all exported attributes if asked
if ( $query =~
/^(?:oidc${type}MetaDataExportedVars|oidcRPMetaDataOptionsExtraClaims)$/
/^(?:oidc${type}MetaDataExportedVars|oidcRPMetaDataOptionsExtraClaims|oidcRPMetaDataMacros)$/
)
{
my $pk = eval { $self->getConfKey( $req, $query )->{$partner} } // {};
@ -478,7 +480,7 @@ sub _casMetaDataNodes {
# Return all exported attributes if asked
if ( $query =~
/^(?:cas${type}MetaDataExportedVars|casSrvMetaDataOptionsProxiedServices)$/
/^(?:cas${type}MetaDataExportedVars|casSrvMetaDataOptionsProxiedServices|casAppMetaDataMacros)$/
)
{
my $pk = eval { $self->getConfKey( $req, $query )->{$partner} } // {};
@ -549,7 +551,8 @@ sub authChoiceModules {
if ($@) {
$self->logger->error(
"Bad value in choice over parameters, deleted ($@)");
} else {
}
else {
$data->[5] = [ map { [ $_, $over->{$_} ] } keys %{$over} ];
}
}

View File

@ -14,22 +14,22 @@ our @EXPORT = ( @{ $EXPORT_TAGS{'all'} } );
our $specialNodeHash = {
virtualHosts => [qw(exportedHeaders locationRules post vhostOptions)],
samlIDPMetaDataNodes => [qw(samlIDPMetaDataXML samlIDPMetaDataExportedAttributes samlIDPMetaDataOptions)],
samlSPMetaDataNodes => [qw(samlSPMetaDataXML samlSPMetaDataExportedAttributes samlSPMetaDataOptions)],
samlSPMetaDataNodes => [qw(samlSPMetaDataXML samlSPMetaDataExportedAttributes samlSPMetaDataOptions samlSPMetaDataMacros)],
oidcOPMetaDataNodes => [qw(oidcOPMetaDataJSON oidcOPMetaDataJWKS oidcOPMetaDataOptions oidcOPMetaDataExportedVars)],
oidcRPMetaDataNodes => [qw(oidcRPMetaDataOptions oidcRPMetaDataExportedVars oidcRPMetaDataOptionsExtraClaims)],
oidcRPMetaDataNodes => [qw(oidcRPMetaDataOptions oidcRPMetaDataExportedVars oidcRPMetaDataOptionsExtraClaims oidcRPMetaDataMacros)],
casSrvMetaDataNodes => [qw(casSrvMetaDataOptions casSrvMetaDataExportedVars)],
casAppMetaDataNodes => [qw(casAppMetaDataOptions casAppMetaDataExportedVars)],
casAppMetaDataNodes => [qw(casAppMetaDataOptions casAppMetaDataExportedVars casAppMetaDataMacros)],
};
our $doubleHashKeys = 'issuerDBGetParameters';
our $simpleHashKeys = '(?:(?:l(?:o(?:calSessionStorageOption|goutService)|dapExportedVar|wp(?:Ssl)?Opt)|c(?:as(?:StorageOption|Attribute)|ustom(?:Plugins|Add)Param|ombModule)|re(?:moteGlobalStorageOption|st2f(?:Verify|Init)Arg|loadUrl)|(?:(?:d(?:emo|bi)|facebook|webID)E|e)xportedVar|g(?:r(?:antSessionRule|oup)|lobalStorageOption)|n(?:otificationStorageOption|ginxCustomHandler)|p(?:ersistentStorageOption|ortalSkinRule)|macro)s|o(?:idcS(?:erviceMetaDataAuthnContext|torageOptions)|penIdExportedVars)|s(?:(?:amlStorageOption|laveExportedVar)s|essionDataToRemember|fExtra)|a(?:ut(?:hChoiceMod|oSigninR)ules|pplicationList)|S(?:MTPTLSOpts|SLVarIf))';
our $specialNodeKeys = '(?:(?:(?:saml(?:ID|S)|oidc[OR])P|cas(?:App|Srv))MetaDataNode|virtualHost)s';
our $casAppMetaDataNodeKeys = 'casAppMetaData(?:Options(?:UserAttribut|Servic|Rul)e|ExportedVars)';
our $casAppMetaDataNodeKeys = 'casAppMetaData(?:Options(?:UserAttribut|Servic|Rul)e|(?:ExportedVar|Macro)s)';
our $casSrvMetaDataNodeKeys = 'casSrvMetaData(?:Options(?:ProxiedServices|DisplayName|SortNumber|Gateway|Renew|Icon|Url)|ExportedVars)';
our $oidcOPMetaDataNodeKeys = 'oidcOPMetaData(?:Options(?:C(?:lient(?:Secret|ID)|heckJWTSignature|onfigurationURI)|S(?:toreIDToken|ortNumber|cope)|TokenEndpointAuthMethod|(?:JWKSTimeou|Promp)t|I(?:DTokenMaxAge|con)|U(?:iLocales|seNonce)|Display(?:Name)?|AcrValues|MaxAge)|ExportedVars|J(?:SON|WKS))';
our $oidcRPMetaDataNodeKeys = 'oidcRPMetaData(?:Options(?:A(?:(?:uthorizationCode|ccessToken)Expiration|llowOffline)|I(?:DToken(?:ForceClaims|Expiration|SignAlg)|con)|R(?:e(?:directUris|freshToken|quirePKCE)|ule)|Logout(?:SessionRequired|Type|Url)|P(?:ostLogoutRedirectUris|ublic)|OfflineSessionExpiration|Client(?:Secret|ID)|BypassConsent|DisplayName|ExtraClaims|UserIDAttr)|ExportedVars)';
our $oidcRPMetaDataNodeKeys = 'oidcRPMetaData(?:Options(?:A(?:(?:uthorizationCode|ccessToken)Expiration|llowOffline)|I(?:DToken(?:ForceClaims|Expiration|SignAlg)|con)|R(?:e(?:directUris|freshToken|quirePKCE)|ule)|Logout(?:SessionRequired|Type|Url)|P(?:ostLogoutRedirectUris|ublic)|OfflineSessionExpiration|Client(?:Secret|ID)|BypassConsent|DisplayName|ExtraClaims|UserIDAttr)|(?:ExportedVar|Macro)s)';
our $samlIDPMetaDataNodeKeys = 'samlIDPMetaData(?:Options(?:(?:Check(?:S[LS]OMessageSignatur|Audienc|Tim)|EncryptionMod|UserAttribut|DisplayNam)e|S(?:ignS[LS]OMessage|toreSAMLToken|[LS]OBinding|ortNumber)|A(?:llow(?:LoginFromIDP|ProxiedAuthn)|daptSessionUtime)|Re(?:questedAuthnContext|solutionRule|layStateURL)|Force(?:Authn|UTF8)|I(?:sPassive|con)|NameIDFormat)|ExportedAttributes|XML)';
our $samlSPMetaDataNodeKeys = 'samlSPMetaData(?:Options(?:N(?:ameID(?:SessionKey|Format)|otOnOrAfterTimeout)|S(?:essionNotOnOrAfterTimeout|ignS[LS]OMessage)|(?:CheckS[LS]OMessageSignatur|OneTimeUs|Rul)e|En(?:ableIDPInitiatedURL|cryptionMode)|ForceUTF8)|ExportedAttributes|XML)';
our $samlSPMetaDataNodeKeys = 'samlSPMetaData(?:Options(?:N(?:ameID(?:SessionKey|Format)|otOnOrAfterTimeout)|S(?:essionNotOnOrAfterTimeout|ignS[LS]OMessage)|(?:CheckS[LS]OMessageSignatur|OneTimeUs|Rul)e|En(?:ableIDPInitiatedURL|cryptionMode)|ForceUTF8)|(?:ExportedAttribute|Macro)s|XML)';
our $virtualHostKeys = '(?:vhost(?:A(?:uthnLevel|liases)|(?:Maintenanc|Typ)e|ServiceTokenTTL|Https|Port)|(?:exportedHeader|locationRule)s|post)';
our $authParameters = {

View File

@ -2,6 +2,7 @@ package Lemonldap::NG::Common::Notifications;
use strict;
use Mouse;
use JSON qw(to_json);
our $VERSION = '2.1.0';
@ -32,8 +33,14 @@ has notifField => (
sub getNotifications {
my ( $self, $uid ) = @_;
my $forAll = $self->get( $self->conf->{notificationWildcard} );
if ( $uid and $uid eq '_all_' ) {
$self->logger->info("Retrieve ALL pending notifications");
my $all = $self->getAll();
$all = { map { $_ => to_json( $all->{$_} ) } keys %$all };
return { %$all, %$forAll };
}
my $forUser = $self->get($uid);
my $forAll = $self->get( $self->conf->{notificationWildcard} );
if ( $forUser and $forAll ) {
return { %$forUser, %$forAll };
}

View File

@ -34,6 +34,9 @@ sub newNotification {
$self->logger->error("$err");
return ( 0, "$err" );
}
# Prevent to store time. Keep date only
$tmp =~ s/^(\d{4}-\d{2}-\d{2}).*$/$1/;
push @data, $tmp;
}
@ -44,6 +47,7 @@ sub newNotification {
}
push @data, ( $notif->{condition} );
$notif->{date} =~ s/^(\d{4}-\d{2}-\d{2}).*$/$1/;
my $body = to_json($notif);
push @notifs, [ @data, $body ];
}

View File

@ -45,6 +45,9 @@ sub newNotification {
$self->logger->error("$err");
return 0;
}
# Prevent to store time. Keep date only
$tmp =~ s/^(\d{4}-\d{2}-\d{2}).*$/$1/;
push @data, $tmp;
}

View File

@ -674,6 +674,17 @@ sub attributes {
},
'type' => 'keyTextContainer'
},
'casAppMetaDataMacros' => {
'default' => {},
'test' => {
'keyMsgFail' => '__badMacroName__',
'keyTest' => qr/^[_a-zA-Z][a-zA-Z0-9_]*$/,
'test' => sub {
return perlExpr(@_);
}
},
'type' => 'keyTextContainer'
},
'casAppMetaDataNodes' => {
'type' => 'casAppMetaDataNodeContainer'
},
@ -2032,6 +2043,17 @@ qr/^(?:\*\.)?(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.])*(?:[a-zA-Z][
},
'type' => 'keyTextContainer'
},
'oidcRPMetaDataMacros' => {
'default' => {},
'test' => {
'keyMsgFail' => '__badMacroName__',
'keyTest' => qr/^[_a-zA-Z][a-zA-Z0-9_]*$/,
'test' => sub {
return perlExpr(@_);
}
},
'type' => 'keyTextContainer'
},
'oidcRPMetaDataNodes' => {
'type' => 'oidcRPMetaDataNodeContainer'
},
@ -3214,6 +3236,17 @@ qr/(?:(?:https?):\/\/(?:(?:(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.]
'test' => qr/\w/,
'type' => 'samlAttributeContainer'
},
'samlSPMetaDataMacros' => {
'default' => {},
'test' => {
'keyMsgFail' => '__badMacroName__',
'keyTest' => qr/^[_a-zA-Z][a-zA-Z0-9_]*$/,
'test' => sub {
return perlExpr(@_);
}
},
'type' => 'keyTextContainer'
},
'samlSPMetaDataNodes' => {
'type' => 'samlSPMetaDataNodeContainer'
},

View File

@ -184,11 +184,11 @@ our \@EXPORT = ( \@{ \$EXPORT_TAGS{'all'} } );
our \$specialNodeHash = {
virtualHosts => [qw(exportedHeaders locationRules post vhostOptions)],
samlIDPMetaDataNodes => [qw(samlIDPMetaDataXML samlIDPMetaDataExportedAttributes samlIDPMetaDataOptions)],
samlSPMetaDataNodes => [qw(samlSPMetaDataXML samlSPMetaDataExportedAttributes samlSPMetaDataOptions)],
samlSPMetaDataNodes => [qw(samlSPMetaDataXML samlSPMetaDataExportedAttributes samlSPMetaDataOptions samlSPMetaDataMacros)],
oidcOPMetaDataNodes => [qw(oidcOPMetaDataJSON oidcOPMetaDataJWKS oidcOPMetaDataOptions oidcOPMetaDataExportedVars)],
oidcRPMetaDataNodes => [qw(oidcRPMetaDataOptions oidcRPMetaDataExportedVars oidcRPMetaDataOptionsExtraClaims)],
oidcRPMetaDataNodes => [qw(oidcRPMetaDataOptions oidcRPMetaDataExportedVars oidcRPMetaDataOptionsExtraClaims oidcRPMetaDataMacros)],
casSrvMetaDataNodes => [qw(casSrvMetaDataOptions casSrvMetaDataExportedVars)],
casAppMetaDataNodes => [qw(casAppMetaDataOptions casAppMetaDataExportedVars)],
casAppMetaDataNodes => [qw(casAppMetaDataOptions casAppMetaDataExportedVars casAppMetaDataMacros)],
};
EOF
@ -277,11 +277,13 @@ $defaultAttr}
exportedHeaders locationRules post vhostOptions
samlIDPMetaDataXML samlIDPMetaDataExportedAttributes
samlIDPMetaDataOptions samlSPMetaDataXML
samlSPMetaDataExportedAttributes samlSPMetaDataOptions
oidcOPMetaDataJSON oidcOPMetaDataJWKS oidcOPMetaDataOptions
samlSPMetaDataExportedAttributes samlSPMetaDataMacros
samlSPMetaDataOptions oidcOPMetaDataJSON
oidcOPMetaDataJWKS oidcOPMetaDataOptions
oidcOPMetaDataExportedVars oidcRPMetaDataOptions
oidcRPMetaDataExportedVars oidcRPMetaDataOptionsExtraClaims
casAppMetaDataExportedVars casAppMetaDataOptions
oidcRPMetaDataMacros casAppMetaDataExportedVars
casAppMetaDataOptions casAppMetaDataMacros
casSrvMetaDataExportedVars casSrvMetaDataOptions
)
)

View File

@ -2202,6 +2202,18 @@ sub attributes {
test => sub { return perlExpr(@_) },
documentation => 'CAS App rule',
},
casAppMetaDataMacros => {
type => 'keyTextContainer',
help =>
'exportedvars.html#extend_variables_using_macros_and_groups',
test => {
keyTest => qr/^[_a-zA-Z][a-zA-Z0-9_]*$/,
keyMsgFail => '__badMacroName__',
test => sub { return perlExpr(@_) },
},
default => {},
documentation => 'Macros',
},
# Fake attribute: used by manager REST API to agglomerate all nodes
# related to a CAS SP partner
@ -2801,6 +2813,18 @@ sub attributes {
test => sub { return perlExpr(@_) },
documentation => 'Rule to grant access to this SP',
},
samlSPMetaDataMacros => {
type => 'keyTextContainer',
help =>
'exportedvars.html#extend_variables_using_macros_and_groups',
test => {
keyTest => qr/^[_a-zA-Z][a-zA-Z0-9_]*$/,
keyMsgFail => '__badMacroName__',
test => sub { return perlExpr(@_) },
},
default => {},
documentation => 'Macros',
},
# AUTH, USERDB and PASSWORD MODULES
authentication => {
@ -3888,6 +3912,18 @@ m{^(?:ldapi://[^/]*/?|\w[\w\-\.]*(?::\d{1,5})?|ldap(?:s|\+tls)?://\w[\w\-\.]*(?:
test => sub { return perlExpr(@_) },
documentation => 'Rule to grant access to this RP',
},
oidcRPMetaDataMacros => {
type => 'keyTextContainer',
help =>
'exportedvars.html#extend_variables_using_macros_and_groups',
test => {
keyTest => qr/^[_a-zA-Z][a-zA-Z0-9_]*$/,
keyMsgFail => '__badMacroName__',
test => sub { return perlExpr(@_) },
},
default => {},
documentation => 'Macros',
},
};
}

View File

@ -138,7 +138,8 @@ sub cTrees {
]
}
]
}
},
"samlSPMetaDataMacros",
],
oidcOPMetaDataNode => [
'oidcOPMetaDataJSON',
@ -224,6 +225,7 @@ sub cTrees {
'oidcRPMetaDataOptionsRule',
]
},
'oidcRPMetaDataMacros',
{
title => 'oidcRPMetaDataOptionsDisplay',
form => 'simpleInputContainer',
@ -266,6 +268,7 @@ sub cTrees {
'casAppMetaDataOptionsRule'
]
},
'casAppMetaDataMacros',
],
};
}

View File

@ -334,7 +334,7 @@ sub _scanNodes {
hdebug(" SAML data is an array, serializing");
$leaf->{data} = join ';', @{ $leaf->{data} };
}
if ( $target =~ /^saml(?:S|ID)PMetaDataExportedAttributes$/ ) {
if ( $target =~ /^saml(?:S|ID)PMetaData(?:ExportedAttributes|Macros)$/ ) {
if ( $leaf->{cnodes} ) {
hdebug(" $target: unopened node");
$self->newConf->{$target}->{$key} =
@ -394,7 +394,7 @@ sub _scanNodes {
hdebug(" $target");
$self->set( $target, $key, $leaf->{data} );
}
elsif ( $target =~ /^oidc(?:O|R)PMetaDataExportedVars$/ ) {
elsif ( $target =~ /^oidc(?:O|R)PMetaData(?:ExportedVars|Macros)$/ ) {
hdebug(" $target");
if ( $leaf->{cnodes} ) {
hdebug(' unopened');
@ -463,7 +463,7 @@ sub _scanNodes {
$self->_scanNodes($subNodes);
$self->set( $target, $key, $leaf->{title}, $leaf->{data} );
}
elsif ( $target =~ /^cas(?:App|Srv)MetaDataExportedVars$/ ) {
elsif ( $target =~ /^cas(?:App|Srv)MetaData(?:ExportedVars|Macros)$/ ) {
hdebug(" $target");
if ( $leaf->{cnodes} ) {
hdebug(' unopened');

View File

@ -200,7 +200,7 @@ sub notifications {
result => 1,
count => $count,
values => $res,
total => $total
total => $total
}
);
}
@ -220,8 +220,15 @@ sub notifications {
@r = sort { $a->{$f} cmp $b->{$f} } @r;
}
}
return $self->sendJSONresponse( $req,
{ result => 1, count => scalar(@r), values => \@r, total => $total } );
return $self->sendJSONresponse(
$req,
{
result => 1,
count => scalar(@r),
values => \@r,
total => $total
}
);
}
}

View File

@ -217,7 +217,7 @@ llapp.controller 'TreeCtrl', [
$scope.getKey($scope.currentNode)
# NODES MANAGEMENT
id = 1
idinc = 1
$scope._findContainer = ->
return $scope._findScopeContainer().$modelValue
$scope._findScopeContainer = ->
@ -237,7 +237,7 @@ llapp.controller 'TreeCtrl', [
l = node.nodes.length
n = if l > 0 then l - 1 else 0
node.nodes.push
id: "#{node.id}/n#{id++}"
id: "#{node.id}/n#{idinc++}"
title: 'New rule'
re: 'Message'
comment: 'New rule'
@ -250,7 +250,7 @@ llapp.controller 'TreeCtrl', [
l = node.nodes.length
n = if l > 0 then l - 1 else 0
node.nodes.splice n, 0,
id: "#{node.id}/n#{id++}"
id: "#{node.id}/n#{idinc++}"
title: 'New rule'
re: '^/new'
comment: 'New rule'
@ -261,7 +261,7 @@ llapp.controller 'TreeCtrl', [
$scope.newPost = ->
node = $scope._findContainer()
node.nodes.push
id: "#{node.id}/n#{id++}"
id: "#{node.id}/n#{idinc++}"
title: "/absolute/path/to/form"
data: {}
type: "post"
@ -274,7 +274,7 @@ llapp.controller 'TreeCtrl', [
$scope.newAuthChoice = ->
node = $scope._findContainer()
node.nodes.push
id: "#{node.id}/n#{id++}"
id: "#{node.id}/n#{idinc++}"
title: "1_Key"
data: ['Null', 'Null', 'Null']
type: "authChoice"
@ -284,7 +284,7 @@ llapp.controller 'TreeCtrl', [
$scope.newHashEntry = ->
node = $scope._findContainer()
node.nodes.push
id: "#{node.id}/n#{id++}"
id: "#{node.id}/n#{idinc++}"
title: 'new'
data: ''
type: "keyText"
@ -295,7 +295,7 @@ llapp.controller 'TreeCtrl', [
if cs.$modelValue.type == 'menuApp'
cs = cs.$parentNodeScope
cs.$modelValue.nodes.push
id: "#{cs.$modelValue.id}/n#{id++}"
id: "#{cs.$modelValue.id}/n#{idinc++}"
title: "New category"
type: "menuCat"
nodes: []
@ -306,7 +306,7 @@ llapp.controller 'TreeCtrl', [
if cs.$modelValue.type == 'menuApp'
cs = cs.$parentNodeScope
cs.$modelValue.nodes.push
id: "#{cs.$modelValue.id}/n#{id++}"
id: "#{cs.$modelValue.id}/n#{idinc++}"
title: "New application"
type: "menuApp"
data:
@ -319,7 +319,7 @@ llapp.controller 'TreeCtrl', [
$scope.newCmbMod = ->
node = $scope._findContainer()
node.nodes.push
id: "#{node.id}/n#{id++}"
id: "#{node.id}/n#{idinc++}"
title: 'new'
type: 'cmbModule'
data:
@ -331,7 +331,7 @@ llapp.controller 'TreeCtrl', [
$scope.newSfExtra = ->
node = $scope._findContainer()
node.nodes.push
id: "#{node.id}/n#{id++}"
id: "#{node.id}/n#{idinc++}"
title: 'new'
type: 'sfExtra'
data:
@ -345,18 +345,18 @@ llapp.controller 'TreeCtrl', [
$scope.newSfOver = ->
d = $scope.currentNode.data
d.over = [] unless d.over
d.over.push ["new#{id++}", '']
d.over.push ["new#{idinc++}", '']
$scope.newCmbOver = ->
d = $scope.currentNode.data
d.over = [] unless d.over
d.over.push ["new#{id++}", '']
d.over.push ["new#{idinc++}", '']
$scope.newChoiceOver = ->
d = $scope.currentNode.data
console.log "data", d
d[5] = [] unless d[5]
d[5].push ["new#{id++}", '']
d[5].push ["new#{idinc++}", '']
# Add host
$scope.addHost = () ->
@ -370,7 +370,7 @@ llapp.controller 'TreeCtrl', [
$scope.addSamlAttribute = ->
node = $scope._findContainer()
node.nodes.push
id: "#{node.id}/n#{id++}"
id: "#{node.id}/n#{idinc++}"
title: 'new'
type: 'samlAttribute'
data: ['0', 'New', '', '']

View File

@ -1,5 +1,5 @@
/**
* @license AngularJS v1.7.8
* @license AngularJS v1.7.9
* (c) 2010-2018 Google, Inc. http://angularjs.org
* License: MIT
*/
@ -4252,7 +4252,7 @@ angular.module('ngAnimate', [], function initAngularHelpers() {
isFunction = angular.isFunction;
isElement = angular.isElement;
})
.info({ angularVersion: '1.7.8' })
.info({ angularVersion: '1.7.9' })
.directive('ngAnimateSwap', ngAnimateSwapDirective)
.directive('ngAnimateChildren', $$AnimateChildrenDirective)

View File

@ -1,5 +1,5 @@
/*
AngularJS v1.7.8
AngularJS v1.7.9
(c) 2010-2018 Google, Inc. http://angularjs.org
License: MIT
*/
@ -11,7 +11,7 @@ f=!a[b]||a[b+"-remove"]):-1===c&&(d="removeClass",f=a[b]||a[b+"-add"]);f&&(k[d].
b:a:b}function Ka(a,b,c){var d=Object.create(null),f=a.getComputedStyle(b)||{};s(c,function(a,c){var b=f[a];if(b){var L=b.charAt(0);if("-"===L||"+"===L||0<=L)b=Va(b);0===b&&(b=null);d[c]=b}});return d}function Va(a){var b=0;a=a.split(/\s*,\s*/);s(a,function(a){"s"===a.charAt(a.length-1)&&(a=a.substring(0,a.length-1));a=parseFloat(a)||0;b=b?Math.max(a,b):a});return b}function ya(a){return 0===a||null!=a}function La(a,b){var c=M,d=a+"s";b?c+="Duration":d+=" linear all";return[c,d]}function Ma(a,b,c){s(c,
function(c){a[c]=za(a[c])?a[c]:b.style.getPropertyValue(c)})}var M,Aa,ca,Ba;void 0===Y.ontransitionend&&void 0!==Y.onwebkittransitionend?(M="WebkitTransition",Aa="webkitTransitionEnd transitionend"):(M="transition",Aa="transitionend");void 0===Y.onanimationend&&void 0!==Y.onwebkitanimationend?(ca="WebkitAnimation",Ba="webkitAnimationEnd animationend"):(ca="animation",Ba="animationend");var qa=ca+"Delay",Ca=ca+"Duration",na=M+"Delay",Na=M+"Duration",Pa=z.$$minErr("ng"),ra={blockTransitions:function(a,
b){var c=b?"-"+b+"s":"";ma(a,[na,c]);return[na,c]}},Wa={transitionDuration:Na,transitionDelay:na,transitionProperty:M+"Property",animationDuration:Ca,animationDelay:qa,animationIterationCount:ca+"IterationCount"},Xa={transitionDuration:Na,transitionDelay:na,animationDuration:Ca,animationDelay:qa},Da,wa,s,Z,za,sa,Ea,ta,G,R,A,N;z.module("ngAnimate",[],function(){N=z.noop;Da=z.copy;wa=z.extend;A=z.element;s=z.forEach;Z=z.isArray;G=z.isString;ta=z.isObject;R=z.isUndefined;za=z.isDefined;Ea=z.isFunction;
sa=z.isElement}).info({angularVersion:"1.7.8"}).directive("ngAnimateSwap",["$animate",function(a){return{restrict:"A",transclude:"element",terminal:!0,priority:550,link:function(b,c,d,f,k){var e,Q;b.$watchCollection(d.ngAnimateSwap||d["for"],function(b){e&&a.leave(e);Q&&(Q.$destroy(),Q=null);(b||0===b)&&k(function(b,d){e=b;Q=d;a.enter(b,null,c)})})}}}]).directive("ngAnimateChildren",["$interpolate",function(a){return{link:function(b,c,d){function f(a){c.data("$$ngAnimateChildren","on"===a||"true"===
sa=z.isElement}).info({angularVersion:"1.7.9"}).directive("ngAnimateSwap",["$animate",function(a){return{restrict:"A",transclude:"element",terminal:!0,priority:550,link:function(b,c,d,f,k){var e,Q;b.$watchCollection(d.ngAnimateSwap||d["for"],function(b){e&&a.leave(e);Q&&(Q.$destroy(),Q=null);(b||0===b)&&k(function(b,d){e=b;Q=d;a.enter(b,null,c)})})}}}]).directive("ngAnimateChildren",["$interpolate",function(a){return{link:function(b,c,d){function f(a){c.data("$$ngAnimateChildren","on"===a||"true"===
a)}var k=d.ngAnimateChildren;G(k)&&0===k.length?c.data("$$ngAnimateChildren",!0):(f(a(k)(b)),d.$observe("ngAnimateChildren",f))}}}]).factory("$$rAFScheduler",["$$rAF",function(a){function b(a){d=d.concat(a);c()}function c(){if(d.length){for(var b=d.shift(),e=0;e<b.length;e++)b[e]();f||a(function(){f||c()})}}var d,f;d=b.queue=[];b.waitUntilQuiet=function(b){f&&f();f=a(function(){f=null;b();c()})};return b}]).provider("$$animateQueue",["$animateProvider",function(a){function b(a){return{addClass:a.addClass,
removeClass:a.removeClass,from:a.from,to:a.to}}function c(a){if(!a)return null;a=a.split(" ");var b=Object.create(null);s(a,function(a){b[a]=!0});return b}function d(a,b){if(a&&b){var d=c(b);return a.split(" ").some(function(a){return d[a]})}}function f(a,b,c){return e[a].some(function(a){return a(b,c)})}function k(a,b){var c=0<(a.addClass||"").length,d=0<(a.removeClass||"").length;return b?c&&d:c||d}var e=this.rules={skip:[],cancel:[],join:[]};e.join.push(function(a,b){return!a.structural&&k(a)});
e.skip.push(function(a,b){return!a.structural&&!k(a)});e.skip.push(function(a,b){return"leave"===b.event&&a.structural});e.skip.push(function(a,b){return b.structural&&2===b.state&&!a.structural});e.cancel.push(function(a,b){return b.structural&&a.structural});e.cancel.push(function(a,b){return 2===b.state&&a.structural});e.cancel.push(function(a,b){if(b.structural)return!1;var c=a.addClass,f=a.removeClass,k=b.addClass,e=b.removeClass;return R(c)&&R(f)||R(k)&&R(e)?!1:d(c,e)||d(f,k)});this.$get=["$$rAF",

View File

@ -1,5 +1,5 @@
/**
* @license AngularJS v1.7.8
* @license AngularJS v1.7.9
* (c) 2010-2018 Google, Inc. http://angularjs.org
* License: MIT
*/
@ -63,7 +63,7 @@
var ARIA_DISABLE_ATTR = 'ngAriaDisable';
var ngAriaModule = angular.module('ngAria', ['ng']).
info({ angularVersion: '1.7.8' }).
info({ angularVersion: '1.7.9' }).
provider('$aria', $AriaProvider);
/**

View File

@ -1,9 +1,9 @@
/*
AngularJS v1.7.8
AngularJS v1.7.9
(c) 2010-2018 Google, Inc. http://angularjs.org
License: MIT
*/
(function(t,l){'use strict';var c="BUTTON A INPUT TEXTAREA SELECT DETAILS SUMMARY".split(" "),m=function(a,e){if(-1!==e.indexOf(a[0].nodeName))return!0};l.module("ngAria",["ng"]).info({angularVersion:"1.7.8"}).provider("$aria",function(){function a(a,c,n,g){return function(d,f,b){if(!b.hasOwnProperty("ngAriaDisable")){var p=b.$normalize(c);!e[p]||m(f,n)||b[p]||d.$watch(b[a],function(b){b=g?!b:!!b;f.attr(c,b)})}}}var e={ariaHidden:!0,ariaChecked:!0,ariaReadonly:!0,ariaDisabled:!0,ariaRequired:!0,ariaInvalid:!0,
(function(t,l){'use strict';var c="BUTTON A INPUT TEXTAREA SELECT DETAILS SUMMARY".split(" "),m=function(a,e){if(-1!==e.indexOf(a[0].nodeName))return!0};l.module("ngAria",["ng"]).info({angularVersion:"1.7.9"}).provider("$aria",function(){function a(a,c,n,g){return function(d,f,b){if(!b.hasOwnProperty("ngAriaDisable")){var p=b.$normalize(c);!e[p]||m(f,n)||b[p]||d.$watch(b[a],function(b){b=g?!b:!!b;f.attr(c,b)})}}}var e={ariaHidden:!0,ariaChecked:!0,ariaReadonly:!0,ariaDisabled:!0,ariaRequired:!0,ariaInvalid:!0,
ariaValue:!0,tabindex:!0,bindKeydown:!0,bindRoleForClick:!0};this.config=function(a){e=l.extend(e,a)};this.$get=function(){return{config:function(a){return e[a]},$$watchExpr:a}}}).directive("ngShow",["$aria",function(a){return a.$$watchExpr("ngShow","aria-hidden",[],!0)}]).directive("ngHide",["$aria",function(a){return a.$$watchExpr("ngHide","aria-hidden",[],!1)}]).directive("ngValue",["$aria",function(a){return a.$$watchExpr("ngValue","aria-checked",c,!1)}]).directive("ngChecked",["$aria",function(a){return a.$$watchExpr("ngChecked",
"aria-checked",c,!1)}]).directive("ngReadonly",["$aria",function(a){return a.$$watchExpr("ngReadonly","aria-readonly",c,!1)}]).directive("ngRequired",["$aria",function(a){return a.$$watchExpr("ngRequired","aria-required",c,!1)}]).directive("ngModel",["$aria",function(a){function e(e,g,d,f){return a.config(g)&&!d.attr(e)&&(f||!m(d,c))&&("hidden"!==d.attr("type")||"INPUT"!==d[0].nodeName)}function k(a,e){return!e.attr("role")&&e.attr("type")===a&&!m(e,c)}function h(a,e){var d=a.type,f=a.role;return"checkbox"===
(d||f)||"menuitemcheckbox"===f?"checkbox":"radio"===(d||f)||"menuitemradio"===f?"radio":"range"===d||"progressbar"===f||"slider"===f?"range":""}return{restrict:"A",require:"ngModel",priority:200,compile:function(c,g){if(!g.hasOwnProperty("ngAriaDisable")){var d=h(g,c);return{post:function(f,b,c,g){function h(){return g.$modelValue}function m(a){b.attr("aria-checked",c.value==g.$viewValue)}function n(){b.attr("aria-checked",!g.$isEmpty(g.$viewValue))}var l=e("tabindex","tabindex",b,!1);switch(d){case "radio":case "checkbox":k(d,

View File

@ -1,5 +1,5 @@
/**
* @license AngularJS v1.7.8
* @license AngularJS v1.7.9
* (c) 2010-2018 Google, Inc. http://angularjs.org
* License: MIT
*/
@ -17,7 +17,7 @@
angular.module('ngCookies', ['ng']).
info({ angularVersion: '1.7.8' }).
info({ angularVersion: '1.7.9' }).
/**
* @ngdoc provider
* @name $cookiesProvider

View File

@ -1,9 +1,9 @@
/*
AngularJS v1.7.8
AngularJS v1.7.9
(c) 2010-2018 Google, Inc. http://angularjs.org
License: MIT
*/
(function(n,e){'use strict';function m(d,k,l){var a=l.baseHref(),h=d[0];return function(f,b,c){var d,g;c=c||{};g=c.expires;d=e.isDefined(c.path)?c.path:a;e.isUndefined(b)&&(g="Thu, 01 Jan 1970 00:00:00 GMT",b="");e.isString(g)&&(g=new Date(g));b=encodeURIComponent(f)+"="+encodeURIComponent(b);b=b+(d?";path="+d:"")+(c.domain?";domain="+c.domain:"");b+=g?";expires="+g.toUTCString():"";b+=c.secure?";secure":"";b+=c.samesite?";samesite="+c.samesite:"";c=b.length+1;4096<c&&k.warn("Cookie '"+f+"' possibly not set or overflowed because it was too large ("+
c+" > 4096 bytes)!");h.cookie=b}}e.module("ngCookies",["ng"]).info({angularVersion:"1.7.8"}).provider("$cookies",[function(){var d=this.defaults={};this.$get=["$$cookieReader","$$cookieWriter",function(k,l){return{get:function(a){return k()[a]},getObject:function(a){return(a=this.get(a))?e.fromJson(a):a},getAll:function(){return k()},put:function(a,h,f){l(a,h,f?e.extend({},d,f):d)},putObject:function(a,d,f){this.put(a,e.toJson(d),f)},remove:function(a,h){l(a,void 0,h?e.extend({},d,h):d)}}}]}]);m.$inject=
c+" > 4096 bytes)!");h.cookie=b}}e.module("ngCookies",["ng"]).info({angularVersion:"1.7.9"}).provider("$cookies",[function(){var d=this.defaults={};this.$get=["$$cookieReader","$$cookieWriter",function(k,l){return{get:function(a){return k()[a]},getObject:function(a){return(a=this.get(a))?e.fromJson(a):a},getAll:function(){return k()},put:function(a,h,f){l(a,h,f?e.extend({},d,f):d)},putObject:function(a,d,f){this.put(a,e.toJson(d),f)},remove:function(a,h){l(a,void 0,h?e.extend({},d,h):d)}}}]}]);m.$inject=
["$document","$log","$browser"];e.module("ngCookies").provider("$$cookieWriter",function(){this.$get=m})})(window,window.angular);
//# sourceMappingURL=angular-cookies.min.js.map

View File

@ -1,5 +1,5 @@
/**
* @license AngularJS v1.7.8
* @license AngularJS v1.7.9
* (c) 2010-2018 Google, Inc. http://angularjs.org
* License: MIT
*/
@ -99,7 +99,7 @@ function isValidObjectMaxDepth(maxDepth) {
function minErr(module, ErrorConstructor) {
ErrorConstructor = ErrorConstructor || Error;
var url = 'https://errors.angularjs.org/1.7.8/';
var url = 'https://errors.angularjs.org/1.7.9/';
var regex = url.replace('.', '\\.') + '[\\s\\S]*';
var errRegExp = new RegExp(regex, 'g');
@ -481,8 +481,10 @@ function baseExtend(dst, objs, deep) {
} else if (isElement(src)) {
dst[key] = src.clone();
} else {
if (!isObject(dst[key])) dst[key] = isArray(src) ? [] : {};
baseExtend(dst[key], [src], true);
if (key !== '__proto__') {
if (!isObject(dst[key])) dst[key] = isArray(src) ? [] : {};
baseExtend(dst[key], [src], true);
}
}
} else {
dst[key] = src;
@ -2805,11 +2807,11 @@ function toDebugString(obj, maxDepth) {
var version = {
// These placeholder strings will be replaced by grunt's `build` task.
// They need to be double- or single-quoted.
full: '1.7.8',
full: '1.7.9',
major: 1,
minor: 7,
dot: 8,
codeName: 'enthusiastic-oblation'
dot: 9,
codeName: 'pollution-eradication'
};
@ -2959,7 +2961,7 @@ function publishExternalAPI(angular) {
});
}
])
.info({ angularVersion: '1.7.8' });
.info({ angularVersion: '1.7.9' });
}
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
@ -7460,7 +7462,7 @@ function $TemplateCacheProvider() {
*
* This example show how you might use `$doCheck` to trigger changes in your component's inputs even if the
* actual identity of the component doesn't change. (Be aware that cloning and deep equality checks on large
* arrays or objects can have a negative impact on your application performance)
* arrays or objects can have a negative impact on your application performance.)
*
* <example name="doCheckArrayExample" module="do-check-module">
* <file name="index.html">
@ -7783,7 +7785,7 @@ function $TemplateCacheProvider() {
* would result in the whole app "stalling" until all templates are loaded asynchronously - even in the
* case when only one deeply nested directive has `templateUrl`.
*
* Template loading is asynchronous even if the template has been preloaded into the {@link $templateCache}
* Template loading is asynchronous even if the template has been preloaded into the {@link $templateCache}.
*
* You can specify `templateUrl` as a string representing the URL or as a function which takes two
* arguments `tElement` and `tAttrs` (described in the `compile` function api below) and returns
@ -7844,7 +7846,7 @@ function $TemplateCacheProvider() {
* own templates or compile functions. Compiling these directives results in an infinite loop and
* stack overflow errors.
*
* This can be avoided by manually using $compile in the postLink function to imperatively compile
* This can be avoided by manually using `$compile` in the postLink function to imperatively compile
* a directive's template instead of relying on automatic template compilation via `template` or
* `templateUrl` declaration or manual compilation inside the compile function.
* </div>
@ -7948,17 +7950,17 @@ function $TemplateCacheProvider() {
*
* * `true` - transclude the content (i.e. the child nodes) of the directive's element.
* * `'element'` - transclude the whole of the directive's element including any directives on this
* element that defined at a lower priority than this directive. When used, the `template`
* element that are defined at a lower priority than this directive. When used, the `template`
* property is ignored.
* * **`{...}` (an object hash):** - map elements of the content onto transclusion "slots" in the template.
*
* **Mult-slot transclusion** is declared by providing an object for the `transclude` property.
* **Multi-slot transclusion** is declared by providing an object for the `transclude` property.
*
* This object is a map where the keys are the name of the slot to fill and the value is an element selector
* used to match the HTML to the slot. The element selector should be in normalized form (e.g. `myElement`)
* and will match the standard element variants (e.g. `my-element`, `my:element`, `data-my-element`, etc).
*
* For further information check out the guide on {@link guide/directive#matching-directives Matching Directives}
* For further information check out the guide on {@link guide/directive#matching-directives Matching Directives}.
*
* If the element selector is prefixed with a `?` then that slot is optional.
*
@ -7983,7 +7985,7 @@ function $TemplateCacheProvider() {
* </div>
*
* If you want to manually control the insertion and removal of the transcluded content in your directive
* then you must use this transclude function. When you call a transclude function it returns a a jqLite/JQuery
* then you must use this transclude function. When you call a transclude function it returns a jqLite/JQuery
* object that contains the compiled DOM, which is linked to the correct transclusion scope.
*
* When you call a transclusion function you can pass in a **clone attach function**. This function accepts
@ -8068,8 +8070,8 @@ function $TemplateCacheProvider() {
* The {@link ng.$compile.directive.Attributes Attributes} object - passed as a parameter in the
* `link()` or `compile()` functions. It has a variety of uses.
*
* * *Accessing normalized attribute names:* Directives like 'ngBind' can be expressed in many ways:
* 'ng:bind', `data-ng-bind`, or 'x-ng-bind'. The attributes object allows for normalized access
* * *Accessing normalized attribute names:* Directives like `ngBind` can be expressed in many ways:
* `ng:bind`, `data-ng-bind`, or `x-ng-bind`. The attributes object allows for normalized access
* to the attributes.
*
* * *Directive inter-communication:* All directives share the same instance of the attributes
@ -8110,25 +8112,24 @@ function $TemplateCacheProvider() {
<file name="index.html">
<script>
angular.module('compileExample', [], function($compileProvider) {
// configure new 'compile' directive by passing a directive
// factory function. The factory function injects the '$compile'
// Configure new 'compile' directive by passing a directive
// factory function. The factory function injects '$compile'.
$compileProvider.directive('compile', function($compile) {
// directive factory creates a link function
// The directive factory creates a link function.
return function(scope, element, attrs) {
scope.$watch(
function(scope) {
// watch the 'compile' expression for changes
// Watch the 'compile' expression for changes.
return scope.$eval(attrs.compile);
},
function(value) {
// when the 'compile' expression changes
// assign it into the current DOM
// When the 'compile' expression changes
// assign it into the current DOM.
element.html(value);
// compile the new DOM and link it to the current
// scope.
// NOTE: we only compile .childNodes so that
// we don't get into infinite loop compiling ourselves
// Compile the new DOM and link it to the current scope.
// NOTE: we only compile '.childNodes' so that we
// don't get into an infinite loop compiling ourselves.
$compile(element.contents())(scope);
}
);
@ -8201,13 +8202,13 @@ function $TemplateCacheProvider() {
* }
* ```
* * `futureParentElement` - defines the parent to which the `cloneAttachFn` will add
* the cloned elements; only needed for transcludes that are allowed to contain non html
* elements (e.g. SVG elements). See also the directive.controller property.
* the cloned elements; only needed for transcludes that are allowed to contain non HTML
* elements (e.g. SVG elements). See also the `directive.controller` property.
*
* Calling the linking function returns the element of the template. It is either the original
* element passed in, or the clone of the element if the `cloneAttachFn` is provided.
*
* After linking the view is not updated until after a call to $digest which typically is done by
* After linking the view is not updated until after a call to `$digest`, which typically is done by
* AngularJS automatically.
*
* If you need access to the bound view, there are two ways to do it:
@ -8215,21 +8216,23 @@ function $TemplateCacheProvider() {
* - If you are not asking the linking function to clone the template, create the DOM element(s)
* before you send them to the compiler and keep this reference around.
* ```js
* var element = $compile('<p>{{total}}</p>')(scope);
* var element = angular.element('<p>{{total}}</p>');
* $compile(element)(scope);
* ```
*
* - if on the other hand, you need the element to be cloned, the view reference from the original
* example would not point to the clone, but rather to the original template that was cloned. In
* this case, you can access the clone via the cloneAttachFn:
* this case, you can access the clone either via the `cloneAttachFn` or the value returned by the
* linking function:
* ```js
* var templateElement = angular.element('<p>{{total}}</p>'),
* scope = ....;
*
* var templateElement = angular.element('<p>{{total}}</p>');
* var clonedElement = $compile(templateElement)(scope, function(clonedElement, scope) {
* //attach the clone to DOM document at the right place
* // Attach the clone to DOM document at the right place.
* });
*
* //now we have reference to the cloned DOM via `clonedElement`
* // Now we have reference to the cloned DOM via `clonedElement`.
* // NOTE: The `clonedElement` returned by the linking function is the same as the
* // `clonedElement` passed to `cloneAttachFn`.
* ```
*
*
@ -8755,9 +8758,9 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
* @description
* Register a new directive with the compiler.
*
* @param {string|Object} name Name of the directive in camel-case (i.e. <code>ngBind</code> which
* will match as <code>ng-bind</code>), or an object map of directives where the keys are the
* names and the values are the factories.
* @param {string|Object} name Name of the directive in camel-case (i.e. `ngBind` which will match
* as `ng-bind`), or an object map of directives where the keys are the names and the values
* are the factories.
* @param {Function|Array} directiveFactory An injectable directive factory function. See the
* {@link guide/directive directive guide} and the {@link $compile compile API} for more info.
* @returns {ng.$compileProvider} Self for chaining.
@ -34424,14 +34427,7 @@ var ngHideDirective = ['$animate', function($animate) {
var ngStyleDirective = ngDirective(function(scope, element, attr) {
scope.$watchCollection(attr.ngStyle, function ngStyleWatchAction(newStyles, oldStyles) {
if (oldStyles && (newStyles !== oldStyles)) {
if (!newStyles) {
newStyles = {};
}
forEach(oldStyles, function(val, style) {
if (newStyles[style] == null) {
newStyles[style] = '';
}
});
forEach(oldStyles, function(val, style) { element.css(style, ''); });
}
if (newStyles) element.css(newStyles);
});

View File

@ -1,12 +1,12 @@
/*
AngularJS v1.7.8
AngularJS v1.7.9
(c) 2010-2018 Google, Inc. http://angularjs.org
License: MIT
*/
(function(C){'use strict';function re(a){if(D(a))w(a.objectMaxDepth)&&(Wb.objectMaxDepth=Xb(a.objectMaxDepth)?a.objectMaxDepth:NaN),w(a.urlErrorParamsEnabled)&&Ga(a.urlErrorParamsEnabled)&&(Wb.urlErrorParamsEnabled=a.urlErrorParamsEnabled);else return Wb}function Xb(a){return W(a)&&0<a}function F(a,b){b=b||Error;return function(){var d=arguments[0],c;c="["+(a?a+":":"")+d+"] http://errors.angularjs.org/1.7.8/"+(a?a+"/":"")+d;for(d=1;d<arguments.length;d++){c=c+(1==d?"?":"&")+"p"+(d-1)+"=";var e=encodeURIComponent,
(function(C){'use strict';function re(a){if(D(a))w(a.objectMaxDepth)&&(Wb.objectMaxDepth=Xb(a.objectMaxDepth)?a.objectMaxDepth:NaN),w(a.urlErrorParamsEnabled)&&Ga(a.urlErrorParamsEnabled)&&(Wb.urlErrorParamsEnabled=a.urlErrorParamsEnabled);else return Wb}function Xb(a){return W(a)&&0<a}function F(a,b){b=b||Error;return function(){var d=arguments[0],c;c="["+(a?a+":":"")+d+"] http://errors.angularjs.org/1.7.9/"+(a?a+"/":"")+d;for(d=1;d<arguments.length;d++){c=c+(1==d?"?":"&")+"p"+(d-1)+"=";var e=encodeURIComponent,
f;f=arguments[d];f="function"==typeof f?f.toString().replace(/ \{[\s\S]*$/,""):"undefined"==typeof f?"undefined":"string"!=typeof f?JSON.stringify(f):f;c+=e(f)}return new b(c)}}function ya(a){if(null==a||$a(a))return!1;if(H(a)||A(a)||x&&a instanceof x)return!0;var b="length"in Object(a)&&a.length;return W(b)&&(0<=b&&b-1 in a||"function"===typeof a.item)}function r(a,b,d){var c,e;if(a)if(B(a))for(c in a)"prototype"!==c&&"length"!==c&&"name"!==c&&a.hasOwnProperty(c)&&b.call(d,a[c],c,a);else if(H(a)||
ya(a)){var f="object"!==typeof a;c=0;for(e=a.length;c<e;c++)(f||c in a)&&b.call(d,a[c],c,a)}else if(a.forEach&&a.forEach!==r)a.forEach(b,d,a);else if(Nc(a))for(c in a)b.call(d,a[c],c,a);else if("function"===typeof a.hasOwnProperty)for(c in a)a.hasOwnProperty(c)&&b.call(d,a[c],c,a);else for(c in a)ta.call(a,c)&&b.call(d,a[c],c,a);return a}function Oc(a,b,d){for(var c=Object.keys(a).sort(),e=0;e<c.length;e++)b.call(d,a[c[e]],c[e]);return c}function Yb(a){return function(b,d){a(d,b)}}function se(){return++pb}
function Zb(a,b,d){for(var c=a.$$hashKey,e=0,f=b.length;e<f;++e){var g=b[e];if(D(g)||B(g))for(var k=Object.keys(g),h=0,l=k.length;h<l;h++){var m=k[h],p=g[m];d&&D(p)?ha(p)?a[m]=new Date(p.valueOf()):ab(p)?a[m]=new RegExp(p):p.nodeName?a[m]=p.cloneNode(!0):$b(p)?a[m]=p.clone():(D(a[m])||(a[m]=H(p)?[]:{}),Zb(a[m],[p],!0)):a[m]=p}}c?a.$$hashKey=c:delete a.$$hashKey;return a}function S(a){return Zb(a,Ha.call(arguments,1),!1)}function te(a){return Zb(a,Ha.call(arguments,1),!0)}function fa(a){return parseInt(a,
function Zb(a,b,d){for(var c=a.$$hashKey,e=0,f=b.length;e<f;++e){var g=b[e];if(D(g)||B(g))for(var k=Object.keys(g),h=0,l=k.length;h<l;h++){var m=k[h],p=g[m];d&&D(p)?ha(p)?a[m]=new Date(p.valueOf()):ab(p)?a[m]=new RegExp(p):p.nodeName?a[m]=p.cloneNode(!0):$b(p)?a[m]=p.clone():"__proto__"!==m&&(D(a[m])||(a[m]=H(p)?[]:{}),Zb(a[m],[p],!0)):a[m]=p}}c?a.$$hashKey=c:delete a.$$hashKey;return a}function S(a){return Zb(a,Ha.call(arguments,1),!1)}function te(a){return Zb(a,Ha.call(arguments,1),!0)}function fa(a){return parseInt(a,
10)}function ac(a,b){return S(Object.create(a),b)}function E(){}function Ta(a){return a}function ia(a){return function(){return a}}function bc(a){return B(a.toString)&&a.toString!==la}function z(a){return"undefined"===typeof a}function w(a){return"undefined"!==typeof a}function D(a){return null!==a&&"object"===typeof a}function Nc(a){return null!==a&&"object"===typeof a&&!Pc(a)}function A(a){return"string"===typeof a}function W(a){return"number"===typeof a}function ha(a){return"[object Date]"===la.call(a)}
function H(a){return Array.isArray(a)||a instanceof Array}function cc(a){switch(la.call(a)){case "[object Error]":return!0;case "[object Exception]":return!0;case "[object DOMException]":return!0;default:return a instanceof Error}}function B(a){return"function"===typeof a}function ab(a){return"[object RegExp]"===la.call(a)}function $a(a){return a&&a.window===a}function bb(a){return a&&a.$evalAsync&&a.$watch}function Ga(a){return"boolean"===typeof a}function ue(a){return a&&W(a.length)&&ve.test(la.call(a))}
function $b(a){return!(!a||!(a.nodeName||a.prop&&a.attr&&a.find))}function we(a){var b={};a=a.split(",");var d;for(d=0;d<a.length;d++)b[a[d]]=!0;return b}function ua(a){return K(a.nodeName||a[0]&&a[0].nodeName)}function cb(a,b){var d=a.indexOf(b);0<=d&&a.splice(d,1);return d}function Ia(a,b,d){function c(a,b,c){c--;if(0>c)return"...";var d=b.$$hashKey,f;if(H(a)){f=0;for(var g=a.length;f<g;f++)b.push(e(a[f],c))}else if(Nc(a))for(f in a)b[f]=e(a[f],c);else if(a&&"function"===typeof a.hasOwnProperty)for(f in a)a.hasOwnProperty(f)&&
@ -29,7 +29,7 @@ c=F("ng");a=b(a,"angular",Object);a.$$minErr=a.$$minErr||F;return b(a,"module",f
bootstrap:Uc,copy:Ia,extend:S,merge:te,equals:va,element:x,forEach:r,injector:fb,noop:E,bind:Va,toJson:eb,fromJson:Rc,identity:Ta,isUndefined:z,isDefined:w,isString:A,isFunction:B,isObject:D,isNumber:W,isElement:$b,isArray:H,version:Ke,isDate:ha,callbacks:{$$counter:0},getTestability:De,reloadWithDebugInfo:Ce,$$minErr:F,$$csp:Aa,$$encodeUriSegment:hc,$$encodeUriQuery:ba,$$lowercase:K,$$stringify:ic,$$uppercase:ub});kc=He(C);kc("ng",["ngLocale"],["$provide",function(a){a.provider({$$sanitizeUri:Le});
a.provider("$compile",Xc).directive({a:Me,input:Yc,textarea:Yc,form:Ne,script:Oe,select:Pe,option:Qe,ngBind:Re,ngBindHtml:Se,ngBindTemplate:Te,ngClass:Ue,ngClassEven:Ve,ngClassOdd:We,ngCloak:Xe,ngController:Ye,ngForm:Ze,ngHide:$e,ngIf:af,ngInclude:bf,ngInit:cf,ngNonBindable:df,ngPluralize:ef,ngRef:ff,ngRepeat:gf,ngShow:hf,ngStyle:jf,ngSwitch:kf,ngSwitchWhen:lf,ngSwitchDefault:mf,ngOptions:nf,ngTransclude:of,ngModel:pf,ngList:qf,ngChange:rf,pattern:Zc,ngPattern:Zc,required:$c,ngRequired:$c,minlength:ad,
ngMinlength:ad,maxlength:bd,ngMaxlength:bd,ngValue:sf,ngModelOptions:tf}).directive({ngInclude:uf,input:vf}).directive(vb).directive(cd);a.provider({$anchorScroll:wf,$animate:xf,$animateCss:yf,$$animateJs:zf,$$animateQueue:Af,$$AnimateRunner:Bf,$$animateAsyncRun:Cf,$browser:Df,$cacheFactory:Ef,$controller:Ff,$document:Gf,$$isDocumentHidden:Hf,$exceptionHandler:If,$filter:dd,$$forceReflow:Jf,$interpolate:Kf,$interval:Lf,$$intervalFactory:Mf,$http:Nf,$httpParamSerializer:Of,$httpParamSerializerJQLike:Pf,
$httpBackend:Qf,$xhrFactory:Rf,$jsonpCallbacks:Sf,$location:Tf,$log:Uf,$parse:Vf,$rootScope:Wf,$q:Xf,$$q:Yf,$sce:Zf,$sceDelegate:$f,$sniffer:ag,$$taskTrackerFactory:bg,$templateCache:cg,$templateRequest:dg,$$testability:eg,$timeout:fg,$window:gg,$$rAF:hg,$$jqLite:ig,$$Map:jg,$$cookieReader:kg})}]).info({angularVersion:"1.7.8"})}function wb(a,b){return b.toUpperCase()}function xb(a){return a.replace(lg,wb)}function lc(a){a=a.nodeType;return 1===a||!a||9===a}function ed(a,b){var d,c,e=b.createDocumentFragment(),
$httpBackend:Qf,$xhrFactory:Rf,$jsonpCallbacks:Sf,$location:Tf,$log:Uf,$parse:Vf,$rootScope:Wf,$q:Xf,$$q:Yf,$sce:Zf,$sceDelegate:$f,$sniffer:ag,$$taskTrackerFactory:bg,$templateCache:cg,$templateRequest:dg,$$testability:eg,$timeout:fg,$window:gg,$$rAF:hg,$$jqLite:ig,$$Map:jg,$$cookieReader:kg})}]).info({angularVersion:"1.7.9"})}function wb(a,b){return b.toUpperCase()}function xb(a){return a.replace(lg,wb)}function lc(a){a=a.nodeType;return 1===a||!a||9===a}function ed(a,b){var d,c,e=b.createDocumentFragment(),
f=[];if(mc.test(a)){d=e.appendChild(b.createElement("div"));c=(mg.exec(a)||["",""])[1].toLowerCase();c=oa[c]||oa._default;d.innerHTML=c[1]+a.replace(ng,"<$1></$2>")+c[2];for(c=c[0];c--;)d=d.lastChild;f=db(f,d.childNodes);d=e.firstChild;d.textContent=""}else f.push(b.createTextNode(a));e.textContent="";e.innerHTML="";r(f,function(a){e.appendChild(a)});return e}function Y(a){if(a instanceof Y)return a;var b;A(a)&&(a=U(a),b=!0);if(!(this instanceof Y)){if(b&&"<"!==a.charAt(0))throw nc("nosel");return new Y(a)}if(b){b=
C.document;var d;a=(d=og.exec(a))?[b.createElement(d[1])]:(d=ed(a,b))?d.childNodes:[];oc(this,a)}else B(a)?fd(a):oc(this,a)}function pc(a){return a.cloneNode(!0)}function yb(a,b){!b&&lc(a)&&x.cleanData([a]);a.querySelectorAll&&x.cleanData(a.querySelectorAll("*"))}function gd(a){for(var b in a)return!1;return!0}function hd(a){var b=a.ng339,d=b&&Ka[b],c=d&&d.events,d=d&&d.data;d&&!gd(d)||c&&!gd(c)||(delete Ka[b],a.ng339=void 0)}function id(a,b,d,c){if(w(c))throw nc("offargs");var e=(c=zb(a))&&c.events,
f=c&&c.handle;if(f){if(b){var g=function(b){var c=e[b];w(d)&&cb(c||[],d);w(d)&&c&&0<c.length||(a.removeEventListener(b,f),delete e[b])};r(b.split(" "),function(a){g(a);Ab[a]&&g(Ab[a])})}else for(b in e)"$destroy"!==b&&a.removeEventListener(b,f),delete e[b];hd(a)}}function qc(a,b){var d=a.ng339;if(d=d&&Ka[d])b?delete d.data[b]:d.data={},hd(a)}function zb(a,b){var d=a.ng339,d=d&&Ka[d];b&&!d&&(a.ng339=d=++pg,d=Ka[d]={events:{},data:{},handle:void 0});return d}function rc(a,b,d){if(lc(a)){var c,e=w(d),
@ -201,7 +201,7 @@ this.$$element=c;this.$$animate=f;this.$$timeout=g;this.$$parse=e;this.$$q=k;thi
b,a,za(d));return a}}function Tb(a){a=fa(a);return X(a)?-1:a}var Wb={objectMaxDepth:5,urlErrorParamsEnabled:!0},ie=/^\/(.+)\/([a-z]*)$/,ta=Object.prototype.hasOwnProperty,K=function(a){return A(a)?a.toLowerCase():a},ub=function(a){return A(a)?a.toUpperCase():a},Ca,x,rb,Ha=[].slice,Fg=[].splice,kh=[].push,la=Object.prototype.toString,Pc=Object.getPrototypeOf,pa=F("ng"),ca=C.angular||(C.angular={}),kc,pb=0;Ca=C.document.documentMode;var X=Number.isNaN||function(a){return a!==a};E.$inject=[];Ta.$inject=
[];var ve=/^\[object (?:Uint8|Uint8Clamped|Uint16|Uint32|Int8|Int16|Int32|Float32|Float64)Array]$/,U=function(a){return A(a)?a.trim():a},Md=function(a){return a.replace(/([-()[\]{}+?*.$^|,:#<!\\])/g,"\\$1").replace(/\x08/g,"\\x08")},Aa=function(){if(!w(Aa.rules)){var a=C.document.querySelector("[ng-csp]")||C.document.querySelector("[data-ng-csp]");if(a){var b=a.getAttribute("ng-csp")||a.getAttribute("data-ng-csp");Aa.rules={noUnsafeEval:!b||-1!==b.indexOf("no-unsafe-eval"),noInlineStyle:!b||-1!==
b.indexOf("no-inline-style")}}else{a=Aa;try{new Function(""),b=!1}catch(d){b=!0}a.rules={noUnsafeEval:b,noInlineStyle:!1}}}return Aa.rules},qb=function(){if(w(qb.name_))return qb.name_;var a,b,d=Qa.length,c,e;for(b=0;b<d;++b)if(c=Qa[b],a=C.document.querySelector("["+c.replace(":","\\:")+"jq]")){e=a.getAttribute(c+"jq");break}return qb.name_=e},xe=/:/g,Qa=["ng-","data-ng-","ng:","x-ng-"],Be=function(a){var b=a.currentScript;if(!b)return!0;if(!(b instanceof C.HTMLScriptElement||b instanceof C.SVGScriptElement))return!1;
b=b.attributes;return[b.getNamedItem("src"),b.getNamedItem("href"),b.getNamedItem("xlink:href")].every(function(b){if(!b)return!0;if(!b.value)return!1;var c=a.createElement("a");c.href=b.value;if(a.location.origin===c.origin)return!0;switch(c.protocol){case "http:":case "https:":case "ftp:":case "blob:":case "file:":case "data:":return!0;default:return!1}})}(C.document),Ee=/[A-Z]/g,Wc=!1,Pa=3,Ke={full:"1.7.8",major:1,minor:7,dot:8,codeName:"enthusiastic-oblation"};Y.expando="ng339";var Ka=Y.cache=
b=b.attributes;return[b.getNamedItem("src"),b.getNamedItem("href"),b.getNamedItem("xlink:href")].every(function(b){if(!b)return!0;if(!b.value)return!1;var c=a.createElement("a");c.href=b.value;if(a.location.origin===c.origin)return!0;switch(c.protocol){case "http:":case "https:":case "ftp:":case "blob:":case "file:":case "data:":return!0;default:return!1}})}(C.document),Ee=/[A-Z]/g,Wc=!1,Pa=3,Ke={full:"1.7.9",major:1,minor:7,dot:9,codeName:"pollution-eradication"};Y.expando="ng339";var Ka=Y.cache=
{},pg=1;Y._data=function(a){return this.cache[a[this.expando]]||{}};var lg=/-([a-z])/g,lh=/^-ms-/,Ab={mouseleave:"mouseout",mouseenter:"mouseover"},nc=F("jqLite"),og=/^<([\w-]+)\s*\/?>(?:<\/\1>|)$/,mc=/<|&#?\w+;/,mg=/<([\w:-]+)/,ng=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:-]+)[^>]*)\/>/gi,oa={option:[1,'<select multiple="multiple">',"</select>"],thead:[1,"<table>","</table>"],col:[2,"<table><colgroup>","</colgroup></table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>",
"</tr></tbody></table>"],_default:[0,"",""]};oa.optgroup=oa.option;oa.tbody=oa.tfoot=oa.colgroup=oa.caption=oa.thead;oa.th=oa.td;var ug=C.Node.prototype.contains||function(a){return!!(this.compareDocumentPosition(a)&16)},Wa=Y.prototype={ready:fd,toString:function(){var a=[];r(this,function(b){a.push(""+b)});return"["+a.join(", ")+"]"},eq:function(a){return 0<=a?x(this[a]):x(this[this.length+a])},length:0,push:kh,sort:[].sort,splice:[].splice},Gb={};r("multiple selected checked disabled readOnly required open".split(" "),
function(a){Gb[K(a)]=a});var md={};r("input select option textarea button form details".split(" "),function(a){md[a]=!0});var td={ngMinlength:"minlength",ngMaxlength:"maxlength",ngMin:"min",ngMax:"max",ngPattern:"pattern",ngStep:"step"};r({data:rc,removeData:qc,hasData:function(a){for(var b in Ka[a.ng339])return!0;return!1},cleanData:function(a){for(var b=0,d=a.length;b<d;b++)qc(a[b]),id(a[b])}},function(a,b){Y[b]=a});r({data:rc,inheritedData:Eb,scope:function(a){return x.data(a,"$scope")||Eb(a.parentNode||
@ -328,8 +328,8 @@ a.$last);a.$odd=!(a.$even=0===(b&1))},f=function(a,b,c){return La(c)},g=function
n);var x=p[3]||p[1],v=p[2];if(w&&(!/^[$a-zA-Z_][$a-zA-Z0-9_]*$/.test(w)||/^(null|undefined|this|\$index|\$first|\$middle|\$last|\$even|\$odd|\$parent|\$root|\$id)$/.test(w)))throw c("badident",w);var z;if(t){var A={$id:La},y=a(t);z=function(a,b,c,d){v&&(A[v]=b);A[x]=c;A.$index=d;return y(a,A)}}return function(a,d,h,k,n){var p=T();a.$watchCollection(q,function(h){var k,q,t=d[0],s,y=T(),B,C,E,D,H,F,K;w&&(a[w]=h);if(ya(h))H=h,q=z||f;else for(K in q=z||g,H=[],h)ta.call(h,K)&&"$"!==K.charAt(0)&&H.push(K);
B=H.length;K=Array(B);for(k=0;k<B;k++)if(C=h===H?k:H[k],E=h[C],D=q(a,C,E,k),p[D])F=p[D],delete p[D],y[D]=F,K[k]=F;else{if(y[D])throw r(K,function(a){a&&a.scope&&(p[a.id]=a)}),c("dupes",l,D,E);K[k]={id:D,scope:void 0,clone:void 0};y[D]=!0}A&&(A[x]=void 0);for(s in p){F=p[s];D=tb(F.clone);b.leave(D);if(D[0].parentNode)for(k=0,q=D.length;k<q;k++)D[k].$$NG_REMOVED=!0;F.scope.$destroy()}for(k=0;k<B;k++)if(C=h===H?k:H[k],E=h[C],F=K[k],F.scope){s=t;do s=s.nextSibling;while(s&&s.$$NG_REMOVED);F.clone[0]!==
s&&b.move(tb(F.clone),null,t);t=F.clone[F.clone.length-1];e(F.scope,k,x,E,v,C,B)}else n(function(a,c){F.scope=c;var d=m.cloneNode(!1);a[a.length++]=d;b.enter(a,null,t);t=d;F.clone=a;y[F.id]=F;e(F.scope,k,x,E,v,C,B)});p=y})}}}}],hf=["$animate",function(a){return{restrict:"A",multiElement:!0,link:function(b,d,c){b.$watch(c.ngShow,function(b){a[b?"removeClass":"addClass"](d,"ng-hide",{tempClasses:"ng-hide-animate"})})}}}],$e=["$animate",function(a){return{restrict:"A",multiElement:!0,link:function(b,
d,c){b.$watch(c.ngHide,function(b){a[b?"addClass":"removeClass"](d,"ng-hide",{tempClasses:"ng-hide-animate"})})}}}],jf=Ra(function(a,b,d){a.$watchCollection(d.ngStyle,function(a,d){d&&a!==d&&(a||(a={}),r(d,function(b,d){null==a[d]&&(a[d]="")}));a&&b.css(a)})}),kf=["$animate","$compile",function(a,b){return{require:"ngSwitch",controller:["$scope",function(){this.cases={}}],link:function(d,c,e,f){var g=[],k=[],h=[],l=[],m=function(a,b){return function(c){!1!==c&&a.splice(b,1)}};d.$watch(e.ngSwitch||
e.on,function(c){for(var d,e;h.length;)a.cancel(h.pop());d=0;for(e=l.length;d<e;++d){var q=tb(k[d].clone);l[d].$destroy();(h[d]=a.leave(q)).done(m(h,d))}k.length=0;l.length=0;(g=f.cases["!"+c]||f.cases["?"])&&r(g,function(c){c.transclude(function(d,e){l.push(e);var f=c.element;d[d.length++]=b.$$createComment("end ngSwitchWhen");k.push({clone:d});a.enter(d,f.parent(),f)})})})}}}],lf=Ra({transclude:"element",priority:1200,require:"^ngSwitch",multiElement:!0,link:function(a,b,d,c,e){a=d.ngSwitchWhen.split(d.ngSwitchWhenSeparator).sort().filter(function(a,
d,c){b.$watch(c.ngHide,function(b){a[b?"addClass":"removeClass"](d,"ng-hide",{tempClasses:"ng-hide-animate"})})}}}],jf=Ra(function(a,b,d){a.$watchCollection(d.ngStyle,function(a,d){d&&a!==d&&r(d,function(a,c){b.css(c,"")});a&&b.css(a)})}),kf=["$animate","$compile",function(a,b){return{require:"ngSwitch",controller:["$scope",function(){this.cases={}}],link:function(d,c,e,f){var g=[],k=[],h=[],l=[],m=function(a,b){return function(c){!1!==c&&a.splice(b,1)}};d.$watch(e.ngSwitch||e.on,function(c){for(var d,
e;h.length;)a.cancel(h.pop());d=0;for(e=l.length;d<e;++d){var q=tb(k[d].clone);l[d].$destroy();(h[d]=a.leave(q)).done(m(h,d))}k.length=0;l.length=0;(g=f.cases["!"+c]||f.cases["?"])&&r(g,function(c){c.transclude(function(d,e){l.push(e);var f=c.element;d[d.length++]=b.$$createComment("end ngSwitchWhen");k.push({clone:d});a.enter(d,f.parent(),f)})})})}}}],lf=Ra({transclude:"element",priority:1200,require:"^ngSwitch",multiElement:!0,link:function(a,b,d,c,e){a=d.ngSwitchWhen.split(d.ngSwitchWhenSeparator).sort().filter(function(a,
b,c){return c[b-1]!==a});r(a,function(a){c.cases["!"+a]=c.cases["!"+a]||[];c.cases["!"+a].push({transclude:e,element:b})})}}),mf=Ra({transclude:"element",priority:1200,require:"^ngSwitch",multiElement:!0,link:function(a,b,d,c,e){c.cases["?"]=c.cases["?"]||[];c.cases["?"].push({transclude:e,element:b})}}),zh=F("ngTransclude"),of=["$compile",function(a){return{restrict:"EAC",compile:function(b){var d=a(b.contents());b.empty();return function(a,b,f,g,k){function h(){d(a,function(a){b.append(a)})}if(!k)throw zh("orphan",
za(b));f.ngTransclude===f.$attr.ngTransclude&&(f.ngTransclude="");f=f.ngTransclude||f.ngTranscludeSlot;k(function(a,c){var d;if(d=a.length)a:{d=0;for(var f=a.length;d<f;d++){var g=a[d];if(g.nodeType!==Pa||g.nodeValue.trim()){d=!0;break a}}d=void 0}d?b.append(a):(h(),c.$destroy())},null,f);f&&!k.isSlotFilled(f)&&h()}}}}],Oe=["$templateCache",function(a){return{restrict:"E",terminal:!0,compile:function(b,d){"text/ng-template"===d.type&&a.put(d.id,b[0].text)}}}],Ah={$setViewValue:E,$render:E},Bh=["$element",
"$scope",function(a,b){function d(){g||(g=!0,b.$$postDigest(function(){g=!1;e.ngModelCtrl.$render()}))}function c(a){k||(k=!0,b.$$postDigest(function(){b.$$destroyed||(k=!1,e.ngModelCtrl.$setViewValue(e.readValue()),a&&e.ngModelCtrl.$render())}))}var e=this,f=new Hb;e.selectValueMap={};e.ngModelCtrl=Ah;e.multiple=!1;e.unknownOption=x(C.document.createElement("option"));e.hasEmptyOption=!1;e.emptyOption=void 0;e.renderUnknownOption=function(b){b=e.generateUnknownOptionValue(b);e.unknownOption.val(b);

File diff suppressed because one or more lines are too long

View File

@ -57,6 +57,14 @@ function templates(tpl,key) {
"id" : "casAppMetaDataOptions",
"title" : "casAppMetaDataOptions",
"type" : "simpleInputContainer"
},
{
"cnodes" : tpl+"s/"+key+"/"+"casAppMetaDataMacros",
"default" : [],
"help" : "exportedvars.html#extend_variables_using_macros_and_groups",
"id" : tpl+"s/"+key+"/"+"casAppMetaDataMacros",
"title" : "casAppMetaDataMacros",
"type" : "keyTextContainer"
}
]
;
@ -579,6 +587,14 @@ function templates(tpl,key) {
"id" : "oidcRPMetaDataOptions",
"title" : "oidcRPMetaDataOptions"
},
{
"cnodes" : tpl+"s/"+key+"/"+"oidcRPMetaDataMacros",
"default" : [],
"help" : "exportedvars.html#extend_variables_using_macros_and_groups",
"id" : tpl+"s/"+key+"/"+"oidcRPMetaDataMacros",
"title" : "oidcRPMetaDataMacros",
"type" : "keyTextContainer"
},
{
"_nodes" : [
{
@ -1120,6 +1136,14 @@ function templates(tpl,key) {
"help" : "idpsaml.html#options",
"id" : "samlSPMetaDataOptions",
"title" : "samlSPMetaDataOptions"
},
{
"cnodes" : tpl+"s/"+key+"/"+"samlSPMetaDataMacros",
"default" : [],
"help" : "exportedvars.html#extend_variables_using_macros_and_groups",
"id" : tpl+"s/"+key+"/"+"samlSPMetaDataMacros",
"title" : "samlSPMetaDataMacros",
"type" : "keyTextContainer"
}
]
;

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,4 +1,4 @@
// Generated by CoffeeScript 1.12.7
// Generated by CoffeeScript 1.12.8
/*
LemonLDAP::NG Manager client
@ -23,7 +23,7 @@ This file contains:
llapp.controller('TreeCtrl', [
'$scope', '$http', '$location', '$q', '$uibModal', '$translator', '$cookies', '$htmlParams', function($scope, $http, $location, $q, $uibModal, $translator, $cookies, $htmlParams) {
var _checkSaveResponse, _download, _getAll, _stoggle, c, id, pathEvent, readError, setDefault, setHelp;
var _checkSaveResponse, _download, _getAll, _stoggle, c, idinc, pathEvent, readError, setDefault, setHelp;
$scope.links = window.links;
$scope.menu = $htmlParams.menu;
$scope.menulinks = window.menulinks;
@ -251,7 +251,7 @@ This file contains:
$scope.currentNode.data = null;
return $scope.getKey($scope.currentNode);
};
id = 1;
idinc = 1;
$scope._findContainer = function() {
return $scope._findScopeContainer().$modelValue;
};
@ -277,7 +277,7 @@ This file contains:
l = node.nodes.length;
n = l > 0 ? l - 1 : 0;
return node.nodes.push({
id: node.id + "/n" + (id++),
id: node.id + "/n" + (idinc++),
title: 'New rule',
re: 'Message',
comment: 'New rule',
@ -291,7 +291,7 @@ This file contains:
l = node.nodes.length;
n = l > 0 ? l - 1 : 0;
return node.nodes.splice(n, 0, {
id: node.id + "/n" + (id++),
id: node.id + "/n" + (idinc++),
title: 'New rule',
re: '^/new',
comment: 'New rule',
@ -303,7 +303,7 @@ This file contains:
var node;
node = $scope._findContainer();
return node.nodes.push({
id: node.id + "/n" + (id++),
id: node.id + "/n" + (idinc++),
title: "/absolute/path/to/form",
data: {},
type: "post"
@ -319,7 +319,7 @@ This file contains:
var node;
node = $scope._findContainer();
node.nodes.push({
id: node.id + "/n" + (id++),
id: node.id + "/n" + (idinc++),
title: "1_Key",
data: ['Null', 'Null', 'Null'],
type: "authChoice"
@ -330,7 +330,7 @@ This file contains:
var node;
node = $scope._findContainer();
return node.nodes.push({
id: node.id + "/n" + (id++),
id: node.id + "/n" + (idinc++),
title: 'new',
data: '',
type: "keyText"
@ -343,7 +343,7 @@ This file contains:
cs = cs.$parentNodeScope;
}
return cs.$modelValue.nodes.push({
id: cs.$modelValue.id + "/n" + (id++),
id: cs.$modelValue.id + "/n" + (idinc++),
title: "New category",
type: "menuCat",
nodes: []
@ -356,7 +356,7 @@ This file contains:
cs = cs.$parentNodeScope;
}
return cs.$modelValue.nodes.push({
id: cs.$modelValue.id + "/n" + (id++),
id: cs.$modelValue.id + "/n" + (idinc++),
title: "New application",
type: "menuApp",
data: {
@ -371,7 +371,7 @@ This file contains:
var node;
node = $scope._findContainer();
node.nodes.push({
id: node.id + "/n" + (id++),
id: node.id + "/n" + (idinc++),
title: 'new',
type: 'cmbModule',
data: {
@ -386,7 +386,7 @@ This file contains:
var node;
node = $scope._findContainer();
return node.nodes.push({
id: node.id + "/n" + (id++),
id: node.id + "/n" + (idinc++),
title: 'new',
type: 'sfExtra',
data: {
@ -404,7 +404,7 @@ This file contains:
if (!d.over) {
d.over = [];
}
return d.over.push(["new" + (id++), '']);
return d.over.push(["new" + (idinc++), '']);
};
$scope.newCmbOver = function() {
var d;
@ -412,7 +412,7 @@ This file contains:
if (!d.over) {
d.over = [];
}
return d.over.push(["new" + (id++), '']);
return d.over.push(["new" + (idinc++), '']);
};
$scope.newChoiceOver = function() {
var d;
@ -421,7 +421,7 @@ This file contains:
if (!d[5]) {
d[5] = [];
}
return d[5].push(["new" + (id++), '']);
return d[5].push(["new" + (idinc++), '']);
};
$scope.addHost = function() {
var cn;
@ -443,7 +443,7 @@ This file contains:
var node;
node = $scope._findContainer();
return node.nodes.push({
id: node.id + "/n" + (id++),
id: node.id + "/n" + (idinc++),
title: 'new',
type: 'samlAttribute',
data: ['0', 'New', '', '']
@ -600,7 +600,7 @@ This file contains:
return $scope.displayForm(p);
};
$scope.down = function() {
var i, ind, len, n, o, p, ref, tmp;
var i, id, ind, len, n, o, p, ref, tmp;
id = $scope.currentNode.id;
p = $scope.currentScope.$parentNodeScope.$modelValue;
ind = p.nodes.length;
@ -619,7 +619,7 @@ This file contains:
return ind;
};
$scope.up = function() {
var i, ind, len, n, o, p, ref, tmp;
var i, id, ind, len, n, o, p, ref, tmp;
id = $scope.currentNode.id;
p = $scope.currentScope.$parentNodeScope.$modelValue;
ind = -1;

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -118,6 +118,7 @@
"casAppMetaDataOptions":"خيارات",
"casAppMetaDataOptionsService":"خدمة أل يو أر ل",
"casAppMetaDataOptionsRule":"القاعدة",
"casAppMetaDataMacros":"ماكرو",
"casAppMetaDataOptionsUserAttribute":"خاصّيّة المستخدم",
"casAppName":"اسم التطبيق كاس",
"casAttr":"تسجيل الدخول كاس",
@ -221,7 +222,7 @@
"customPassword":"وحدة كلمة المرورالمخصصة",
"customPlugins":"Modules list",
"customPluginsNode":"Custom plugins",
"customPluginsParams":"Additional parameters",
"customPluginsParams":"معايير إضافية",
"customPortalSkin":"غلاف البوابة مخصص",
"customRegister":"وحدة تسجيل مخصص",
"customToTrace":"REMOTE_CUSTOM",
@ -317,7 +318,7 @@
"friendlyName":"اسم مألوف",
"generalParameters":"المعاييرالعامة",
"globalLogout":"Global logout",
"globalLogoutRule":"Activation",
"globalLogoutRule":"تفعيل",
"globalLogoutTimer":"قبول تلقائي للوقت",
"globalStorage":"أباتشي :: وحدة الجلسة",
"globalStorageOptions":"أباتشي :: معايير وحدة الجلسة",
@ -572,6 +573,7 @@
"oidcRPMetaDataOptionsPublic":"Public client",
"oidcRPMetaDataOptionsRequirePKCE":"Require PKCE",
"oidcRPMetaDataOptionsRule":"قاعدة الدخول",
"oidcRPMetaDataMacros":"ماكرو",
"oidcOPMetaDataOptionsScope":"نطاق",
"oidcOPMetaDataOptionsStoreIDToken":"مخزن تعريف التوكن",
"oidcOPMetaDataOptionsTokenEndpointAuthMethod":"توكن نقطة النهاية لطريقة إثبات الهوية",
@ -1037,6 +1039,7 @@
"samlSPMetaDataOptionsNotOnOrAfterTimeout":"ليس على أو بعد المدة",
"samlSPMetaDataOptionsForceUTF8":"فرضUTF-8 ",
"samlSPMetaDataOptionsRule":"قاعدة الدخول",
"samlSPMetaDataMacros":"ماكرو",
"samlIDPName":"اسم SAML IDP",
"samlServiceMetaData":"خدمة 2 SAML",
"samlEntityID":"معرف الكيان",

View File

@ -118,6 +118,7 @@
"casAppMetaDataOptions":"Optionen",
"casAppMetaDataOptionsService":"Service URL",
"casAppMetaDataOptionsRule":"Regel",
"casAppMetaDataMacros":"Macros",
"casAppMetaDataOptionsUserAttribute":"User attribute",
"casAppName":"CAS App Name",
"casAttr":"CAS login",
@ -571,6 +572,7 @@
"oidcRPMetaDataOptionsPublic":"Public client",
"oidcRPMetaDataOptionsRequirePKCE":"Require PKCE",
"oidcRPMetaDataOptionsRule":"Access rule",
"oidcRPMetaDataMacros":"Macros",
"oidcOPMetaDataOptionsScope":"Scope",
"oidcOPMetaDataOptionsStoreIDToken":"Store ID Token",
"oidcOPMetaDataOptionsTokenEndpointAuthMethod":"Token endpoint authentication method",
@ -1036,6 +1038,7 @@
"samlSPMetaDataOptionsNotOnOrAfterTimeout":"notOnOrAfter duration",
"samlSPMetaDataOptionsForceUTF8":"Force UTF-8",
"samlSPMetaDataOptionsRule":"Access rule",
"samlSPMetaDataMacros":"Macros",
"samlIDPName":"SAML IDP Name",
"samlServiceMetaData":"SAML2 Service",
"samlEntityID":"Entity Identifier",

View File

@ -118,6 +118,7 @@
"casAppMetaDataOptions":"Options",
"casAppMetaDataOptionsService":"Service URL",
"casAppMetaDataOptionsRule":"Rule",
"casAppMetaDataMacros":"Macros",
"casAppMetaDataOptionsUserAttribute":"User attribute",
"casAppName":"CAS App Name",
"casAttr":"CAS login",
@ -571,6 +572,7 @@
"oidcRPMetaDataOptionsPublic":"Public client",
"oidcRPMetaDataOptionsRequirePKCE":"Require PKCE",
"oidcRPMetaDataOptionsRule":"Access rule",
"oidcRPMetaDataMacros":"Macros",
"oidcOPMetaDataOptionsScope":"Scope",
"oidcOPMetaDataOptionsStoreIDToken":"Store ID Token",
"oidcOPMetaDataOptionsTokenEndpointAuthMethod":"Token endpoint authentication method",
@ -1036,6 +1038,7 @@
"samlSPMetaDataOptionsNotOnOrAfterTimeout":"notOnOrAfter duration",
"samlSPMetaDataOptionsForceUTF8":"Force UTF-8",
"samlSPMetaDataOptionsRule":"Access rule",
"samlSPMetaDataMacros":"Macros",
"samlIDPName":"SAML IDP Name",
"samlServiceMetaData":"SAML2 Service",
"samlEntityID":"Entity Identifier",

View File

@ -118,6 +118,7 @@
"casAppMetaDataOptions":"Options",
"casAppMetaDataOptionsService":"URL du service",
"casAppMetaDataOptionsRule":"Règle",
"casAppMetaDataMacros":"Macros",
"casAppMetaDataOptionsUserAttribute":"Attribut de l'utilisateur",
"casAppName":"Nom de l'application CAS",
"casAttr":"Identifiant CAS",
@ -571,6 +572,7 @@
"oidcRPMetaDataOptionsPublic":"Client public",
"oidcRPMetaDataOptionsRequirePKCE":"PKCE requis",
"oidcRPMetaDataOptionsRule":"Règle d'accès",
"oidcRPMetaDataMacros":"Macros",
"oidcOPMetaDataOptionsScope":"Étendue",
"oidcOPMetaDataOptionsStoreIDToken":"Conserver le jeton d'identité",
"oidcOPMetaDataOptionsTokenEndpointAuthMethod":"Méthode d'authentification pour l'accès aux jetons",
@ -1036,6 +1038,7 @@
"samlSPMetaDataOptionsNotOnOrAfterTimeout":"Durée notOnOrAfter",
"samlSPMetaDataOptionsForceUTF8":"Forcer l'UTF-8",
"samlSPMetaDataOptionsRule":"Règle d'accès",
"samlSPMetaDataMacros":"Macros",
"samlIDPName":"Nom du fournisseur d'identité SAML",
"samlServiceMetaData":"Service SAML 2",
"samlEntityID":"Identifiant d'entité",

View File

@ -118,6 +118,7 @@
"casAppMetaDataOptions":"Opzioni",
"casAppMetaDataOptionsService":"URL del servizio",
"casAppMetaDataOptionsRule":"Regola",
"casAppMetaDataMacros":"Macro",
"casAppMetaDataOptionsUserAttribute":"Attributo utente",
"casAppName":"Nome App CAS",
"casAttr":"Login CAS",
@ -221,7 +222,7 @@
"customPassword":"Personalizza il modulo password",
"customPlugins":"Modules list",
"customPluginsNode":"Custom plugins",
"customPluginsParams":"Additional parameters",
"customPluginsParams":"Parametri aggiuntivi",
"customPortalSkin":"Personalizza faccia del portale ",
"customRegister":"Personalizza modulo di registro",
"customToTrace":"REMOTE_CUSTOM",
@ -316,7 +317,7 @@
"friendlyName":"Nome amichevole",
"generalParameters":"Parametri generali",
"globalLogout":"Global logout",
"globalLogoutRule":"Activation",
"globalLogoutRule":"Attivazione",
"globalLogoutTimer":"Auto accettazione tempo",
"globalStorage":"Modulo Apache::Session",
"globalStorageOptions":"Parametri di modulo Apache::Session",
@ -372,7 +373,7 @@
"issuerDBOpenIDConnectActivation":"Attivazione",
"issuerDBOpenIDConnectPath":"Path",
"issuerDBOpenIDConnectRule":"Utilizza la regola",
"issuerOptions":"Options",
"issuerOptions":"Opzioni",
"issuerParams":"Moduli emittenti",
"issuersTimeout":"Issuers timeout",
"jsRedirect":"Messaggio di reindirizzamento",
@ -571,6 +572,7 @@
"oidcRPMetaDataOptionsPublic":"Cliente pubblico",
"oidcRPMetaDataOptionsRequirePKCE":"Richiedi PKCE",
"oidcRPMetaDataOptionsRule":"Regola di accesso",
"oidcRPMetaDataMacros":"Macro",
"oidcOPMetaDataOptionsScope":"Scopo",
"oidcOPMetaDataOptionsStoreIDToken":"Immagazzina ID Token",
"oidcOPMetaDataOptionsTokenEndpointAuthMethod":"Metodo di autenticazione degli endpoint di token",
@ -1036,6 +1038,7 @@
"samlSPMetaDataOptionsNotOnOrAfterTimeout":"Durata di notOnOrAfter ",
"samlSPMetaDataOptionsForceUTF8":"Forza UTF-8",
"samlSPMetaDataOptionsRule":"Regola di accesso",
"samlSPMetaDataMacros":"Macro",
"samlIDPName":"Nome di SAML IDP ",
"samlServiceMetaData":"Servizio SAML 2",
"samlEntityID":"Identificatore dell'entità",

View File

@ -118,6 +118,7 @@
"casAppMetaDataOptions":"Seçenekler",
"casAppMetaDataOptionsService":"Servis URL'si",
"casAppMetaDataOptionsRule":"Kural",
"casAppMetaDataMacros":"Makrolar",
"casAppMetaDataOptionsUserAttribute":"Kullanıcı niteliği",
"casAppName":"CAS Uygulama Adı",
"casAttr":"CAS girişi",
@ -571,6 +572,7 @@
"oidcRPMetaDataOptionsPublic":"Açık istemci",
"oidcRPMetaDataOptionsRequirePKCE":"PKCE gerektir",
"oidcRPMetaDataOptionsRule":"Erişim kuralı",
"oidcRPMetaDataMacros":"Makrolar",
"oidcOPMetaDataOptionsScope":"Kapsam",
"oidcOPMetaDataOptionsStoreIDToken":"ID Jetonu Sakla",
"oidcOPMetaDataOptionsTokenEndpointAuthMethod":"Jeton uç noktası doğrulama metodu",
@ -1036,6 +1038,7 @@
"samlSPMetaDataOptionsNotOnOrAfterTimeout":"notOnOrAfter süresi",
"samlSPMetaDataOptionsForceUTF8":"UTF-8'e zorla",
"samlSPMetaDataOptionsRule":"Erişim kuralı",
"samlSPMetaDataMacros":"Makrolar",
"samlIDPName":"SAML IDP Adı",
"samlServiceMetaData":"SAML2 Servisi",
"samlEntityID":"Varlık Tanımlayıcı",

View File

@ -118,6 +118,7 @@
"casAppMetaDataOptions":"Tùy chọn",
"casAppMetaDataOptionsService":"Dịch vụ URL",
"casAppMetaDataOptionsRule":"Quy tắc",
"casAppMetaDataMacros":"Macros",
"casAppMetaDataOptionsUserAttribute":"thuộc tính người dùng",
"casAppName":"Tên ứng dụng CAS",
"casAttr":"Đăng nhập CAS ",
@ -571,6 +572,7 @@
"oidcRPMetaDataOptionsPublic":"Public client",
"oidcRPMetaDataOptionsRequirePKCE":"Require PKCE",
"oidcRPMetaDataOptionsRule":"Quy tắc truy cập",
"oidcRPMetaDataMacros":"Macros",
"oidcOPMetaDataOptionsScope":"Phạm vi",
"oidcOPMetaDataOptionsStoreIDToken":"Mã thông báo Cửa hàng",
"oidcOPMetaDataOptionsTokenEndpointAuthMethod":"Phương pháp xác thực thiết bị đầu cuối Token",
@ -1036,6 +1038,7 @@
"samlSPMetaDataOptionsNotOnOrAfterTimeout":"Thời gian notOnOrAfter ",
"samlSPMetaDataOptionsForceUTF8":"Bắt buộc UTF-8",
"samlSPMetaDataOptionsRule":"Quy tắc truy cập",
"samlSPMetaDataMacros":"Macros",
"samlIDPName":"Tên SAML IDP ",
"samlServiceMetaData":"Dịch vụ SAML 2",
"samlEntityID":"Thực thể trình định danh",

View File

@ -118,6 +118,7 @@
"casAppMetaDataOptions":"选项",
"casAppMetaDataOptionsService":"服务 URL",
"casAppMetaDataOptionsRule":"规则",
"casAppMetaDataMacros":"Macros",
"casAppMetaDataOptionsUserAttribute":"User attribute",
"casAppName":"CAS App 名称",
"casAttr":"CAS 登录",
@ -571,6 +572,7 @@
"oidcRPMetaDataOptionsPublic":"Public client",
"oidcRPMetaDataOptionsRequirePKCE":"Require PKCE",
"oidcRPMetaDataOptionsRule":"Access rule",
"oidcRPMetaDataMacros":"Macros",
"oidcOPMetaDataOptionsScope":"Scope",
"oidcOPMetaDataOptionsStoreIDToken":"Store ID Token",
"oidcOPMetaDataOptionsTokenEndpointAuthMethod":"Token endpoint authentication method",
@ -1036,6 +1038,7 @@
"samlSPMetaDataOptionsNotOnOrAfterTimeout":"notOnOrAfter duration",
"samlSPMetaDataOptionsForceUTF8":"Force UTF-8",
"samlSPMetaDataOptionsRule":"Access rule",
"samlSPMetaDataMacros":"Macros",
"samlIDPName":"SAML IDP Name",
"samlServiceMetaData":"SAML2 Service",
"samlEntityID":"Entity Identifier",

View File

@ -221,13 +221,11 @@ site/htdocs/static/bwr/font-awesome/fonts/fontawesome-webfont.woff2
site/htdocs/static/bwr/font-awesome/fonts/FontAwesome.otf
site/htdocs/static/bwr/jquery-ui/jquery-ui.js
site/htdocs/static/bwr/jquery-ui/jquery-ui.min.js
site/htdocs/static/bwr/jquery-ui/jquery-ui.min.js.map
site/htdocs/static/bwr/jquery.cookie/jquery.cookie.js
site/htdocs/static/bwr/jquery.cookie/jquery.cookie.min.js
site/htdocs/static/bwr/jquery.cookie/jquery.cookie.min.js.map
site/htdocs/static/bwr/jquery/dist/jquery.js
site/htdocs/static/bwr/jquery/dist/jquery.min.js
site/htdocs/static/bwr/jquery/dist/jquery.min.js.map
site/htdocs/static/bwr/jquery/dist/jquery.min.map
site/htdocs/static/bwr/qrious/dist/qrious.js
site/htdocs/static/bwr/qrious/dist/qrious.js.map
@ -509,6 +507,7 @@ t/30-Auth-and-issuer-SAML-Redirect.t
t/30-Auth-SAML-with-choice.t
t/30-CDC.t
t/30-SAML-Head-to-Tail-POST.t
t/30-SAML-Macros.t
t/30-SAML-POST-Logout-when-expired.t
t/30-SAML-POST-with-2F-and-Notification.t
t/30-SAML-POST-with-Notification.t
@ -536,6 +535,8 @@ t/32-Auth-and-issuer-OIDC-implicit-no-token.t
t/32-Auth-and-issuer-OIDC-implicit.t
t/32-Auth-and-issuer-OIDC-sorted.t
t/32-CAS-10.t
t/32-CAS-Macros.t
t/32-OIDC-Macro.t
t/32-OIDC-Offline-Session.t
t/32-OIDC-Refresh-Token.t
t/32-OIDC-RP-rule.t

View File

@ -186,7 +186,7 @@ sub extractFormInfo {
my $id_token_payload = $self->extractJWT($id_token)->[1];
my $id_token_payload_hash =
$self->decodeJSON( decode_base64($id_token_payload) );
$self->decodeJSON( $self->decodeBase64url($id_token_payload) );
# Check validity of Access Token (optional)
my $at_hash = $id_token_payload_hash->{at_hash};
@ -243,7 +243,7 @@ sub extractFormInfo {
my $portalPath = $self->{conf}->{portal};
$portalPath =~ s#^https?://[^/]+/?#/#;
$req->data->{list} = $self->opList;
$req->data->{list} = $self->opList;
$req->data->{login} = 1;
return PE_IDPCHOICE;

View File

@ -801,9 +801,21 @@ sub _validate2 {
}
foreach my $casAttribute ( keys %$ev ) {
my $localSessionValue = $localSession->data->{ $ev->{$casAttribute} };
$attributes->{$casAttribute} = $localSessionValue
if defined $localSessionValue;
my $sessionAttr = $ev->{$casAttribute};
my $value;
# Lookup per-service macros first, and then local sessions
#
if ( $app and $self->spMacros->{$app}->{$sessionAttr} ) {
$value = $self->spMacros->{$app}->{$sessionAttr}
->( $req, $localSession->data );
}
else {
$value = $localSession->data->{$sessionAttr};
}
$attributes->{$casAttribute} = $value
if defined $value;
}
# Return success message

View File

@ -781,7 +781,7 @@ sub run {
# No access_token
# Claims must be set in id_token
my $claims =
$self->buildUserInfoResponseFromId(
$self->buildUserInfoResponseFromId( $req,
$oidc_request->{'scope'},
$rp, $req->id );
@ -926,7 +926,8 @@ sub run {
# No access_token
# Claims must be set in id_token
my $claims = $self->buildUserInfoResponseFromId(
my $claims =
$self->buildUserInfoResponseFromId( $req,
$oidc_request->{'scope'},
$rp, $req->id );
@ -1261,9 +1262,10 @@ sub token {
$id_token_payload_hash->{'at_hash'} = $at_hash if $at_hash;
if ( $self->force_id_claims($rp) ) {
my $claims =
$self->buildUserInfoResponseFromId( $codeSession->data->{'scope'},
$rp, $codeSession->data->{user_session_id} );
my $claims = $self->buildUserInfoResponseFromId(
$req, $codeSession->data->{'scope'},
$rp, $codeSession->data->{user_session_id}
);
foreach ( keys %$claims ) {
$id_token_payload_hash->{$_} = $claims->{$_}
@ -1481,7 +1483,8 @@ sub token {
# If we forced sending claims in ID token
if ( $self->force_id_claims($rp) ) {
my $claims =
$self->buildUserInfoResponse( $refreshSession->data->{scope},
$self->buildUserInfoResponse( $req,
$refreshSession->data->{scope},
$rp, $session );
foreach ( keys %$claims ) {
@ -1594,7 +1597,7 @@ sub userInfo {
}
my $userinfo_response =
$self->buildUserInfoResponse( $scope, $rp, $session );
$self->buildUserInfoResponse( $req, $scope, $rp, $session );
unless ($userinfo_response) {
return $self->returnBearerError( 'invalid_request', 'Invalid request',
401 );

View File

@ -610,8 +610,17 @@ sub run {
# Name is required
next unless $name;
# Error if corresponding attribute is not in user session
my $value = $req->{sessionInfo}->{$_};
# Lookup attribute value in SP macros or session
my $value;
if ( $self->spMacros->{$sp}->{$_} ) {
$value = $self->spMacros->{$sp}->{$_}
->( $req, $req->{sessionInfo} );
}
else {
$value = $req->{sessionInfo}->{$_};
}
# Check whether the value is required or not
unless ( defined $value ) {
if ($mandatory) {
$self->logger->error(
@ -1478,7 +1487,7 @@ sub sloRelayTerm {
my $session = $logout->get_session();
unless ($session) {
$self->logger->error( "Could not get session from logout" );
$self->logger->error("Could not get session from logout");
return PE_SAML_SLO_ERROR;
}

View File

@ -26,6 +26,7 @@ has ua => (
has casSrvList => ( is => 'rw', default => sub { {} }, );
has casAppList => ( is => 'rw', default => sub { {} }, );
has spRules => ( is => 'rw', default => sub { {} }, );
has spMacros => ( is => 'rw', default => sub { {} }, );
# RUNNING METHODS
@ -67,6 +68,22 @@ sub loadApp {
}
$self->spRules->{$_} = $rule;
}
# Load per-application macros
my $macros = $self->conf->{casAppMetaDataMacros}->{$_};
for my $macroAttr ( keys %{$macros} ) {
my $macroRule = $macros->{$macroAttr};
if ( length $macroRule ) {
$macroRule = $self->p->HANDLER->substitute($macroRule);
unless ( $macroRule = $self->p->HANDLER->buildSub($macroRule) )
{
$self->error( 'SAML SP macro error: '
. $self->p->HANDLER->tsv->{jail}->error );
return 0;
}
$self->spMacros->{$_}->{$macroAttr} = $macroRule;
}
}
}
return 1;
}

View File

@ -343,7 +343,10 @@ sub notificationServer {
}
else {
push @$res,
{ "uid" => $json->{uid}, "reference" => $json->{reference} };
{
"uid" => $json->{uid},
"reference" => ( $json->{reference} || $json->{ref} )
};
}
}
}

View File

@ -37,6 +37,7 @@ has oidcOPList => ( is => 'rw', default => sub { {} }, );
has oidcRPList => ( is => 'rw', default => sub { {} }, );
has rpAttributes => ( is => 'rw', default => sub { {} }, );
has spRules => ( is => 'rw', default => sub { {} } );
has spMacros => ( is => 'rw', default => sub { {} } );
# return LWP::UserAgent object
has ua => (
@ -132,6 +133,22 @@ sub loadRPs {
}
$self->spRules->{$rp} = $rule;
}
# Load per-RP macros
my $macros = $self->conf->{oidcRPMetaDataMacros}->{$rp};
for my $macroAttr ( keys %{$macros} ) {
my $macroRule = $macros->{$macroAttr};
if ( length $macroRule ) {
$macroRule = $self->p->HANDLER->substitute($macroRule);
unless ( $macroRule = $self->p->HANDLER->buildSub($macroRule) )
{
$self->error( 'OIDC RP macro error: '
. $self->p->HANDLER->tsv->{jail}->error );
return 0;
}
$self->spMacros->{$rp}->{$macroAttr} = $macroRule;
}
}
}
return 1;
}
@ -1299,11 +1316,11 @@ sub getAttributesListFromClaim {
# @param user_session_id User session identifier
# @return hashref UserInfo data
sub buildUserInfoResponseFromId {
my ( $self, $scope, $rp, $user_session_id ) = @_;
my ( $self, $req, $scope, $rp, $user_session_id ) = @_;
my $session = $self->p->getApacheSession($user_session_id);
return undef unless ($session);
return buildUserInfoResponse( $self, $scope, $rp, $session );
return buildUserInfoResponse( $self, $req, $scope, $rp, $session );
}
# Return Hash of UserInfo data
@ -1312,7 +1329,7 @@ sub buildUserInfoResponseFromId {
# @param session SSO or offline session
# @return hashref UserInfo data
sub buildUserInfoResponse {
my ( $self, $scope, $rp, $session ) = @_;
my ( $self, $req, $scope, $rp, $session ) = @_;
my $userinfo_response = {};
my $user_id_attribute =
@ -1335,7 +1352,19 @@ sub buildUserInfoResponse {
my $session_key =
$self->conf->{oidcRPMetaDataExportedVars}->{$rp}->{$attribute};
if ($session_key) {
my $session_value = $session->data->{$session_key};
my $session_value;
# Lookup attribute in macros first
if ( $self->spMacros->{$rp}->{$session_key} ) {
$session_value = $self->spMacros->{$rp}->{$session_key}
->( $req, $session->data );
# If not found, search in session
}
else {
$session_value = $session->data->{$session_key};
}
# Address is a JSON object
if ( $claim eq "address" ) {
@ -1604,6 +1633,16 @@ sub decode_base64url {
return decode_base64($s);
}
sub encodeBase64url {
my ( $self, $value ) = @_;
return encode_base64url($value);
}
sub decodeBase64url {
my ( $self, $value ) = @_;
return decode_base64url($value);
}
sub addRouteFromConf {
my ( $self, $type, %subs ) = @_;
my $adder = "add${type}Route";

View File

@ -30,6 +30,7 @@ has spList => ( is => 'rw', default => sub { {} } );
has idpList => ( is => 'rw', default => sub { {} } );
has idpRules => ( is => 'rw', default => sub { {} } );
has spRules => ( is => 'rw', default => sub { {} } );
has spMacros => ( is => 'rw', default => sub { {} } );
# return LWP::UserAgent object
has ua => (
@ -417,6 +418,22 @@ sub loadSPs {
$self->spRules->{$entityID} = $rule;
}
# Load per-SP macros
my $macros = $self->conf->{samlSPMetaDataMacros}->{$_};
for my $macroAttr ( keys %{$macros} ) {
my $macroRule = $macros->{$macroAttr};
if ( length $macroRule ) {
$macroRule = $self->p->HANDLER->substitute($macroRule);
unless ( $macroRule = $self->p->HANDLER->buildSub($macroRule) )
{
$self->error( 'SAML SP macro error: '
. $self->p->HANDLER->tsv->{jail}->error );
return 0;
}
$self->spMacros->{$entityID}->{$macroAttr} = $macroRule;
}
}
$self->logger->debug("SP $_ added");
}

View File

@ -164,9 +164,10 @@ sub refresh {
my ( $self, $req ) = @_;
$req->mustRedirect(1);
my %data = %{ $req->userData };
$req->user( $data{ $self->conf->{whatToTrace} } );
$self->userLogger->notice(
'Refresh request for ' . $data{ $self->conf->{whatToTrace} } );
$req->user( $data{_user} || $data{ $self->conf->{whatToTrace} } );
$req->id( $data{_session_id} );
$self->userLogger->notice( 'Refresh request for ' . $req->user );
foreach ( keys %data ) {
delete $data{$_} unless ( /^_/ or /^(?:startTime)$/ );
}
@ -237,7 +238,7 @@ sub do {
}
# Remove userData if authentication fails
if ( $err == PE_BADCREDENTIALS or $err == PE_BADOTP) {
if ( $err == PE_BADCREDENTIALS or $err == PE_BADOTP ) {
$req->userData( {} );
}

View File

@ -42,7 +42,7 @@ sub ask {
my ( $self, $req ) = @_;
# Check if auth is already running
if ( $req->param('upgrading') ) {
if ( $req->param('upgrading') or $req->param('kerberos') ) {
# verify token
return $self->confirm($req);
@ -69,12 +69,18 @@ sub confirm {
# Disabled due to #1821
#$req->pdata->{keepPdata} = 1;
my $upg;
if ( my $t = $req->param('upgrading') ) {
if ( $self->ott->getToken($t) ) {
$upg = 1;
}
else {
return $self->p->do( $req, [ sub { PE_TOKENEXPIRED } ] );
if ( $req->param('kerberos') ) {
$upg = 1;
}
else {
if ( my $t = $req->param('upgrading') ) {
if ( $self->ott->getToken($t) ) {
$upg = 1;
}
else {
return $self->p->do( $req, [ sub { PE_TOKENEXPIRED } ] );
}
}
}
$req->steps( ['controlUrl'] );

View File

@ -1,7 +1,7 @@
# Launch Kerberos request
$(document).ready ->
$.ajax portal + '?kerberos=1',
$.ajax (if window.location.href.match /\/upgradesession/ then window.location.href else portal )+ '?kerberos=1',
dataType: 'json'
# Called if browser can't find Kerberos ticket, will display
# PE_BADCREDENTIALS
@ -11,7 +11,10 @@ $(document).ready ->
# If request succeed cookie is set, posting form to get redirection
# or menu
success: (data) ->
$('#lform').submit()
if window.location.href.match /\/upgradesession/
document.location = portal
else
$('#lform').submit()
# Case else, will display PE_BADCREDENTIALS or fallback to next auth
# backend
error: () ->

View File

@ -1,5 +1,5 @@
/*!
* Bootstrap Grid v4.3.1 (https://getbootstrap.com/)
* Bootstrap Grid v4.4.1 (https://getbootstrap.com/)
* Copyright 2011-2019 The Bootstrap Authors
* Copyright 2011-2019 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
@ -47,7 +47,7 @@ html {
}
}
.container-fluid {
.container-fluid, .container-sm, .container-md, .container-lg, .container-xl {
width: 100%;
padding-right: 15px;
padding-left: 15px;
@ -55,6 +55,30 @@ html {
margin-left: auto;
}
@media (min-width: 576px) {
.container, .container-sm {
max-width: 540px;
}
}
@media (min-width: 768px) {
.container, .container-sm, .container-md {
max-width: 720px;
}
}
@media (min-width: 992px) {
.container, .container-sm, .container-md, .container-lg {
max-width: 960px;
}
}
@media (min-width: 1200px) {
.container, .container-sm, .container-md, .container-lg, .container-xl {
max-width: 1140px;
}
}
.row {
display: -ms-flexbox;
display: flex;
@ -95,6 +119,42 @@ html {
max-width: 100%;
}
.row-cols-1 > * {
-ms-flex: 0 0 100%;
flex: 0 0 100%;
max-width: 100%;
}
.row-cols-2 > * {
-ms-flex: 0 0 50%;
flex: 0 0 50%;
max-width: 50%;
}
.row-cols-3 > * {
-ms-flex: 0 0 33.333333%;
flex: 0 0 33.333333%;
max-width: 33.333333%;
}
.row-cols-4 > * {
-ms-flex: 0 0 25%;
flex: 0 0 25%;
max-width: 25%;
}
.row-cols-5 > * {
-ms-flex: 0 0 20%;
flex: 0 0 20%;
max-width: 20%;
}
.row-cols-6 > * {
-ms-flex: 0 0 16.666667%;
flex: 0 0 16.666667%;
max-width: 16.666667%;
}
.col-auto {
-ms-flex: 0 0 auto;
flex: 0 0 auto;
@ -301,6 +361,36 @@ html {
flex-grow: 1;
max-width: 100%;
}
.row-cols-sm-1 > * {
-ms-flex: 0 0 100%;
flex: 0 0 100%;
max-width: 100%;
}
.row-cols-sm-2 > * {
-ms-flex: 0 0 50%;
flex: 0 0 50%;
max-width: 50%;
}
.row-cols-sm-3 > * {
-ms-flex: 0 0 33.333333%;
flex: 0 0 33.333333%;
max-width: 33.333333%;
}
.row-cols-sm-4 > * {
-ms-flex: 0 0 25%;
flex: 0 0 25%;
max-width: 25%;
}
.row-cols-sm-5 > * {
-ms-flex: 0 0 20%;
flex: 0 0 20%;
max-width: 20%;
}
.row-cols-sm-6 > * {
-ms-flex: 0 0 16.666667%;
flex: 0 0 16.666667%;
max-width: 16.666667%;
}
.col-sm-auto {
-ms-flex: 0 0 auto;
flex: 0 0 auto;
@ -473,6 +563,36 @@ html {
flex-grow: 1;
max-width: 100%;
}
.row-cols-md-1 > * {
-ms-flex: 0 0 100%;
flex: 0 0 100%;
max-width: 100%;
}
.row-cols-md-2 > * {
-ms-flex: 0 0 50%;
flex: 0 0 50%;
max-width: 50%;
}
.row-cols-md-3 > * {
-ms-flex: 0 0 33.333333%;
flex: 0 0 33.333333%;
max-width: 33.333333%;
}
.row-cols-md-4 > * {
-ms-flex: 0 0 25%;
flex: 0 0 25%;
max-width: 25%;
}
.row-cols-md-5 > * {
-ms-flex: 0 0 20%;
flex: 0 0 20%;
max-width: 20%;
}
.row-cols-md-6 > * {
-ms-flex: 0 0 16.666667%;
flex: 0 0 16.666667%;
max-width: 16.666667%;
}
.col-md-auto {
-ms-flex: 0 0 auto;
flex: 0 0 auto;
@ -645,6 +765,36 @@ html {
flex-grow: 1;
max-width: 100%;
}
.row-cols-lg-1 > * {
-ms-flex: 0 0 100%;
flex: 0 0 100%;
max-width: 100%;
}
.row-cols-lg-2 > * {
-ms-flex: 0 0 50%;
flex: 0 0 50%;
max-width: 50%;
}
.row-cols-lg-3 > * {
-ms-flex: 0 0 33.333333%;
flex: 0 0 33.333333%;
max-width: 33.333333%;
}
.row-cols-lg-4 > * {
-ms-flex: 0 0 25%;
flex: 0 0 25%;
max-width: 25%;
}
.row-cols-lg-5 > * {
-ms-flex: 0 0 20%;
flex: 0 0 20%;
max-width: 20%;
}
.row-cols-lg-6 > * {
-ms-flex: 0 0 16.666667%;
flex: 0 0 16.666667%;
max-width: 16.666667%;
}
.col-lg-auto {
-ms-flex: 0 0 auto;
flex: 0 0 auto;
@ -817,6 +967,36 @@ html {
flex-grow: 1;
max-width: 100%;
}
.row-cols-xl-1 > * {
-ms-flex: 0 0 100%;
flex: 0 0 100%;
max-width: 100%;
}
.row-cols-xl-2 > * {
-ms-flex: 0 0 50%;
flex: 0 0 50%;
max-width: 50%;
}
.row-cols-xl-3 > * {
-ms-flex: 0 0 33.333333%;
flex: 0 0 33.333333%;
max-width: 33.333333%;
}
.row-cols-xl-4 > * {
-ms-flex: 0 0 25%;
flex: 0 0 25%;
max-width: 25%;
}
.row-cols-xl-5 > * {
-ms-flex: 0 0 20%;
flex: 0 0 20%;
max-width: 20%;
}
.row-cols-xl-6 > * {
-ms-flex: 0 0 16.666667%;
flex: 0 0 16.666667%;
max-width: 16.666667%;
}
.col-xl-auto {
-ms-flex: 0 0 auto;
flex: 0 0 auto;

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,5 +1,5 @@
/*!
* Bootstrap Reboot v4.3.1 (https://getbootstrap.com/)
* Bootstrap Reboot v4.4.1 (https://getbootstrap.com/)
* Copyright 2011-2019 The Bootstrap Authors
* Copyright 2011-2019 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
@ -33,7 +33,7 @@ body {
background-color: #fff;
}
[tabindex="-1"]:focus {
[tabindex="-1"]:focus:not(:focus-visible) {
outline: 0 !important;
}
@ -133,20 +133,16 @@ a:hover {
text-decoration: underline;
}
a:not([href]):not([tabindex]) {
a:not([href]) {
color: inherit;
text-decoration: none;
}
a:not([href]):not([tabindex]):hover, a:not([href]):not([tabindex]):focus {
a:not([href]):hover {
color: inherit;
text-decoration: none;
}
a:not([href]):not([tabindex]):focus {
outline: 0;
}
pre,
code,
kbd,

File diff suppressed because one or more lines are too long

View File

@ -1,8 +1,8 @@
/*!
* Bootstrap Reboot v4.3.1 (https://getbootstrap.com/)
* Bootstrap Reboot v4.4.1 (https://getbootstrap.com/)
* Copyright 2011-2019 The Bootstrap Authors
* Copyright 2011-2019 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
* Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md)
*/*,::after,::before{box-sizing:border-box}html{font-family:sans-serif;line-height:1.15;-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:transparent}article,aside,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}body{margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-size:1rem;font-weight:400;line-height:1.5;color:#212529;text-align:left;background-color:#fff}[tabindex="-1"]:focus{outline:0!important}hr{box-sizing:content-box;height:0;overflow:visible}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem}p{margin-top:0;margin-bottom:1rem}abbr[data-original-title],abbr[title]{text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;border-bottom:0;-webkit-text-decoration-skip-ink:none;text-decoration-skip-ink:none}address{margin-bottom:1rem;font-style:normal;line-height:inherit}dl,ol,ul{margin-top:0;margin-bottom:1rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}b,strong{font-weight:bolder}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:#007bff;text-decoration:none;background-color:transparent}a:hover{color:#0056b3;text-decoration:underline}a:not([href]):not([tabindex]){color:inherit;text-decoration:none}a:not([href]):not([tabindex]):focus,a:not([href]):not([tabindex]):hover{color:inherit;text-decoration:none}a:not([href]):not([tabindex]):focus{outline:0}code,kbd,pre,samp{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;font-size:1em}pre{margin-top:0;margin-bottom:1rem;overflow:auto}figure{margin:0 0 1rem}img{vertical-align:middle;border-style:none}svg{overflow:hidden;vertical-align:middle}table{border-collapse:collapse}caption{padding-top:.75rem;padding-bottom:.75rem;color:#6c757d;text-align:left;caption-side:bottom}th{text-align:inherit}label{display:inline-block;margin-bottom:.5rem}button{border-radius:0}button:focus{outline:1px dotted;outline:5px auto -webkit-focus-ring-color}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,input{overflow:visible}button,select{text-transform:none}select{word-wrap:normal}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]:not(:disabled),[type=reset]:not(:disabled),[type=submit]:not(:disabled),button:not(:disabled){cursor:pointer}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{padding:0;border-style:none}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}input[type=date],input[type=datetime-local],input[type=month],input[type=time]{-webkit-appearance:listbox}textarea{overflow:auto;resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;max-width:100%;padding:0;margin-bottom:.5rem;font-size:1.5rem;line-height:inherit;color:inherit;white-space:normal}progress{vertical-align:baseline}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:none}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}summary{display:list-item;cursor:pointer}template{display:none}[hidden]{display:none!important}
*/*,::after,::before{box-sizing:border-box}html{font-family:sans-serif;line-height:1.15;-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:transparent}article,aside,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}body{margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-size:1rem;font-weight:400;line-height:1.5;color:#212529;text-align:left;background-color:#fff}[tabindex="-1"]:focus:not(:focus-visible){outline:0!important}hr{box-sizing:content-box;height:0;overflow:visible}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem}p{margin-top:0;margin-bottom:1rem}abbr[data-original-title],abbr[title]{text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;border-bottom:0;-webkit-text-decoration-skip-ink:none;text-decoration-skip-ink:none}address{margin-bottom:1rem;font-style:normal;line-height:inherit}dl,ol,ul{margin-top:0;margin-bottom:1rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}b,strong{font-weight:bolder}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:#007bff;text-decoration:none;background-color:transparent}a:hover{color:#0056b3;text-decoration:underline}a:not([href]){color:inherit;text-decoration:none}a:not([href]):hover{color:inherit;text-decoration:none}code,kbd,pre,samp{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;font-size:1em}pre{margin-top:0;margin-bottom:1rem;overflow:auto}figure{margin:0 0 1rem}img{vertical-align:middle;border-style:none}svg{overflow:hidden;vertical-align:middle}table{border-collapse:collapse}caption{padding-top:.75rem;padding-bottom:.75rem;color:#6c757d;text-align:left;caption-side:bottom}th{text-align:inherit}label{display:inline-block;margin-bottom:.5rem}button{border-radius:0}button:focus{outline:1px dotted;outline:5px auto -webkit-focus-ring-color}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,input{overflow:visible}button,select{text-transform:none}select{word-wrap:normal}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]:not(:disabled),[type=reset]:not(:disabled),[type=submit]:not(:disabled),button:not(:disabled){cursor:pointer}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{padding:0;border-style:none}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}input[type=date],input[type=datetime-local],input[type=month],input[type=time]{-webkit-appearance:listbox}textarea{overflow:auto;resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;max-width:100%;padding:0;margin-bottom:.5rem;font-size:1.5rem;line-height:inherit;color:inherit;white-space:normal}progress{vertical-align:baseline}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:none}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}summary{display:list-item;cursor:pointer}template{display:none}[hidden]{display:none!important}
/*# sourceMappingURL=bootstrap-reboot.min.css.map */

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,7 +1,7 @@
// Generated by CoffeeScript 1.12.8
(function() {
$(document).ready(function() {
return $.ajax(portal + '?kerberos=1', {
return $.ajax((window.location.href.match(/\/upgradesession/) ? window.location.href : portal) + '?kerberos=1', {
dataType: 'json',
statusCode: {
401: function() {
@ -9,7 +9,11 @@
}
},
success: function(data) {
return $('#lform').submit();
if (window.location.href.match(/\/upgradesession/)) {
return document.location = portal;
} else {
return $('#lform').submit();
}
},
error: function() {
return $('#lform').submit();

View File

@ -1 +1 @@
(function(){$(document).ready(function(){return $.ajax(portal+"?kerberos=1",{dataType:"json",statusCode:{401:function(){return $("#lform").submit()}},success:function(r){return $("#lform").submit()},error:function(){return $("#lform").submit()}})})}).call(this);
(function(){$(document).ready(function(){return $.ajax((window.location.href.match(/\/upgradesession/)?window.location.href:portal)+"?kerberos=1",{dataType:"json",statusCode:{401:function(){return $("#lform").submit()}},success:function(o){return window.location.href.match(/\/upgradesession/)?document.location=portal:$("#lform").submit()},error:function(){return $("#lform").submit()}})})}).call(this);

View File

@ -1 +1 @@
{"version":3,"sources":["lemonldap-ng-portal/site/htdocs/static/common/js/kerberos.js"],"names":["$","document","ready","ajax","portal","dataType","statusCode","401","submit","success","data","error","call","this"],"mappings":"CACA,WACEA,EAAEC,UAAUC,MAAM,WAChB,OAAOF,EAAEG,KAAKC,OAAS,cAAe,CACpCC,SAAU,OACVC,WAAY,CACVC,IAAK,WACH,OAAOP,EAAE,UAAUQ,WAGvBC,QAAS,SAASC,GAChB,OAAOV,EAAE,UAAUQ,UAErBG,MAAO,WACL,OAAOX,EAAE,UAAUQ,gBAKxBI,KAAKC"}
{"version":3,"sources":["lemonldap-ng-portal/site/htdocs/static/common/js/kerberos.js"],"names":["$","document","ready","ajax","window","location","href","match","portal","dataType","statusCode","401","submit","success","data","error","call","this"],"mappings":"CACA,WACEA,EAAEC,UAAUC,MAAM,WAChB,OAAOF,EAAEG,MAAMC,OAAOC,SAASC,KAAKC,MAAM,oBAAsBH,OAAOC,SAASC,KAAOE,QAAU,cAAe,CAC9GC,SAAU,OACVC,WAAY,CACVC,IAAK,WACH,OAAOX,EAAE,UAAUY,WAGvBC,QAAS,SAASC,GAChB,OAAIV,OAAOC,SAASC,KAAKC,MAAM,oBACtBN,SAASI,SAAWG,OAEpBR,EAAE,UAAUY,UAGvBG,MAAO,WACL,OAAOf,EAAE,UAAUY,gBAKxBI,KAAKC"}

View File

@ -104,7 +104,7 @@
"askToRenew":"هذا التطبيق يحتاج إلى مصادقة أكثر حداثة. هل تريد إعادة المصادقة؟",
"askToUpgrade":"هذا التطبيق يحتاج إلى مستوى مصادقة أعلى. هل تريد إعادة المصادقة؟",
"attributes":"ATTRIBUTES",
"authLevel":"Authentication level",
"authLevel":"مستوى إثبات الهوية",
"authPortal":"بوابة إثبات الهوية",
"authRemaining":"٪ s المصادقة المتبقية، غيير كلمة المرور الخاصة بك!",
"autoAccept":"تقبل تلقائيا في 30 ثانية",

View File

@ -103,7 +103,7 @@
"askToRenew":"Questa applicazione richiede un'autenticazione più recente. Vuoi reautenticare?",
"askToUpgrade":"Questa applicazione richiede un livello di autenticazione superiore. Vuoi reautenticare?",
"attributes":"ATTRIBUTI",
"authLevel":"Authentication level",
"authLevel":"Livello di autenticazione",
"authPortal":"Portale di autenticazione",
"authRemaining":"Rimangono ancora %s autenticazioni, modifica la password!",
"autoAccept":"Accetta automaticamente in 30 secondi",
@ -246,7 +246,7 @@
"sfaManager":"2ndFA Manager",
"spoofId":"Id usurpato",
"SSOSessionInactive":"Sessione SSO inattiva",
"startTime":"Creation date",
"startTime":"Data di creazione",
"stayConnected":"Resta connesso su questo dispositivo",
"submit":"Invia",
"switchContext":"Switch context",
@ -264,7 +264,7 @@
"unknownAction":"Azione sconosciuta",
"unregister":"Annullare la registrazione",
"updateCdc":"Aggiorna il Cookie di Common Domain",
"updateTime":"Update date",
"updateTime":"Aggiorna data",
"upgradeSession":"Sessione di aggiornamento",
"user":"Utente",
"useYubikey":"Usa la tua Yubikey",

View File

@ -0,0 +1,196 @@
use lib 'inc';
use Test::More;
use strict;
use IO::String;
use LWP::UserAgent;
use LWP::Protocol::PSGI;
use MIME::Base64;
use XML::LibXML;
BEGIN {
require 't/test-lib.pm';
require 't/saml-lib.pm';
}
my $debug = 'error';
my ( $issuer, $res );
SKIP: {
eval "use Lasso";
if ($@) {
skip 'Lasso not found';
}
# Initialization
ok( $issuer = issuer(), 'Issuer portal' );
ok(
$res = $issuer->_post(
'/', IO::String->new('user=french&password=french'),
length => 27
),
'Auth query'
);
expectOK($res);
my $idpId = expectCookie($res);
# Query IdP to access to SP
ok(
$res = $issuer->_get(
'/saml/singleSignOn',
query => 'IDPInitiated=1&spConfKey=sp.com',
cookie => "lemonldap=$idpId",
accept => 'test/html'
),
'Query IdP to access to SP'
);
expectOK($res);
ok(
$res->[2]->[0] =~
m#<form.+?action="http://auth.sp.com(.*?)".+?method="post"#,
'Form method is POST'
);
my $url = $1;
ok(
$res->[2]->[0] =~
/<input type="hidden".+?name="SAMLResponse".+?value="(.+?)"/s,
'Found SAML response'
);
my $s = decode_base64($1);
my $dom = XML::LibXML->load_xml( string => $s );
my $xpc = XML::LibXML::XPathContext->new($dom);
$xpc->registerNs( 'saml', 'urn:oasis:names:tc:SAML:2.0:assertion' );
foreach my $value (
$xpc->findnodes('//saml:Attribute[@Name="sn"]/saml:AttributeValue') )
{
is( $value->textContent, 'Accents', 'Check Attribute' );
}
}
clean_sessions();
done_testing();
sub issuer {
return LLNG::Manager::Test->new( {
ini => {
logLevel => $debug,
domain => 'idp.com',
portal => 'http://auth.idp.com',
authentication => 'Demo',
userDB => 'Same',
issuerDBSAMLActivation => 1,
samlSPMetaDataMacros => {
'sp.com' => {
extracted_sn => '(split(/\s/, $cn))[1]'
}
},
samlSPMetaDataOptions => {
'sp.com' => {
samlSPMetaDataOptionsEncryptionMode => 'none',
samlSPMetaDataOptionsEnableIDPInitiatedURL => 1,
samlSPMetaDataOptionsSignSSOMessage => 1,
samlSPMetaDataOptionsSignSLOMessage => 1,
samlSPMetaDataOptionsCheckSSOMessageSignature => 1,
samlSPMetaDataOptionsCheckSLOMessageSignature => 1,
}
},
samlSPMetaDataExportedAttributes => {
'sp.com' => {
cn =>
'1;cn;urn:oasis:names:tc:SAML:2.0:attrname-format:basic',
extracted_sn =>
'1;sn;urn:oasis:names:tc:SAML:2.0:attrname-format:basic',
uid =>
'1;uid;urn:oasis:names:tc:SAML:2.0:attrname-format:basic',
}
},
samlOrganizationDisplayName => "IDP",
samlOrganizationName => "IDP",
samlOrganizationURL => "http://www.idp.com/",
samlServicePrivateKeyEnc => "-----BEGIN RSA PRIVATE KEY-----
MIIEogIBAAKCAQEAnfKBDG/K0TnGT7Xu8q1N45sNWvIK91SqNg8nvN2uVeKoHADT
csus5Xn3id5+8Q9TuMFsW9kIEeXiaPKXQa9ryfSNDhWDWloNkpGEeWif2BnHUu46
Abu1UBWb0mH6VwcG1PR4qHruLis1odjQ1qnVDNfSEASVIppEBYjDX203ypmURIzU
6h53GRRRlf1BLWkbVn9ysmDeR57Xw5Rsx/+tBlcnMrkv/40DSUkehQIl2JmlFrl2
Caik+gU4pd20apA/pNLjBZF0OmGoS08AIR5NMd0KFa6CwZUUSHJqH5GFy5Y2yl4l
g8K0klAS9q7L7aXI+eFQZhkwidjpxXnHPyxIGQIDAQABAoIBAHnfqjX3eO8SfnP5
NURp90Td2mNHirCn0qLd9NKl1ySMPR1GgeH9SQ7Umu32EcteAUL5dOw2PiTZVmeW
cKINgsWVftXUQcOQ4xIqWKb51QUBdy0FhxrZRSFjWxXt5iYK1PmzHfsax/g1/S9C
RnqtFyjOy1bywkSt9jiy+9YBR2B7BDhLHlILbijWn5zaecaV4YA+L1UK4M/mehdb
+0FVPavbGpnlqBRTY+7YXfZ/mRPCfn5DvO9lW1O0pJMmNdBh9kmm3DxHf6AkK47a
43gO/dRWiWo2rZ/+Jw7uyqOb23U0MydP7kia0p3tzCUBPsrlgnichYG5RNFp0wqy
3VT1TYECgYEA0Y9vENy1jJd+s7WbGrsRtSKxfZgtJr0yjSlQVYrIlwbZSGn+ndxq
V2vVlwIgLX3pz6T40BMfk6SNx08jjy0Sgn6OAM0ILrinno8yWcSAMCmfCU0S/3O1
55bqtcnk4XTHBHzJ5OrnrPaW5ourvJz0lcWEKMg3BXxLzaF6ZRy85nECgYEAwPMD
LNAKLCDrUMyYFOpPyPLe7wvszcFvPipGgerSgFP1c6N7xaMUdHDYqBfuis1khPGF
YcMHeNBYmzX6yEGbp3lrB4PHpUySmTU3mv3u9I05aahInK21gXum3uRkCWyyIF6V
T/qeszl9mVOCp0CC4eG3IMVpaD0UKDEHVhERYCkCgYAjuTPRyA4a3Wh38ilysRkf
q75eDqcDx5Tqg3RyYKo5NK2troP9HSnzpSpQB8i8eI53G0RfFCN5479XjqIdMi3J
mRFUCZ+vd0L7wKVwsBK6Ix49U6o9adhElnGEc9pUpLeYiD1SjMjZr1+iBYVNLeRz
86vH1/mpMbsqXrCis/dvwQKBgGttomHr/w3s0jftget7PirrFrbP0+wHfDGHhjRF
kyhCFtJovrwefYALaIXGtVjw3LusYZA570oT7pGUb2naJZkMYEwR0jG1vZWx7KDO
K6JbkxDB0pPxn7JVL2bAkPYyX8boAohCSOQO6WBZ/8+xem3bp4OGhpa0EyoBik0g
OaVpAoGATj4SyYsE10hGT676iie8zy3fi5IPC3E+x4QlVuusaLtuY8LJA50stjtx
gUa/JAKlZZL+gvzvOviQIxyfIChXOdTt5uiOYkdHJDbAF3NSrji7hrXq4v8UZv75
8hBrwJZIpy6y01dRlrriHmPRtEq1pk7JX2uUg0sP5g4BEcsaCbc=
-----END RSA PRIVATE KEY-----
",
samlServicePrivateKeySig => "-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAtR/wgDqWB4Maho5V6TjcL/NbNfjgIh7GcgkrB5RZcVT1GTej
JlMjUQdgBKBuZXQN+7/29P6UcGq1kYalURq6S8SpeJ1ofp5rBEoD/TIkvU0JOcid
65wp+fdzXGXsfiZvHraU74jSCgjP/wqfVGRyBIQzB0SIxSpnrsigqNsE1E94toDM
x4wovjHu/9ABAImREV7Sz83OeFF00/sghrjTEJOD/gHf04JCn9MgNOqvSTysr9LX
Wg/oUKQDEYeTq9ux6pq/oqv1MxwONbSZPtN5yD41mi+hT8Rh+W8Je8rsiML4VMxz
sb1l9303asw6suo5bLTISKNSbu1nt1NkpNxzywIDAQABAoIBAQCQkbvPPfP+bwC/
IeEk1IO7qkzFWa7czR+safD0jc6OjTdNN4F716Q6yt4zEzLKu8VliiW+C23EBQiD
7asKf4DvdTun0ExVtHDK7aEdeealSlXwz1ZtdypyILbtq1UGo/rR0v4x601rQPl0
IrBmFf6D6FkqleNtLJmxguXpoVfLdYKNwkxH2ux+GOA9r2o5pUCQmJGDap5YWRuQ
uB71ewJjVWujaL3e1ac/5cP7/tqWmgAiOaN8sYdD6+oWOR47bHj8JKcMBSl4y2QC
dL31cGmmf5KqBbtISki3RXfHHjT7E3Z85CbESkKTZlEb1ar3XmepY6Z7V5UO16oz
fFE5R6khAoGBAOl9Qb+qYVVO5ugE65ORjYVeuXykANhM9ssiY5a6zuAakWzw7Zv3
k6PXm9p7azlEXAlTnTXVwHYMyuuzZDvQ8LRV1iBOdPuIkUAmaQ5K9ASD7VcoHexh
k8DAKf9Ln7sTRaMdvgceRNczOmJOBIEpTZkssA/jVGXZsoyTWYl1en/ZAoGBAMaW
RnNbSNprEV2b8UeAJ6i77c4SXwu1I8X2NLtiLScb1ETBjfrdHmdlJglfyd/0gmhH
p/43Ku2iGUoY5KtuOI6QmahrJYQscRQhoj252VXadG6fNWWAlpgdCm9houhHb5BF
3zge/bTr0anUe9EA7Z/ymav12rEouoNjIlhI9C5DAoGATR85a2SMt8/TB0owwdJu
62GpZNkLCmcJkXkvaecUVAOSi2hdI4o4MwMRkK35cbX5rH74y4JqCtQY5pefgP53
sykzDAK+MyMdzxGg2764MRGegI5Yq+5jDmSquo+xF+q6srEtRk6iMG7UVwosBLmu
zuxqzySoiOfKSRKWnYe3SakCgYEAwWMkVkAmETXE4oDzFSsS8/mW2l//mPocTTK3
JWe1CunJ6+8FYbAlZJEW2ngismp8+CoXybNVpbZ+pC7buKoMf6EHUgCNt0pEEFO0
mCG9KSMk0XlPWXpArP9S4yaUq1itpzSz7QYZES+4rIcU0HLz9RgeWFyCTJWaFErc
7laVG9sCgYBKOtk5WlIOP4BxSd2y4cYzohgwTZIs1/2kTEn1u4eH73M1xvAlHHFB
wSF5QXgDKJ8pPAOhNWpdLO/PdtnQn91nOvTNc+ShJZzjdbneUdQVpWpoBf72uA+N
6rIVf1JBUL2p7HFHaGdUZC7KGQ+yv6ZHrE1+7202nuDvJdvGEEdFsQ==
-----END RSA PRIVATE KEY-----
",
samlServicePublicKeyEnc => "-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnfKBDG/K0TnGT7Xu8q1N
45sNWvIK91SqNg8nvN2uVeKoHADTcsus5Xn3id5+8Q9TuMFsW9kIEeXiaPKXQa9r
yfSNDhWDWloNkpGEeWif2BnHUu46Abu1UBWb0mH6VwcG1PR4qHruLis1odjQ1qnV
DNfSEASVIppEBYjDX203ypmURIzU6h53GRRRlf1BLWkbVn9ysmDeR57Xw5Rsx/+t
BlcnMrkv/40DSUkehQIl2JmlFrl2Caik+gU4pd20apA/pNLjBZF0OmGoS08AIR5N
Md0KFa6CwZUUSHJqH5GFy5Y2yl4lg8K0klAS9q7L7aXI+eFQZhkwidjpxXnHPyxI
GQIDAQAB
-----END PUBLIC KEY-----
",
samlServicePublicKeySig => "-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtR/wgDqWB4Maho5V6Tjc
L/NbNfjgIh7GcgkrB5RZcVT1GTejJlMjUQdgBKBuZXQN+7/29P6UcGq1kYalURq6
S8SpeJ1ofp5rBEoD/TIkvU0JOcid65wp+fdzXGXsfiZvHraU74jSCgjP/wqfVGRy
BIQzB0SIxSpnrsigqNsE1E94toDMx4wovjHu/9ABAImREV7Sz83OeFF00/sghrjT
EJOD/gHf04JCn9MgNOqvSTysr9LXWg/oUKQDEYeTq9ux6pq/oqv1MxwONbSZPtN5
yD41mi+hT8Rh+W8Je8rsiML4VMxzsb1l9303asw6suo5bLTISKNSbu1nt1NkpNxz
ywIDAQAB
-----END PUBLIC KEY-----
",
samlSPMetaDataXML => {
"sp.com" => {
samlSPMetaDataXML =>
samlSPMetaDataXML( 'sp', 'HTTP-Redirect' )
},
},
}
}
);
}

View File

@ -0,0 +1,115 @@
use lib 'inc';
use Test::More; # skip_all => 'CAS is in rebuild';
use strict;
use IO::String;
use LWP::UserAgent;
use LWP::Protocol::PSGI;
use MIME::Base64;
BEGIN {
require 't/test-lib.pm';
}
my $debug = 'error';
my ( $issuer, $res );
eval { require XML::Simple };
plan skip_all => "Missing dependencies: $@" if ($@);
ok( $issuer = issuer(), 'Issuer portal' );
count(1);
ok(
$res = $issuer->_get(
'/cas/login',
query => 'service=http://auth.sp.com/',
accept => 'text/html'
),
'Query CAS server'
);
count(1);
expectOK($res);
my $pdata = 'lemonldappdata=' . expectCookie( $res, 'lemonldappdata' );
# Try to authenticate to IdP
my $body = $res->[2]->[0];
$body =~ s/^.*?<form.*?>//s;
$body =~ s#</form>.*$##s;
my %fields =
( $body =~ /<input type="hidden".+?name="(.+?)".+?value="(.*?)"/sg );
$fields{user} = $fields{password} = 'french';
use URI::Escape;
my $s = join( '&', map { "$_=" . uri_escape( $fields{$_} ) } keys %fields );
ok(
$res = $issuer->_post(
'/cas/login',
IO::String->new($s),
cookie => $pdata,
accept => 'text/html',
length => length($s),
),
'Post authentication'
);
count(1);
my $idpId = expectCookie($res);
# Expect pdata to be cleared
$pdata = expectCookie( $res, 'lemonldappdata' );
ok( $pdata !~ 'issuerRequestsaml', 'SAML request cleared from pdata' );
count(1);
my ($query) =
expectRedirection( $res, qr#^http://auth.sp.com/\?(ticket=[^&]+)$# );
ok(
$res = $issuer->_get(
'/cas/p3/serviceValidate',
query => 'service=http://auth.sp.com/&' . $query,
accept => 'text/html'
),
'Query CAS server'
);
expectOK($res);
count(1);
ok( $res->[2]->[0] =~ m#<cas:sn>Accents</cas:sn>#, "Found macro attribute" );
count(1);
clean_sessions();
done_testing( count() );
sub issuer {
return LLNG::Manager::Test->new( {
ini => {
logLevel => $debug,
domain => 'idp.com',
portal => 'http://auth.idp.com',
authentication => 'Demo',
userDB => 'Same',
issuerDBCASActivation => 1,
casAttr => 'uid',
casAppMetaDataOptions => {
sp => {
casAppMetaDataOptionsService => 'http://auth.sp.com/',
},
},
casAppMetaDataExportedVars => {
sp => {
cn => 'cn',
sn => 'extracted_sn',
mail => 'mail',
uid => 'uid',
},
},
casAppMetaDataMacros => {
sp => {
extracted_sn => '(split(/\s/, $cn))[1]',
}
},
casAccessControlPolicy => 'error',
multiValuesSeparator => ';',
}
}
);
}

View File

@ -0,0 +1,191 @@
use lib 'inc';
use Test::More;
use strict;
use IO::String;
use LWP::UserAgent;
use LWP::Protocol::PSGI;
use MIME::Base64;
BEGIN {
require 't/test-lib.pm';
}
my $debug = 'error';
my $res;
my $url;
# Initialization
ok( my $op = op(), 'OP portal' );
ok( $res = $op->_get('/oauth2/jwks'), 'Get JWKS, endpoint /oauth2/jwks' );
expectOK($res);
my $jwks = $res->[2]->[0];
ok(
$res = $op->_get('/.well-known/openid-configuration'),
'Get metadata, endpoint /.well-known/openid-configuration'
);
expectOK($res);
my $metadata = $res->[2]->[0];
my $query =
"response_type=code&scope=openid%20profile%20email&client_id=rpid&state=af0ifjsldkj&redirect_uri=http%3A%2F%2Frp.com%2F";
# Push request to OP
ok(
$res =
$op->_get( "/oauth2/authorize", query => $query, accept => 'text/html' ),
"Start Authorization Code flow"
);
expectOK($res);
# Try to authenticate to OP
$query = "user=french&password=french&$query";
ok(
$res = $op->_post(
"/oauth2/authorize",
IO::String->new($query),
accept => 'text/html',
length => length($query),
),
"Post authentication, endpoint $url"
);
my $idpId = expectCookie($res);
my ($code) = expectRedirection( $res, qr#http://rp.com/\?.*code=([^&]+)# );
# Get access token
$query =
"grant_type=authorization_code&code=$code&redirect_uri=http%3A%2F%2Frp.com%2F";
ok(
$res = $op->_post(
"/oauth2/token",
IO::String->new($query),
accept => 'text/html',
length => length($query),
custom => {
HTTP_AUTHORIZATION => "Basic " . encode_base64("rpid:rpsecret"),
},
),
"Post token"
);
my $tokenresp = JSON::from_json( $res->[2]->[0] );
ok( my $access_token = $tokenresp->{access_token}, 'Found access token' );
# Get Userinfo
ok(
$res = $op->_get(
"/oauth2/userinfo",
accept => 'text/html',
custom => {
HTTP_AUTHORIZATION => "Bearer " . $access_token,
},
),
"Post token"
);
my $userinfo = JSON::from_json( $res->[2]->[0] );
is( $userinfo->{family_name}, 'Accents', 'Correct macro value' );
clean_sessions();
done_testing();
sub op {
return LLNG::Manager::Test->new( {
ini => {
logLevel => $debug,
domain => 'idp.com',
portal => 'http://auth.op.com',
authentication => 'Demo',
userDB => 'Same',
issuerDBOpenIDConnectActivation => 1,
issuerDBOpenIDConnectRule => '$uid eq "french"',
oidcRPMetaDataExportedVars => {
rp => {
email => "mail",
family_name => "extract_sn",
name => "cn"
}
},
oidcServiceMetaDataAuthorizeURI => "authorize",
oidcServiceMetaDataCheckSessionURI => "checksession.html",
oidcServiceMetaDataJWKSURI => "jwks",
oidcServiceMetaDataEndSessionURI => "logout",
oidcServiceMetaDataRegistrationURI => "register",
oidcServiceMetaDataTokenURI => "token",
oidcServiceMetaDataUserInfoURI => "userinfo",
oidcServiceAllowHybridFlow => 1,
oidcServiceAllowImplicitFlow => 1,
oidcServiceAllowDynamicRegistration => 1,
oidcServiceAllowAuthorizationCodeFlow => 1,
oidcRPMetaDataMacros => {
rp => {
extract_sn => '(split(/\s/, $cn))[1]',
}
},
oidcRPMetaDataOptions => {
rp => {
oidcRPMetaDataOptionsDisplayName => "RP",
oidcRPMetaDataOptionsIDTokenExpiration => 3600,
oidcRPMetaDataOptionsClientID => "rpid",
oidcRPMetaDataOptionsIDTokenSignAlg => "HS512",
oidcRPMetaDataOptionsBypassConsent => 1,
oidcRPMetaDataOptionsClientSecret => "rpsecret",
oidcRPMetaDataOptionsUserIDAttr => "",
oidcRPMetaDataOptionsAccessTokenExpiration => 3600,
}
},
oidcOPMetaDataOptions => {},
oidcOPMetaDataJSON => {},
oidcOPMetaDataJWKS => {},
oidcServiceMetaDataAuthnContext => {
'loa-4' => 4,
'loa-1' => 1,
'loa-5' => 5,
'loa-2' => 2,
'loa-3' => 3
},
oidcServicePrivateKeySig => "-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAs2jsmIoFuWzMkilJaA8//5/T30cnuzX9GImXUrFR2k9EKTMt
GMHCdKlWOl3BV+BTAU9TLz7Jzd/iJ5GJ6B8TrH1PHFmHpy8/qE/S5OhinIpIi7eb
ABqnoVcwDdCa8ugzq8k8SWxhRNXfVIlwz4NH1caJ8lmiERFj7IvNKqEhzAk0pyDr
8hubveTC39xREujKlsqutpPAFPJ3f2ybVsdykX5rx0h5SslG3jVWYhZ/SOb2aIzO
r0RMjhQmsYRwbpt3anjlBZ98aOzg7GAkbO8093X5VVk9vaPRg0zxJQ0Do0YLyzkR
isSAIFb0tdKuDnjRGK6y/N2j6At2HjkxntbtGQIDAQABAoIBADYq6LxJd977LWy3
0HT9nboFPIf+SM2qSEc/S5Po+6ipJBA4ZlZCMf7dHa6znet1TDpqA9iQ4YcqIHMH
6xZNQ7hhgSAzG9TrXBHqP+djDlrrGWotvjuy0IfS9ixFnnLWjrtAH9afRWLuG+a/
NHNC1M6DiiTE0TzL/lpt/zzut3CNmWzH+t19X6UsxUg95AzooEeewEYkv25eumWD
mfQZfCtSlIw1sp/QwxeJa/6LJw7KcPZ1wXUm1BN0b9eiKt9Cmni1MS7elgpZlgGt
xtfGTZtNLQ7bgDiM8MHzUfPBhbceNSIx2BeCuOCs/7eaqgpyYHBbAbuBQex2H61l
Lcc3Tz0CgYEA4Kx/avpCPxnvsJ+nHVQm5d/WERuDxk4vH1DNuCYBvXTdVCGADf6a
F5No1JcTH3nPTyPWazOyGdT9LcsEJicLyD8vCM6hBFstG4XjqcAuqG/9DRsElpHQ
yi1zc5DNP7Vxmiz9wII0Mjy0abYKtxnXh9YK4a9g6wrcTpvShhIcIb8CgYEAzGzG
lorVCfX9jXULIznnR/uuP5aSnTEsn0xJeqTlbW0RFWLdj8aIL1peirh1X89HroB9
GeTNqEJXD+3CVL2cx+BRggMDUmEz4hR59meZCDGUyT5fex4LIsceb/ESUl2jo6Sw
HXwWbN67rQ55N4oiOcOppsGxzOHkl5HdExKidycCgYEAr5Qev2tz+fw65LzfzHvH
Kj4S/KuT/5V6He731cFd+sEpdmX3vPgLVAFPG1Q1DZQT/rTzDDQKK0XX1cGiLG63
NnaqOye/jbfzOF8Z277kt51NFMDYhRLPKDD82IOA4xjY/rPKWndmcxwdob8yAIWh
efY76sMz6ntCT+xWSZA9i+ECgYBWMZM2TIlxLsBfEbfFfZewOUWKWEGvd9l5vV/K
D5cRIYivfMUw5yPq2267jPUolayCvniBH4E7beVpuPVUZ7KgcEvNxtlytbt7muil
5Z6X3tf+VodJ0Swe2NhTmNEB26uwxzLe68BE3VFCsbSYn2y48HAq+MawPZr18bHG
ZfgMxwKBgHHRg6HYqF5Pegzk1746uH2G+OoCovk5ylGGYzcH2ghWTK4agCHfBcDt
EYqYAev/l82wi+OZ5O8U+qjFUpT1CVeUJdDs0o5u19v0UJjunU1cwh9jsxBZAWLy
PAGd6SWf4S3uQCTw6dLeMna25YIlPh5qPA6I/pAahe8e3nSu2ckl
-----END RSA PRIVATE KEY-----
",
oidcServicePublicKeySig => "-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAs2jsmIoFuWzMkilJaA8/
/5/T30cnuzX9GImXUrFR2k9EKTMtGMHCdKlWOl3BV+BTAU9TLz7Jzd/iJ5GJ6B8T
rH1PHFmHpy8/qE/S5OhinIpIi7ebABqnoVcwDdCa8ugzq8k8SWxhRNXfVIlwz4NH
1caJ8lmiERFj7IvNKqEhzAk0pyDr8hubveTC39xREujKlsqutpPAFPJ3f2ybVsdy
kX5rx0h5SslG3jVWYhZ/SOb2aIzOr0RMjhQmsYRwbpt3anjlBZ98aOzg7GAkbO80
93X5VVk9vaPRg0zxJQ0Do0YLyzkRisSAIFb0tdKuDnjRGK6y/N2j6At2Hjkxntbt
GQIDAQAB
-----END PUBLIC KEY-----
",
}
}
);
}

View File

@ -29,7 +29,6 @@ my $op = LLNG::Manager::Test->new( {
name => "cn"
}
},
oidcServiceMetaDataIssuer => "http://auth.op.com",
oidcServiceMetaDataAuthorizeURI => "authorize",
oidcServiceMetaDataCheckSessionURI => "checksession.html",
oidcServiceMetaDataJWKSURI => "jwks",

View File

@ -29,7 +29,6 @@ my $op = LLNG::Manager::Test->new( {
name => "cn"
}
},
oidcServiceMetaDataIssuer => "http://auth.op.com",
oidcServiceMetaDataAuthorizeURI => "authorize",
oidcServiceMetaDataCheckSessionURI => "checksession.html",
oidcServiceMetaDataJWKSURI => "jwks",

View File

@ -1,13 +1,14 @@
use Test::More;
use strict;
use IO::String;
use JSON qw(from_json);
BEGIN {
require 't/test-lib.pm';
}
my $json = '{
"date": "2016-05-30",
"date": "2016-05-30 15:35:10",
"reference": "testref",
"uid": "dwho",
"title": "Test title",
@ -42,7 +43,7 @@ my $jsonall = '{
my $notifs = q%[{
"uid": "dwho",
"date": "2019-11-15",
"date": "2019-11-15 15:35:10",
"reference": "ABC1",
"title": "You have new authorizations",
"subtitle": "Application 1",
@ -132,6 +133,36 @@ ok( $res->[2]->[0] =~ /"uid":"everyone"/, 'Wildcard found' )
or print STDERR Dumper( $res->[2]->[0] );
count(4);
ok(
$res = $client->_get(
'/notifications/bad_uid', type => 'application/json',
),
'List notifications for bad uid'
);
ok(
$res->[2]->[0] =~ /"reference":"testrefall"/,
'Notification for all users found'
) or print STDERR Dumper( $res->[2]->[0] );
count(2);
ok(
$res = $client->_get(
'/notifications/_all_', type => 'application/json',
),
'List ALL notifications'
);
ok( $json = eval { from_json( $res->[2]->[0] ) }, 'Response is JSON' );
ok( scalar @{ $json->{result} } == 3, 'Three notifications found' )
or print STDERR Dumper($json);
foreach ( @{$json->{result}} ) {
ok( $_->{reference} =~ /^testref/, "Reference \'$_->{reference}\' found" )
or print STDERR Dumper($json);
ok( $_->{uid} =~ /^(dwho|everyone)$/, "UID \'$_->{uid}\' found" )
or print STDERR Dumper($json);
}
count(9);
ok(
$res = $client->_get(
'/notifications/dwho', type => 'application/json',

View File

@ -43,7 +43,7 @@ LWP::Protocol::PSGI->register(
);
my $xml = '<?xml version="1.0" encoding="UTF-8"?>
<root><notification uid="dwho" date="2016-05-30" reference="testref">
<root><notification uid="dwho" date="2016-05-30 15:35:10" reference="testref">
<title>Test title</title>
<subtitle>Test subtitle</subtitle>
<text>This is a test text</text>
@ -57,7 +57,7 @@ my $xmlbis = '<?xml version="1.0" encoding="UTF-8"?>
</notification></root>';
my $combined = '<?xml version="1.0" encoding="UTF-8"?>
<root><notification uid="dwho" date="2016-05-31" reference="ABC1">
<root><notification uid="dwho" date="2016-05-31 15:35:10" reference="ABC1">
<title>Test title</title>
<subtitle>Test subtitle</subtitle>
<text>This is a test text</text>

View File

@ -18,6 +18,7 @@ my $client = LLNG::Manager::Test->new( {
requireToken => 0,
securedCookie => 1,
https => 0,
whatToTrace => 'mail'
}
}
);