Service token server (#971)
This commit is contained in:
parent
e2f4de3f9d
commit
64756142e1
|
@ -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|g(?:r(?:antSessionRule|oup)|lobalStorageOption)|n(?:otificationStorageOption|ginxCustomHandler)|c(?:as(?:StorageOption|Attribute)|ombModule)|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)|SSLVarIf)$/;
|
||||
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)|c(?:as(?:StorageOption|Attribute)|ombModule)|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))|(?:laveExportedVar|TokenScope)s|essionDataToRemember)|p(?:ersistentStorageOptions|o(?:rtalSkinRules|st))|a(?:uthChoiceModules|pplicationList)|v(?:hostOptions|irtualHost)|SSLVarIf)$/;
|
||||
|
||||
our @sessionTypes = ( 'remoteGlobal', 'cas', 'global', 'localSession', 'persistent', 'saml', 'oidc' );
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@ our $specialNodeHash = {
|
|||
};
|
||||
|
||||
our $doubleHashKeys = 'issuerDBGetParameters';
|
||||
our $simpleHashKeys = '(?:(?:l(?:o(?:calSessionStorageOption|goutService)|dapExportedVar|wp(?:Ssl)?Opt)|(?:(?:d(?:emo|bi)|facebook|webID)E|e)xportedVar|g(?:r(?:antSessionRule|oup)|lobalStorageOption)|n(?:otificationStorageOption|ginxCustomHandler)|c(?:as(?:StorageOption|Attribute)|ombModule)|p(?:ersistentStorageOption|ortalSkinRule)|re(?:moteGlobalStorageOption|loadUrl)|CAS_proxiedService|macro)s|o(?:idcS(?:erviceMetaDataAuthnContext|torageOptions)|penIdExportedVars)|s(?:(?:amlStorageOption|laveExportedVar)s|essionDataToRemember)|a(?:uthChoiceModules|pplicationList)|SSLVarIf)';
|
||||
our $simpleHashKeys = '(?:(?:l(?:o(?:calSessionStorageOption|goutService)|dapExportedVar|wp(?:Ssl)?Opt)|(?:(?:d(?:emo|bi)|facebook|webID)E|e)xportedVar|g(?:r(?:antSessionRule|oup)|lobalStorageOption)|n(?:otificationStorageOption|ginxCustomHandler)|c(?:as(?:StorageOption|Attribute)|ombModule)|p(?:ersistentStorageOption|ortalSkinRule)|re(?:moteGlobalStorageOption|loadUrl)|CAS_proxiedService|macro)s|s(?:(?:amlStorageOption|laveExportedVar|TokenScope)s|essionDataToRemember)|o(?:idcS(?:erviceMetaDataAuthnContext|torageOptions)|penIdExportedVars)|a(?:uthChoiceModules|pplicationList)|SSLVarIf)';
|
||||
our $specialNodeKeys = '(?:(?:saml(?:ID|S)|oidc[OR])PMetaDataNode|virtualHost)s';
|
||||
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)|(?:RedirectUri|ExtraClaim)s|AccessTokenExpiration|Client(?:Secret|ID)|BypassConsent|DisplayName|UserIDAttr)|ExportedVars)';
|
||||
|
|
|
@ -2784,6 +2784,13 @@ qr/^(?:(?:(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.])*(?:[a-zA-Z][-a-
|
|||
'staticPrefix' => {
|
||||
'type' => 'text'
|
||||
},
|
||||
'sTokenScopes' => {
|
||||
'type' => 'keyTextContainer'
|
||||
},
|
||||
'sTokenService' => {
|
||||
'default' => 0,
|
||||
'type' => 'bool'
|
||||
},
|
||||
'storePassword' => {
|
||||
'default' => 0,
|
||||
'type' => 'bool'
|
||||
|
|
|
@ -1011,6 +1011,17 @@ sub attributes {
|
|||
'List of attributes to export by SOAP or REST servers',
|
||||
},
|
||||
|
||||
# Service token service
|
||||
sTokenService => {
|
||||
type => 'bool',
|
||||
default => 0,
|
||||
documentation => 'Enable service token service',
|
||||
},
|
||||
sTokenScopes => {
|
||||
type => 'keyTextContainer',
|
||||
documentation => 'List of authorizated virtualHosts',
|
||||
},
|
||||
|
||||
## Virtualhosts
|
||||
|
||||
# Fake attribute: used by manager REST API to agglomerate all other
|
||||
|
|
|
@ -498,10 +498,8 @@ sub tree {
|
|||
},
|
||||
'reloadUrls',
|
||||
{
|
||||
title => 'advancedParams',
|
||||
help => 'start.html#advanced_features',
|
||||
title => 'plugins',
|
||||
nodes => [
|
||||
'customFunctions',
|
||||
'portalStatus',
|
||||
{
|
||||
title => 'portalServers',
|
||||
|
@ -591,6 +589,17 @@ sub tree {
|
|||
form => 'simpleInputContainer',
|
||||
nodes => [ 'u2fActivation', 'u2fSelfRegistration', ]
|
||||
},
|
||||
{
|
||||
title => 'sToken',
|
||||
nodes => [ 'sTokenService', 'sTokenScopes' ]
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
title => 'advancedParams',
|
||||
help => 'start.html#advanced_features',
|
||||
nodes => [
|
||||
'customFunctions',
|
||||
{
|
||||
title => 'security',
|
||||
help => 'security.html#configure_security_settings',
|
||||
|
|
|
@ -503,6 +503,7 @@
|
|||
"persistentSessions": "Persistent sessions",
|
||||
"persistentStorage": "Apache::Session module",
|
||||
"persistentStorageOptions": "Apache::Session module parameters",
|
||||
"plugins": "Plugins",
|
||||
"port": "Port",
|
||||
"portal": "URL",
|
||||
"portalAntiFrame": "Anti frame protection",
|
||||
|
@ -620,6 +621,9 @@
|
|||
"SSLVar": "Extracted certificate field",
|
||||
"SSLVarIf": "Conditional extracted certificate field",
|
||||
"startTime": "Creation date",
|
||||
"sToken": "Service tokens",
|
||||
"sTokenScopes": "Service token scopes",
|
||||
"sTokenService": "Token server activation",
|
||||
"successfullySaved": "Successfully saved",
|
||||
"storePassword": "Store user password in session datas",
|
||||
"successLoginNumber": "Number of registered logins",
|
||||
|
|
|
@ -503,6 +503,7 @@
|
|||
"persistentSessions": "Sessions persistantes",
|
||||
"persistentStorage": "Module Apache::Session",
|
||||
"persistentStorageOptions": "Paramètres du module Apache::Session",
|
||||
"plugins": "Extensions",
|
||||
"port": "Port",
|
||||
"portal": "URL",
|
||||
"portalAntiFrame": "Protection anti frame",
|
||||
|
@ -620,6 +621,9 @@
|
|||
"SSLVar": "Champ extrait du certificat",
|
||||
"SSLVarIf": "Champ conditionnel extrait du certificat",
|
||||
"startTime": "Date de création",
|
||||
"sToken": "Tickets de service",
|
||||
"sTokenScopes": "Portée des tickets de service",
|
||||
"sTokenService": "Activation du server de tickets",
|
||||
"successfullySaved": "Sauvegarde effectuée",
|
||||
"storePassword": "Stocke le mot de passe de l'utilisateur en session",
|
||||
"successLoginNumber": "Nombre de connexions mémorisées",
|
||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -86,6 +86,7 @@ lib/Lemonldap/NG/Portal/Plugins/Notifications.pm
|
|||
lib/Lemonldap/NG/Portal/Plugins/PublicPages.pm
|
||||
lib/Lemonldap/NG/Portal/Plugins/Register.pm
|
||||
lib/Lemonldap/NG/Portal/Plugins/RESTServer.pm
|
||||
lib/Lemonldap/NG/Portal/Plugins/ServiceTokenServer.pm
|
||||
lib/Lemonldap/NG/Portal/Plugins/SingleSession.pm
|
||||
lib/Lemonldap/NG/Portal/Plugins/SOAPServer.pm
|
||||
lib/Lemonldap/NG/Portal/Plugins/Status.pm
|
||||
|
|
|
@ -22,6 +22,7 @@ our @pList = (
|
|||
u2fSelfRegistration => '::Register::U2F',
|
||||
notification => '::Plugins::Notifications',
|
||||
portalCheckLogins => '::Plugins::History',
|
||||
sTokenService => '::Plugins::ServiceTokenServer',
|
||||
);
|
||||
|
||||
##@method list enabledPlugins
|
||||
|
|
|
@ -0,0 +1,92 @@
|
|||
# Token server plugin for underlying requests
|
||||
#
|
||||
# This plugin handle /tokenfor path to give to applications tokens to query
|
||||
# other web applications on behalf of the connected user (second apps are
|
||||
# protected by specific handler).
|
||||
#
|
||||
# 0) Administrator set "sTokenScopes" parameter in the manager. Each entry is
|
||||
# a couple of key/value where:
|
||||
# - key is the name of the list
|
||||
# - value is a comma separated list of virtualHosts authorizated for this
|
||||
# key
|
||||
# Token header are also added for App-1 (application that wants to query
|
||||
# others on behalf of the connected user)
|
||||
# 1) App 1 received a token in headers (header is generated using
|
||||
# "token($uid,'ref')" where ref is a key of "sTokenScopes" configuration
|
||||
# parameter).
|
||||
# 2) It send it to this plugin (request to /tokenfor)
|
||||
# 3) run() method verify that token is available and return a service token that
|
||||
# can be used to request a fixed list of servers. This list is the value of
|
||||
# "tokenScope"->{$ref}
|
||||
# 4) App-1 queries App-2, App-3,... with this token set in "X-Llng-Token" header
|
||||
# 5) App-2 handler verifies that token is valid for this vhost and accept or
|
||||
# not the query
|
||||
|
||||
package Lemonldap::NG::Portal::Plugins::ServiceTokenServer;
|
||||
|
||||
use strict;
|
||||
use Mouse;
|
||||
|
||||
our $VERSION = '2.0.0';
|
||||
|
||||
extends 'Lemonldap::NG::Portal::Main::Plugin';
|
||||
|
||||
# INITIALIZATION
|
||||
|
||||
has tokenScopes => (
|
||||
is => 'rw',
|
||||
default => sub {
|
||||
my $ts = $_[0]->conf->{sTokenScopes} || {};
|
||||
my %h = map {
|
||||
my $v = $ts->{$_};
|
||||
$v =~ s/[, ]+/:/g;
|
||||
( $_ => $v );
|
||||
} keys %$ts;
|
||||
return \%h;
|
||||
}
|
||||
);
|
||||
|
||||
sub init {
|
||||
my ($self) = @_;
|
||||
$self->addUnauthRoute( tokenfor => 'run', ['POST'] );
|
||||
return 1;
|
||||
}
|
||||
|
||||
sub run {
|
||||
my ( $self, $req ) = @_;
|
||||
|
||||
# 1. Recover request token
|
||||
my $reqToken;
|
||||
if ( $req->content_type =~ /json/ ) {
|
||||
my $j;
|
||||
eval { $j = from_json( $req->content ) };
|
||||
if ($@) {
|
||||
return $self->p->sendError( $req, 'Bad request', 403 );
|
||||
}
|
||||
$reqToken = $j->{token};
|
||||
}
|
||||
else {
|
||||
$reqToken = $req->param('token');
|
||||
}
|
||||
unless ($reqToken) {
|
||||
return $self->p->sendError( $req, 'Missing token', 403 );
|
||||
}
|
||||
|
||||
# 2. Uncipher request token
|
||||
my $s = $self->conf->{cipher}->decrypt($reqToken)
|
||||
or return $self->p->sendError( $req, 'Bad token', 403 );
|
||||
|
||||
# 3. Verify time
|
||||
my ( $t, $uid, $ref ) = split /:/, $s;
|
||||
unless ( $t <= time and $t > time - 15 ) {
|
||||
return $self->p->sendError( $req, 'Token expired', 403 );
|
||||
}
|
||||
unless ( $self->tokenScopes->{$ref} ) {
|
||||
return $self->p->sendError( $req, 'Bad reference', 403 );
|
||||
}
|
||||
my $respToken = $self->conf->{cipher}
|
||||
->encrypt( join ':', time, $uid, $self->tokenScopes->{$ref} );
|
||||
return $self->p->sendJSONresponse( $req, { sToken => $respToken } );
|
||||
}
|
||||
|
||||
1;
|
Loading…
Reference in New Issue
Block a user