Prepare CAS partners managment (#1183)

This commit is contained in:
Xavier Guimard 2017-04-12 16:45:49 +00:00
parent 43ece77749
commit 679911d4d0
24 changed files with 275 additions and 25 deletions

View File

@ -183,6 +183,7 @@ MANAGERJSONSRC= scripts/jsongenerator.pl \
$(SRCMANAGERDIR)/lib/Lemonldap/NG/Manager/Build.pm \
$(SRCMANAGERDIR)/lib/Lemonldap/NG/Manager/Build/Attributes.pm \
$(SRCMANAGERDIR)/lib/Lemonldap/NG/Manager/Build/Tree.pm \
$(SRCMANAGERDIR)/lib/Lemonldap/NG/Manager/Build/CTrees.pm \
$(SRCMANAGERDIR)/lib/Lemonldap/NG/Manager/Conf/Zero.pm
MANAGERJSONDST=$(SRCMANAGERDIR)/site/htdocs/static/struct.json \
$(SRCMANAGERDIR)/site/htdocs/static/js/conftree.js \

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))r|c(?:as(?:StorageOption|Attribute)|ustomAddParam|ombModule)|g(?:r(?:antSessionRule|oup)|lobalStorageOption)|n(?:otificationStorageOption|ginxCustomHandler)|re(?:moteGlobalStorageOption|loadUrl)|CAS_proxiedService|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)|p(?:ersistentStorageOptions|o(?:rtalSkinRules|st))|a(?:uthChoiceModules|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))r|g(?:r(?:antSessionRule|oup)|lobalStorageOption)|n(?:otificationStorageOption|ginxCustomHandler)|re(?:moteGlobalStorageOption|loadUrl)|CAS_proxiedService|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)|c(?:as(?:S(?:rvMetaDataNode|torageOptions)|A(?:ppMetaDataNode|ttributes))|(?:ustomAddParam|ombModule)s)|p(?:ersistentStorageOptions|o(?:rtalSkinRules|st))|a(?:uthChoiceModules|pplicationList)|v(?:hostOptions|irtualHost)|S(?:MTPTLSOpts|SLVarIf))$/;
our @sessionTypes = ( 'remoteGlobal', 'cas', 'global', 'localSession', 'persistent', 'saml', 'oidc' );

View File

