New Issuer::CAS (#1183)

This commit is contained in:
Xavier Guimard 2017-04-13 19:17:29 +00:00
parent d1d57fae22
commit b83374b274
11 changed files with 84 additions and 35 deletions

View File

@ -24,7 +24,7 @@ our $specialNodeHash = {
our $doubleHashKeys = 'issuerDBGetParameters';
our $simpleHashKeys = '(?:(?:l(?:o(?:calSessionStorageOption|goutService)|dapExportedVar|wp(?:Ssl)?Opt)|c(?:as(?:StorageOption|Attribute)|ustomAddParam|ombModule)|(?:(?:d(?:emo|bi)|facebook|webID)E|e)xportedVar|g(?:r(?:antSessionRule|oup)|lobalStorageOption)|n(?:otificationStorageOption|ginxCustomHandler)|p(?:ersistentStorageOption|ortalSkinRule)|re(?:moteGlobalStorageOption|loadUrl)|macro)s|o(?:idcS(?:erviceMetaDataAuthnContext|torageOptions)|penIdExportedVars)|s(?:(?:amlStorageOption|laveExportedVar)s|essionDataToRemember)|a(?:uthChoiceModules|pplicationList)|S(?:MTPTLSOpts|SLVarIf))';
our $specialNodeKeys = '(?:(?:(?:saml(?:ID|S)|oidc[OR])P|cas(?:App|Srv))MetaDataNode|virtualHost)s';
our $casAppMetaDataNodeKeys = 'casAppMetaData(?:OptionsService|ExportedVars)';
our $casAppMetaDataNodeKeys = 'casAppMetaData(?:Options(?:Servic|Rul)e|ExportedVars)';
our $casSrvMetaDataNodeKeys = 'casSrvMetaData(?:Options(?:ProxiedServices|DisplayName|Gateway|Renew|Icon|Url)|ExportedVars)';
our $oidcOPMetaDataNodeKeys = 'oidcOPMetaData(?:Options(?:C(?:lient(?:Secret|ID)|heckJWTSignature|onfigurationURI)|TokenEndpointAuthMethod|(?:JWKSTimeou|Promp)t|I(?:DTokenMaxAge|con)|S(?:toreIDToken|cope)|U(?:iLocales|seNonce)|Display(?:Name)?|AcrValues|MaxAge)|ExportedVars|J(?:SON|WKS))';
our $oidcRPMetaDataNodeKeys = 'oidcRPMetaData(?:Options(?:I(?:DToken(?:Expiration|SignAlg)|con)|Logout(?:SessionRequired|Type|Url)|AccessTokenExpiration|R(?:edirectUris|ule)|Client(?:Secret|ID)|BypassConsent|DisplayName|ExtraClaims|UserIDAttr)|ExportedVars)';
@ -67,6 +67,5 @@ our $issuerParameters = {
};
our $samlServiceParameters = [qw(samlEntityID samlServicePrivateKeySig samlServicePrivateKeySigPwd samlServicePublicKeySig samlServicePrivateKeyEnc samlServicePrivateKeyEncPwd samlServicePublicKeyEnc samlServiceUseCertificateInResponse samlNameIDFormatMapEmail samlNameIDFormatMapX509 samlNameIDFormatMapWindows samlNameIDFormatMapKerberos samlAuthnContextMapPassword samlAuthnContextMapPasswordProtectedTransport samlAuthnContextMapTLSClient samlAuthnContextMapKerberos samlOrganizationDisplayName samlOrganizationName samlOrganizationURL samlSPSSODescriptorAuthnRequestsSigned samlSPSSODescriptorWantAssertionsSigned samlSPSSODescriptorSingleLogoutServiceHTTPRedirect samlSPSSODescriptorSingleLogoutServiceHTTPPost samlSPSSODescriptorSingleLogoutServiceSOAP samlSPSSODescriptorAssertionConsumerServiceHTTPArtifact samlSPSSODescriptorAssertionConsumerServiceHTTPPost samlSPSSODescriptorArtifactResolutionServiceArtifact samlIDPSSODescriptorWantAuthnRequestsSigned samlIDPSSODescriptorSingleSignOnServiceHTTPRedirect samlIDPSSODescriptorSingleSignOnServiceHTTPPost samlIDPSSODescriptorSingleSignOnServiceHTTPArtifact samlIDPSSODescriptorSingleLogoutServiceHTTPRedirect samlIDPSSODescriptorSingleLogoutServiceHTTPPost samlIDPSSODescriptorSingleLogoutServiceSOAP samlIDPSSODescriptorArtifactResolutionServiceArtifact samlAttributeAuthorityDescriptorAttributeServiceSOAP samlIdPResolveCookie samlMetadataForceUTF8 samlStorage samlStorageOptions samlRelayStateTimeout samlUseQueryStringSpecific samlCommonDomainCookieActivation samlCommonDomainCookieDomain samlCommonDomainCookieReader samlCommonDomainCookieWriter)];
our $oidcServiceParameters = [qw(oidcServiceMetaDataIssuer oidcServiceMetaDataAuthorizeURI oidcServiceMetaDataTokenURI oidcServiceMetaDataUserInfoURI oidcServiceMetaDataJWKSURI oidcServiceMetaDataRegistrationURI oidcServiceMetaDataEndSessionURI oidcServiceMetaDataCheckSessionURI oidcServiceMetaDataFrontChannelURI oidcServiceMetaDataBackChannelURI oidcServiceMetaDataAuthnContext oidcServicePrivateKeySig oidcServicePublicKeySig oidcServiceKeyIdSig oidcServiceAllowDynamicRegistration oidcServiceAllowAuthorizationCodeFlow oidcServiceAllowImplicitFlow oidcServiceAllowHybridFlow oidcStorage oidcStorageOptions)];
our $casServiceParameters = [qw()];
1;

View File

@ -640,6 +640,25 @@ sub attributes {
'casAppMetaDataOptions' => {
'type' => 'subContainer'
},
'casAppMetaDataOptionsRule' => {
'test' => sub {
my ( $val, $conf ) = @_;
my $s = '';
BEGIN {
${^WARNING_BITS} =
"\x54\x55\x55\x55\x15\x55\x55\x55\x55\x55\x51\x55\x55\x55\x55\x55\x55";
}
eval "$s $val";
my $err = join(
'',
grep( { $_ =~ /Undefined subroutine/ ? () : $_; }
split( /\n/, $@, 0 ) )
);
return $err ? ( 1, "__badExpression__: $err" ) : 1;
},
'type' => 'text'
},
'casAppMetaDataOptionsService' => {
'type' => 'url'
},

View File

@ -214,7 +214,7 @@ EOF
}
print F "$tmp};\n";
}
foreach (qw(samlServiceParameters oidcServiceParameters casServiceParameters)) {
foreach (qw(samlServiceParameters oidcServiceParameters)) {
no strict 'refs';
$tmp = "our \$$_ = [qw(" . join( ' ', @$$_ ) . ")];\n";
print F "$tmp";

View File

@ -1237,16 +1237,21 @@ sub attributes {
},
# Partners
casAppMetaDataOptions => { type => 'subContainer', },
casAppMetaDataOptions => { type => 'subContainer', },
casAppMetaDataExportedVars => {
type => 'keyTextContainer',
default => { cn => 'cn', mail => 'mail', uid => 'uid', },
documentation => 'CAS exported variables',
},
casAppMetaDataOptionsService => {
type => 'url',
type => 'url',
documentation => 'CAS App service',
},
casAppMetaDataOptionsRule => {
type => 'text',
test => $perlExpr,
documentation => 'CAS App rule',
},
# Fake attribute: used by manager REST API to agglomerate all nodes
# related to a SAML SP partner
@ -2083,7 +2088,7 @@ m{^(?:ldapi://[^/]*/?|\w[\w\-\.]*(?::\d{1,5})?|ldap(?:s|\+tls)?://\w[\w\-\.]*(?:
default => { cn => 'cn', mail => 'mail', uid => 'uid', },
documentation => 'CAS exported variables',
},
casSrvMetaDataOptions => { type => 'subContainer', },
casSrvMetaDataOptions => { type => 'subContainer', },
casSrvMetaDataOptionsGateway => { type => 'bool', },
casSrvMetaDataOptionsProxiedServices => {
type => 'keyTextContainer',
@ -2097,11 +2102,11 @@ m{^(?:ldapi://[^/]*/?|\w[\w\-\.]*(?::\d{1,5})?|ldap(?:s|\+tls)?://\w[\w\-\.]*(?:
msgFail => '__badUrl__',
},
casSrvMetaDataOptionsDisplayName => {
type => 'text',
type => 'text',
documentation => 'Name to display for CAS server',
},
casSrvMetaDataOptionsIcon => {
type => 'text',
type => 'text',
documentation => 'Path of CAS Server Icon',
},

View File

@ -231,7 +231,10 @@ sub cTrees {
casAppMetaDataNode => [
{
title => 'casAppMetaDataOptions',
nodes => ['casAppMetaDataOptionsService']
nodes => [
'casAppMetaDataOptionsService',
'casAppMetaDataOptionsRule'
]
},
'casAppMetaDataExportedVars',
],

View File

@ -16,6 +16,11 @@ function templates(tpl,key) {
"get" : tpl+"s/"+key+"/"+"casAppMetaDataOptionsService",
"id" : tpl+"s/"+key+"/"+"casAppMetaDataOptionsService",
"title" : "casAppMetaDataOptionsService"
},
{
"get" : tpl+"s/"+key+"/"+"casAppMetaDataOptionsRule",
"id" : tpl+"s/"+key+"/"+"casAppMetaDataOptionsRule",
"title" : "casAppMetaDataOptionsRule"
}
],
"id" : "casAppMetaDataOptions",

File diff suppressed because one or more lines are too long

View File

@ -108,6 +108,7 @@
"casAppMetaDataNodes": "CAS Applications",
"casAppMetaDataOptions": "Options",
"casAppMetaDataOptionsService": "Service URL",
"casAppMetaDataOptionsRule": "Rule",
"casAppName": "CAS App Name",
"casAttr": "CAS login",
"casAttributes": "CAS exported attributes",

View File

@ -108,6 +108,7 @@
"casAppMetaDataNodes": "Applications CAS",
"casAppMetaDataOptions": "Options",
"casAppMetaDataOptionsService": "URL du service",
"casAppMetaDataOptionsRule": "Règle",
"casAppName": "Nom de l'application CAS",
"casAttr": "Identifiant CAS",
"casAttributes": "Attributs CAS",

View File

@ -27,6 +27,7 @@ sub init {
# Launch parents initialization subroutines, then launch IdP en SP lists
my $res = $self->Lemonldap::NG::Portal::Main::Issuer::init();
return 0 unless($self->loadApp);
$self->addUnauthRoute(
( $self->path ) => {
serviceValidate => 'serviceValidate',
@ -108,35 +109,37 @@ sub run {
"CAS access control requested on service $service");
## HERE
unless ( $service =~ m#^https?://([^/]+)(/.*)?$# ) {
unless ( $service =~ m#^(https?://[^/]+)(/.*)?$# ) {
$self->logger->error("Bad service $service");
return PE_ERROR;
}
my ( $host, $uri ) = ( $1, $2 );
if (
$self->p->HANDLER->grant(
$req, $req->sessionInfo, $1, undef, $2
)
)
{
$self->logger->debug("CAS service $service access allowed");
my $app;
unless($app = $self->casAppList->{$host} ) {
$self->userLogger->error('CAS service not configured');
return PE_CAS_SERVICE_NOT_ALLOWED;
}
else {
$self->userLogger->error(
"CAS service $service access not allowed");
if ( $casAccessControlPolicy =~ /^(error)$/i ) {
$self->logger->debug(
"Return error instead of redirecting user on CAS service"
);
return PE_CAS_SERVICE_NOT_ALLOWED;
if ( my $rule = $self->appRules->{$app} ) {
if($rule->($req, $req->sessionInfo ) ) {
$self->logger->debug("CAS service $service access allowed");
}
else {
$self->logger->debug(
"Redirect user on CAS service with a fake ticket");
$casServiceTicket = "ST-F4K3T1CK3T";
$self->userLogger->error(
"CAS service $service access not allowed");
if ( $casAccessControlPolicy =~ /^(error)$/i ) {
$self->logger->debug(
"Return error instead of redirecting user on CAS service"
);
return PE_CAS_SERVICE_NOT_ALLOWED;
}
else {
$self->logger->debug(
"Redirect user on CAS service with a fake ticket");
$casServiceTicket = "ST-F4K3T1CK3T";
}
}
}
}

View File

@ -25,6 +25,7 @@ has ua => (
has casSrvList => ( is => 'rw', default => sub { {} }, );
has casAppList => ( is => 'rw', default => sub { {} }, );
has appRules => ( is => 'rw', default => sub { {} }, );
# RUNNING METHODS
@ -50,9 +51,21 @@ sub loadApp {
}
foreach ( keys %{ $self->conf->{casAppMetaDataOptions} } ) {
my $tmp =
$self->conf->{casAppMetaDataOptions}->{casAppMetaDataOptionsService};
$self->conf->{casAppMetaDataOptions}->{$_}
->{casAppMetaDataOptionsService};
$tmp =~ s#^(https?://[^/]+).*$#$1#;
$self->casAppList->{$tmp} = $_;
my $rule = $self->conf->{casAppMetaDataOptions}->{$_}
->{casAppMetaDataOptionsRule};
if ( length $rule ) {
$rule = $self->p->HANDLER->substitute($rule);
unless ( $rule = $self->p->HANDLER->buildSub($rule) ) {
$self->error( 'OIDC RP rule error: '
. $self->p->HANDLER->tsv->{jail}->error );
return 0;
}
$self->appRules->{$_} = $rule;
}
}
return 1;
}
@ -315,7 +328,7 @@ sub validateST {
my $proxied = $srvConf->{casSrvMetaDataOptionsProxiedServices} || {};
my $proxy_url;
if(%$proxied) {
if (%$proxied) {
$proxy_url = $self->p->fullUrl($req) . '?casProxy=1';
if ( $self->conf->{authChoiceParam}
and my $tmp = $req->param( $self->conf->{authChoiceParam} ) )
@ -327,7 +340,7 @@ sub validateST {
$req->datas->{casProxyUrl} = $proxy_url;
$serviceValidateUrl .= "&pgtUrl=" . uri_escape( $proxy_url );
$serviceValidateUrl .= "&pgtUrl=" . uri_escape($proxy_url);
}
my $response = $self->ua->get($serviceValidateUrl);
@ -348,7 +361,7 @@ sub validateST {
}
# Get proxy data and store pgtId
if ( $proxy_url ) {
if ($proxy_url) {
my $pgtIou =
$xml->{'cas:authenticationSuccess'}->{'cas:proxyGrantingTicket'};