@ -444,6 +444,65 @@ sub oidcRPMetaDataNodes {
return $self->_oidcMetaDataNodes( 'RP', $req, @path );
}
# 314 - CAS
# ---
sub _casMetaDataNodes {
my ( $self, $type, $req, @path ) = @_;
my $refKey =
( $type eq 'App' ? 'casAppMetaDataOptions' : 'casSrvMetaDataOptions' );
return $self->complexNodesRoot( $req, $refKey, "oidc${type}MetaDataNode" )
unless (@path);
my $partner = shift @path;
my $query = shift @path;
unless ($query) {
return $self->sendError( $req,
"Bad request: oidc${type}MetaDataNode query must ask for a key",
400 );
}
# setDefault response for new partners
return $self->sendError( $req, 'setDefault', 200 )
if ( $partner =~ /^new__/ );
# Reject unknown partners
return $self->sendError( $req, "Unknown CAS partner ($partner)", 400 )
unless ( defined eval { $self->getConfKey( $req, $refKey )->{$partner}; }
);
my ( $id, $resp ) = ( 1, [] );
# Options
if (
$query =~ {
App => qr/^$casAppMetaDataNodeKeys$/o,
Srv => qr/^$casSrvMetaDataNodeKeys$/o
}->{$type}
)
{
my $value = eval {
$self->getConfKey( $req, "cas${type}MetaDataOptions" )->{$partner}
->{$query};
} // undef;
return $self->sendJSONresponse( $req, { value => $value } );
}
else {
return $self->sendError( $req,
"Bad key for cas${type}MetaDataNode ($query)", 400 );
}
}
sub casSrvMetaDataNodes {
my ( $self, $req, @path ) = @_;
return $self->_oidcMetaDataNodes( 'Srv', $req, @path );
}
sub casAppMetaDataNodes {
my ( $self, $req, @path ) = @_;
return $self->_oidcMetaDataNodes( 'App', $req, @path );
}
# 32 - Other special nodes
# -------------------
@ -654,7 +713,9 @@ sub metadatas {
my $id = -1;
my ($ind) = map { $id++; $_ == $res->{cfgNum} ? ($id) : () } @a;
if ($ind) { $res->{prev} = $a[ $ind - 1 ]; }
if ( defined $ind and $ind < $#a ) { $res->{next} = $a[ $ind + 1 ]; }
if ( defined $ind and $ind < $#a ) {
$res->{next} = $a[ $ind + 1 ];
}
if ( $self->can('userId') ) {
$self->userLogger->info( 'User '
. $self->userId($req)
@ -664,7 +725,8 @@ sub metadatas {
}
else {
$self->logger->info(
"REST request to get configuration metadatas ($res->{cfgNum})");
"REST request to get configuration metadatas ($res->{cfgNum})"
);
}
return $self->sendJSONresponse( $req, $res );
}
@ -730,7 +792,8 @@ sub getKey {
# When scalar
return $self->sendError( $req, "Key $key is not a hash", 400 )
if ($subkey);
return $self->sendError( $req, 'setDefault', 200 ) unless defined($value);
return $self->sendError( $req, 'setDefault', 200 )
unless defined($value);
return $self->sendJSONresponse( $req, { value => $value } );
# TODO authParam key

View File

@ -82,6 +82,10 @@ site/htdocs/static/forms/authParamsTextContainer.html
site/htdocs/static/forms/blackWhiteList.html
site/htdocs/static/forms/bool.html
site/htdocs/static/forms/boolOrExpr.html
site/htdocs/static/forms/casAppMetaDataNode.html
site/htdocs/static/forms/casAppMetaDataNodeContainer.html
site/htdocs/static/forms/casSrvMetaDataNode.html
site/htdocs/static/forms/casSrvMetaDataNodeContainer.html
site/htdocs/static/forms/catAndAppList.html
site/htdocs/static/forms/cmbModule.html
site/htdocs/static/forms/cmbModuleContainer.html

View File

@ -626,12 +626,6 @@ sub attributes {
'CAS_renew' => {
'type' => 'bool'
},
'CAS_url' => {
'msgFail' => '__badUrl__',
'test' =>
qr/(?:(?:https?):\/\/(?:(?:(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.])*(?:[a-zA-Z][-a-zA-Z0-9]*[a-zA-Z0-9]|[a-zA-Z])[.]?)|(?:[0-9]+[.][0-9]+[.][0-9]+[.][0-9]+)))(?::(?:(?:[0-9]*)))?(?:\/(?:(?:(?:(?:(?:(?:[a-zA-Z0-9\-_.!~*'():@&=+\$,]+|(?:%[a-fA-F0-9][a-fA-F0-9]))*)(?:;(?:(?:[a-zA-Z0-9\-_.!~*'():@&=+\$,]+|(?:%[a-fA-F0-9][a-fA-F0-9]))*))*)(?:\/(?:(?:(?:[a-zA-Z0-9\-_.!~*'():@&=+\$,]+|(?:%[a-fA-F0-9][a-fA-F0-9]))*)(?:;(?:(?:[a-zA-Z0-9\-_.!~*'():@&=+\$,]+|(?:%[a-fA-F0-9][a-fA-F0-9]))*))*))*))(?:[?](?:(?:(?:[;\/?:@&=+\$,a-zA-Z0-9\-_.!~*'()]+|(?:%[a-fA-F0-9][a-fA-F0-9]))*)))?))?)/,
'type' => 'text'
},
'casAccessControlPolicy' => {
'default' => 'none',
'select' => [
@ -650,12 +644,24 @@ qr/(?:(?:https?):\/\/(?:(?:(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.]
],
'type' => 'select'
},
'casAppMetaDataNodes' => {
'type' => 'casAppMetaDataNodeContainer'
},
'casAttr' => {
'type' => 'text'
},
'casAttributes' => {
'type' => 'keyTextContainer'
},
'casSrvMetaDataNodes' => {
'type' => 'casSrvMetaDataNodeContainer'
},
'casSrvMetaDataOptionsUrl' => {
'msgFail' => '__badUrl__',
'test' =>
qr/(?:(?:https?):\/\/(?:(?:(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.])*(?:[a-zA-Z][-a-zA-Z0-9]*[a-zA-Z0-9]|[a-zA-Z])[.]?)|(?:[0-9]+[.][0-9]+[.][0-9]+[.][0-9]+)))(?::(?:(?:[0-9]*)))?(?:\/(?:(?:(?:(?:(?:(?:[a-zA-Z0-9\-_.!~*'():@&=+\$,]+|(?:%[a-fA-F0-9][a-fA-F0-9]))*)(?:;(?:(?:[a-zA-Z0-9\-_.!~*'():@&=+\$,]+|(?:%[a-fA-F0-9][a-fA-F0-9]))*))*)(?:\/(?:(?:(?:[a-zA-Z0-9\-_.!~*'():@&=+\$,]+|(?:%[a-fA-F0-9][a-fA-F0-9]))*)(?:;(?:(?:[a-zA-Z0-9\-_.!~*'():@&=+\$,]+|(?:%[a-fA-F0-9][a-fA-F0-9]))*))*))*))(?:[?](?:(?:(?:[;\/?:@&=+\$,a-zA-Z0-9\-_.!~*'()]+|(?:%[a-fA-F0-9][a-fA-F0-9]))*)))?))?)/,
'type' => 'text'
},
'casStorage' => {
'type' => 'PerlModule'
},

View File

@ -2065,12 +2065,28 @@ m{^(?:ldapi://[^/]*/?|\w[\w\-\.]*(?::\d{1,5})?|ldap(?:s|\+tls)?://\w[\w\-\.]*(?:
keyMsgFail => '__badCasProxyId__',
},
CAS_renew => { type => 'bool', },
CAS_url => {
casSrvMetaDataOptionsUrl => {
type => 'text',
test => $url,
msgFail => '__badUrl__',
},
# Fake attribute: used by manager REST API to agglomerate all nodes
# related to a SAML IDP partner
casSrvMetaDataNodes => {
type => 'casSrvMetaDataNodeContainer',
template => 'casSrvMetaDataNode',
},
# Fake attribute: used by manager REST API to agglomerate all nodes
# related to a SAML SP partner
casAppMetaDataNodes => {
type => 'casAppMetaDataNodeContainer',
help => 'authsaml.html',
template => 'casAppMetaDataNode',
},
# PAM
pamAuthnLevel => {
type => 'int',

View File

@ -214,6 +214,11 @@ sub cTrees {
},
'oidcRPMetaDataOptionsExtraClaims',
],
casSrvMetaDataNode => [
'casSrvMetaDataOptionsUrl',
],
casAppMetaDataNode => [
],
};
}

View File

@ -122,9 +122,9 @@ sub tree {
title => 'casParams',
help => 'authcas.html',
nodes => [
'CAS_authnLevel', 'CAS_url',
'CAS_CAFile', 'CAS_renew',
'CAS_gateway', 'CAS_proxiedServices'
'CAS_authnLevel', 'CAS_CAFile',
'CAS_renew', 'CAS_gateway',
'CAS_proxiedServices'
]
},
{
@ -911,6 +911,8 @@ sub tree {
},
'oidcOPMetaDataNodes',
'oidcRPMetaDataNodes',
'casSrvMetaDataNodes',
'casAppMetaDataNodes',
];
}

View File

@ -43,6 +43,7 @@ sub addRoutes {
':cfgNum' => [
qw(virtualHosts samlIDPMetaDataNodes samlSPMetaDataNodes
applicationList oidcOPMetaDataNodes oidcRPMetaDataNodes
casSrvMetaDataNodes casAppMetaDataNodes
authChoiceModules grantSessionRules combModules
openIdIDPList)
]

View File

@ -426,6 +426,23 @@ sub _scanNodes {
}
next;
}
# CAS
elsif ( $base =~ /^cas(?:App|Srv)MetaDataNodes$/ ) {
my $optKey = $&;
hdebug('CAS');
if ( $target =~
/^(?:$casSrvMetaDataNodeKeys|$casAppMetaDataNodeKeys)/o )
{
$self->set( $optKey, [ $oldName, $key ],
$target, $leaf->{data} );
}
else {
push @{ $self->errors },
{ message => "Unknown OIDC metadata option $target" };
return 0;
}
}
else {
push @{ $self->errors },
{ message => "Fatal: unknown special sub node $base" };
@ -752,7 +769,8 @@ sub _scanNodes {
@oldKeys = keys %{ $self->refConf->{$name}->{$host} };
}
foreach my $prm ( @{ $getHost->{h} } ) {
$self->newConf->{$name}->{$host}->{ $prm->{k} } = $prm->{v};
$self->newConf->{$name}->{$host}->{ $prm->{k} } =
$prm->{v};
if (
!$change
and (

View File

@ -373,6 +373,12 @@ llapp.controller 'TreeCtrl', [
$scope.addOidcRp = ->
$scope.newTemplateNode 'oidcRPMetaDataNode', 'oidcRPName', 'rp-example'
$scope.addCasSrv = ->
$scope.newTemplateNode 'casSrvMetaDataNode', 'casPartnerName', 'srv-example'
$scope.addCasApp = ->
$scope.newTemplateNode 'casAppMetaDataNode', 'casPartnerName', 'app-example'
$scope.newTemplateNode = (type, title, init) ->
$scope.message =
title: title

View File

@ -0,0 +1,21 @@
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title" trspan="casApp"></h3>
</div>
<table class="table">
<tr>
<th><span trspan="casAppName"></span></th>
<td><input id="hashkeyinput" class="form-control" ng-model="currentNode.title"/></td>
</tr>
</table>
</div>
<script type="text/menu">
[{
"title": "addAppCasPartner",
"action": "addCasSrv",
"icon": "plus-sign"
},{
"title": "deleteEntry",
"icon": "minus-sign"
}]
</script>

View File

@ -0,0 +1,26 @@
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title" trspan="casAppMetaDataNodes"></h3>
</div>
<table class="table table-striped">
<thead>
<tr><th trspan="casAppName"></th><th></th></tr>
</thead>
<tbody>
<tr ng-repeat="s in currentNode.nodes">
<td><input class="form-control" ng-model="s.title"/></td>
<td>
<span class="link text-danger glyphicon glyphicon-minus-sign" ng-click="del(currentNode.nodes,$index)"/>
<span ng-if="$last" class="link text-success glyphicon glyphicon-plus-sign" ng-click="menuClick({title:'addAppCasPartner',action:'addCasApp'})"/>
</td>
</tr>
</tbody>
</table>
</div>
<script type="text/menu">
[{
"title": "addAppCasPartner",
"action": "addCasApp",
"icon": "plus-sign"
}]
</script>

View File

@ -0,0 +1,21 @@
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title" trspan="casSrv"></h3>
</div>
<table class="table">
<tr>
<th><span trspan="casSrvName"></span></th>
<td><input id="hashkeyinput" class="form-control" ng-model="currentNode.title"/></td>
</tr>
</table>
</div>
<script type="text/menu">
[{
"title": "addSrvCasPartner",
"action": "addCasSrv",
"icon": "plus-sign"
}, {
"title": "deleteEntry",
"icon": "plus-sign"
}]
</script>

View File

@ -0,0 +1,26 @@
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title" trspan="casSrvMetaDataNodes"></h3>
</div>
<table class="table table-striped">
<thead>
<tr><th trspan="casSrvName"></th><th></th></tr>
</thead>
<tbody>
<tr ng-repeat="s in currentNode.nodes">
<td><input class="form-control" ng-model="s.title"/></td>
<td>
<span class="link text-danger glyphicon glyphicon-minus-sign" ng-click="del(currentNode.nodes,$index)"/>
<span ng-if="$last" class="link text-success glyphicon glyphicon-plus-sign" ng-click="menuClick({title:'addSrvCasPartner',action:'addCasSrv'})"/>
</td>
</tr>
</tbody>
</table>
</div>
<script type="text/menu">
[{
"title": "addSrvCasPartner",
"action": "addCasSrv",
"icon": "plus-sign"
}]
</script>

View File

@ -8,6 +8,18 @@ function templates(tpl,key) {
};
};
switch(tpl){
case 'casAppMetaDataNode':
return []
;
case 'casSrvMetaDataNode':
return [
{
"get" : tpl+"s/"+key+"/"+"casSrvMetaDataOptionsUrl",
"id" : tpl+"s/"+key+"/"+"casSrvMetaDataOptionsUrl",
"title" : "casSrvMetaDataOptionsUrl"
}
]
;
case 'oidcOPMetaDataNode':
return [
{

File diff suppressed because one or more lines are too long

View File

@ -446,6 +446,12 @@ This file contains:
$scope.addOidcRp = function() {
return $scope.newTemplateNode('oidcRPMetaDataNode', 'oidcRPName', 'rp-example');
};
$scope.addCasSrv = function() {
return $scope.newTemplateNode('casSrvMetaDataNode', 'casPartnerName', 'srv-example');
};
$scope.addCasApp = function() {
return $scope.newTemplateNode('casAppMetaDataNode', 'casPartnerName', 'app-example');
};
$scope.newTemplateNode = function(type, title, init) {
$scope.message = {
title: title,

File diff suppressed because one or more lines are too long

View File

@ -26,11 +26,13 @@
"_utime": "Session timestamp",
"actives": "Actives",
"activeTimer": "Auto accept time",
"addSamlAttribute": "Add attribute",
"addAppCasPartner": "Add CAS application",
"addIDPSamlPartner": "Add SAML IDP",
"addSPSamlPartner": "Add SAML SP",
"addOidcOp": "Add OpenID Connect Provider",
"addOidcRp": "Add OpenID Relying Party",
"addSamlAttribute": "Add attribute",
"addSPSamlPartner": "Add SAML SP",
"addSrvCasPartner": "Add CAS server",
"addVhost": "Add virtualhost",
"adParams": "Active Directory Parameters",
"ADPwdExpireWarning": "Password expire warning",
@ -104,11 +106,17 @@
"CAS_gateway": "Gateway authentication",
"CAS_renew": "Renew authentication",
"CAS_proxiedServices": "Proxied services",
"CAS_url": "Server URL",
"casAccessControlPolicy": "Access control policy",
"casApp": "CAS Application",
"casAppMetaDataNodes": "CAS Applications",
"casAppName": "CAS App Name",
"casAttr": "CAS login",
"casAttributes": "CAS exported attributes",
"casParams": "CAS parameters",
"casSrv": "CAS Server",
"casSrvMetaDataOptionsUrl": "Server URL",
"casSrvMetaDataNodes": "CAS Servers",
"casSrvName": "CAS Server Name",
"casStorage": "CAS sessions module name",
"casStorageOptions": "CAS sessions module options",
"categoryName": "Category name",

View File

@ -26,11 +26,13 @@
"_utime": "Tampon de la session",
"actives": "Actives",
"activeTimer": "Délai d'acceptation automatique",
"addSamlAttribute": "Ajouter un attribut",
"addAppCasPartner": "Ajouter une application CAS",
"addIDPSamlPartner": "Ajouter un IDP SAML",
"addSPSamlPartner": "Ajouter un SP SAML",
"addOidcOp": "Ajouter un fournisseur OpenID Connect",
"addOidcRp": "Ajouter un client OpenID Connect",
"addSamlAttribute": "Ajouter un attribut",
"addSPSamlPartner": "Ajouter un SP SAML",
"addSrvCasPartner": "Ajouter un serveur CAS",
"addVhost": "Ajouter un hôte virtuel",
"adParams": "Paramètres Active Directory",
"ADPwdExpireWarning": "Avertissement avant expiration du mot de passe",
@ -104,11 +106,17 @@
"CAS_gateway": "Authentification transparente",
"CAS_renew": "Renouveller l'authentication",
"CAS_proxiedServices": "Services mandatés",
"CAS_url": "URL du serveur",
"casAccessControlPolicy": "Politique de contrôle d'accès",
"casApp": "Application CAS",
"casAppMetaDataNodes": "Applications CAS",
"casAppName": "Nom de l'application CAS",
"casAttr": "Identifiant CAS",
"casAttributes": "Attributs CAS",
"casParams": "Paramètres CAS",
"casSrv": "Serveur CAS",
"casSrvMetaDataOptionsUrl": "URL du serveur",
"casSrvMetaDataNodes": "Serveurs CAS",
"casSrvName": "Nom du serveur CAS",
"casStorage": "Nom du module des session CAS",
"casStorageOptions": "Options du module des sessions CAS",
"categoryName": "Nom de la catégorie",

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long