Append an option to download rules.json file from remote server (#2458)

This commit is contained in:
Christophe Maudoux 2021-02-16 00:40:57 +01:00
parent e477a1cef8
commit 1f66e03969
24 changed files with 110 additions and 22 deletions

View File

@ -19,7 +19,7 @@ dirName=__pwd__/e2e-tests/conf
[portal]
checkXSS = 0
checkXSS = 1
portalSkin = bootstrap
staticPrefix = /static
languages = fr, en, vi, it, ar, de, zh, nl, es, pt, ro, tr, zh_TW

View File

@ -5,7 +5,7 @@ use strict;
use Exporter 'import';
use base qw(Exporter);
our $VERSION = '2.0.11';
our $VERSION = '2.0.12';
# CONSTANTS
@ -30,7 +30,7 @@ use constant DEFAULTCONFBACKENDOPTIONS => (
dirName => '/usr/local/lemonldap-ng/data/conf',
);
our $hashParameters = qr/^(?:(?:l(?:o(?:ca(?:lSessionStorageOption|tionRule)|goutService)|dapExportedVar|wp(?:Ssl)?Opt)|(?:(?:d(?:emo|bi)|webID)ExportedVa|exported(?:Heade|Va)|issuerDBGetParamete)r|f(?:indUser(?:Exclud|Search)ingAttribute|acebookExportedVar)|re(?:moteGlobalStorageOption|st2f(?:Verify|Init)Arg|loadUrl)|g(?:r(?:antSessionRule|oup)|lobalStorageOption)|n(?:otificationStorageOption|ginxCustomHandler)|macro)s|o(?:idc(?:S(?:ervice(?:DynamicRegistrationEx(?:portedVar|traClaim)s|MetaDataAuthnContext)|torageOptions)|RPMetaData(?:(?:Option(?:sExtraClaim)?|ExportedVar|Macro)s|Node)|OPMetaData(?:(?:ExportedVar|Option)s|J(?:SON|WKS)|Node))|penIdExportedVars)|c(?:as(?:A(?:ppMetaData(?:(?:ExportedVar|Option|Macro)s|Node)|ttributes)|S(?:rvMetaData(?:(?:ExportedVar|Option)s|Node)|torageOptions))|(?:ustom(?:Plugins|Add)Param|heckUserHiddenHeader|ombModule)s)|s(?:aml(?:S(?:PMetaData(?:(?:ExportedAttribute|Option|Macro)s|Node|XML)|torageOptions)|IDPMetaData(?:(?:ExportedAttribute|Option)s|Node|XML))|essionDataToRemember|laveExportedVars|fExtra)|a(?:(?:daptativeAuthenticationLevelR|ut(?:hChoiceMod|oSigninR))ules|pplicationList)|p(?:ersistentStorageOptions|o(?:rtalSkinRules|st))|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)|f(?:RemovedUseNotif|OnlyUpgrade)|kip(?:Upgrade|Renew)Confirmation|oap(?:Session|Config)Server|t(?:ayConnecte|orePasswor)d|laveDisplayLogo|howLanguages|slByAjax)|o(?:idc(?:RPMetaDataOptions(?:A(?:llow(?:(?:ClientCredentials|Password)Grant|Offline)|ccessToken(?:Claims|JWT))|Re(?:freshToken|quirePKCE)|LogoutSessionRequired|IDTokenForceClaims|BypassConsent|Public)|ServiceAllow(?:(?:AuthorizationCode|Implicit|Hybrid)Flow|DynamicRegistration)|OPMetaDataOptions(?:(?:CheckJWTSignatur|UseNonc)e|StoreIDToken))|ldNotifFormat)|p(?:ortal(?:Display(?:Re(?:freshMyRights|setPassword|gister)|CertificateResetByMail|GeneratePassword|PasswordPolicy)|ErrorOn(?:ExpiredSession|MailNotFound)|(?:CheckLogin|Statu)s|OpenLinkInNewWindow|ForceAuthn|AntiFrame)|roxyUseSoap)|l(?:dap(?:(?:G(?:roup(?:DecodeSearchedValu|Recursiv)|etUserBeforePasswordChang)|UsePasswordResetAttribut)e|(?:AllowResetExpired|Set)Password|ChangePasswordAsUser|PpolicyControl|ITDS)|oginHistoryEnabled)|c(?:o(?:ntextSwitching(?:Allowed2fModifications|StopWithLogout)|mpactConf|rsEnabled)|a(?:ptcha_(?:register|login|mail)_enabled|sSrvMetaDataOptions(?:Gateway|Renew))|heck(?:DevOps|State|User|XSS)|da)|no(?:tif(?:ication(?:Server(?:(?:POS|GE)T|DELETE)?|sExplorer)?|y(?:Deleted|Other))|AjaxHook)|i(?:ssuerDB(?:OpenID(?:Connect)?|SAML|CAS|Get)Activation|mpersonationSkipEmptyValues)|to(?:tp2f(?:UserCan(?:Chang|Remov)eKey|DisplayExistingSecret)|kenUseGlobalStorage)|u(?:se(?:RedirectOn(?:Forbidden|Error)|SafeJail)|2fUserCanRemoveKey|pgradeSession)|re(?:st(?:(?:Password|Session|Config|Auth)Server|ExportSecretKeys)|freshSessions)|br(?:uteForceProtection(?:IncrementalTempo)?|owsersDontStorePassword)|d(?:is(?:ablePersistentStorage|playSessionId)|biDynamicHashEnabled)|(?:mai(?:lOnPasswordChang|ntenanc)|vhostMaintenanc)e|g(?:roupsBeforeMacros|lobalLogoutTimer)|a(?:voidAssignment|ctiveTimer)|h(?:ideOldPassword|ttpOnly)|yubikey2fUserCanRemoveKey|krb(?:RemoveDomain|ByJs)|(?:wsdlServ|findUs)er)$/;
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)|f(?:RemovedUseNotif|OnlyUpgrade)|kip(?:Upgrade|Renew)Confirmation|oap(?:Session|Config)Server|t(?:ayConnecte|orePasswor)d|laveDisplayLogo|howLanguages|slByAjax)|o(?:idc(?:RPMetaDataOptions(?:A(?:llow(?:(?:ClientCredentials|Password)Grant|Offline)|ccessToken(?:Claims|JWT))|Re(?:freshToken|quirePKCE)|LogoutSessionRequired|IDTokenForceClaims|BypassConsent|Public)|ServiceAllow(?:(?:AuthorizationCode|Implicit|Hybrid)Flow|DynamicRegistration)|OPMetaDataOptions(?:(?:CheckJWTSignatur|UseNonc)e|StoreIDToken))|ldNotifFormat)|p(?:ortal(?:Display(?:Re(?:freshMyRights|setPassword|gister)|CertificateResetByMail|GeneratePassword|PasswordPolicy)|ErrorOn(?:ExpiredSession|MailNotFound)|(?:CheckLogin|Statu)s|OpenLinkInNewWindow|ForceAuthn|AntiFrame)|roxyUseSoap)|c(?:o(?:ntextSwitching(?:Allowed2fModifications|StopWithLogout)|mpactConf|rsEnabled)|a(?:ptcha_(?:register|login|mail)_enabled|sSrvMetaDataOptions(?:Gateway|Renew))|heck(?:DevOps(?:Download)?|State|User|XSS)|da)|l(?:dap(?:(?:G(?:roup(?:DecodeSearchedValu|Recursiv)|etUserBeforePasswordChang)|UsePasswordResetAttribut)e|(?:AllowResetExpired|Set)Password|ChangePasswordAsUser|PpolicyControl|ITDS)|oginHistoryEnabled)|no(?:tif(?:ication(?:Server(?:(?:POS|GE)T|DELETE)?|sExplorer)?|y(?:Deleted|Other))|AjaxHook)|i(?:ssuerDB(?:OpenID(?:Connect)?|SAML|CAS|Get)Activation|mpersonationSkipEmptyValues)|to(?:tp2f(?:UserCan(?:Chang|Remov)eKey|DisplayExistingSecret)|kenUseGlobalStorage)|u(?:se(?:RedirectOn(?:Forbidden|Error)|SafeJail)|2fUserCanRemoveKey|pgradeSession)|re(?:st(?:(?:Password|Session|Config|Auth)Server|ExportSecretKeys)|freshSessions)|br(?:uteForceProtection(?:IncrementalTempo)?|owsersDontStorePassword)|d(?:is(?:ablePersistentStorage|playSessionId)|biDynamicHashEnabled)|(?:mai(?:lOnPasswordChang|ntenanc)|vhostMaintenanc)e|g(?:roupsBeforeMacros|lobalLogoutTimer)|a(?:voidAssignment|ctiveTimer)|h(?:ideOldPassword|ttpOnly)|yubikey2fUserCanRemoveKey|krb(?:RemoveDomain|ByJs)|(?:wsdlServ|findUs)er)$/;
our @sessionTypes = ( 'remoteGlobal', 'global', 'localSession', 'persistent', 'saml', 'oidc', 'cas' );

View File

@ -1,7 +1,7 @@
# This file is generated by Lemonldap::NG::Manager::Build. Don't modify it by hand
package Lemonldap::NG::Common::Conf::DefaultValues;
our $VERSION = '2.0.11';
our $VERSION = '2.0.12';
sub defaultValues {
return {
@ -35,6 +35,7 @@ sub defaultValues {
'certificateResetByMailURL' =>
'http://auth.example.com/certificateReset',
'certificateResetByMailValidityDelay' => 0,
'checkDevOpsDownload' => 1,
'checkTime' => 600,
'checkUserDisplayComputedSession' => 1,
'checkUserDisplayEmptyHeaders' => 0,

View File

@ -5,7 +5,7 @@ use strict;
use Exporter 'import';
use base qw(Exporter);
our $VERSION = '2.0.11';
our $VERSION = '2.0.12';
our %EXPORT_TAGS = ( 'all' => [qw($simpleHashKeys $doubleHashKeys $specialNodeKeys $casAppMetaDataNodeKeys $casSrvMetaDataNodeKeys $oidcOPMetaDataNodeKeys $oidcRPMetaDataNodeKeys $samlIDPMetaDataNodeKeys $samlSPMetaDataNodeKeys $virtualHostKeys $specialNodeHash $authParameters $issuerParameters $samlServiceParameters $oidcServiceParameters $casServiceParameters)] );
our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );

View File

@ -4,7 +4,7 @@ package Lemonldap::NG::Handler::Lib::StatusConstants;
use strict;
use Exporter 'import';
our $VERSION = '2.0.11';
our $VERSION = '2.0.12';
sub portalConsts {
return {

View File

@ -1,7 +1,7 @@
# This file is generated by Lemonldap::NG::Manager::Build. Don't modify it by hand
package Lemonldap::NG::Manager::Attributes;
our $VERSION = '2.0.11';
our $VERSION = '2.0.12';
sub perlExpr {
my ( $val, $conf ) = @_;
@ -866,6 +866,10 @@ qr/(?:(?:https?):\/\/(?:(?:(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.]
'default' => 0,
'type' => 'bool'
},
'checkDevOpsDownload' => {
'default' => 1,
'type' => 'bool'
},
'checkState' => {
'default' => 0,
'type' => 'bool'

View File

@ -6,7 +6,7 @@
package Lemonldap::NG::Manager::Build::Attributes;
our $VERSION = '2.0.11';
our $VERSION = '2.0.12';
use strict;
use Regexp::Common qw/URI/;
@ -470,6 +470,12 @@ sub attributes {
documentation => 'Enable check DevOps',
flags => 'p',
},
checkDevOpsDownload => {
default => 1,
type => 'bool',
documentation => 'Enable check DevOps download field',
flags => 'p',
},
checkUser => {
default => 0,
type => 'bool',

View File

@ -803,7 +803,7 @@ sub tree {
title => 'devOpsCheck',
help => 'checkdevops.html',
form => 'simpleInputContainer',
nodes => ['checkDevOps'],
nodes => [ 'checkDevOps', 'checkDevOpsDownload' ],
},
{
title => 'impersonation',

View File

@ -198,6 +198,7 @@
"claimName":"Claim name",
"checkboxes":"Checkboxes",
"checkDevOps":"Activation",
"checkDevOpsDownload":"Download file",
"checkState":"تفعيل",
"checkStateSecret":"سر مشترك",
"checkUsers":"SSO profile check",

View File

@ -198,6 +198,7 @@
"claimName":"Claim name",
"checkboxes":"Checkboxes",
"checkDevOps":"Activation",
"checkDevOpsDownload":"Download file",
"checkState":"Activation",
"checkStateSecret":"Shared secret",
"checkUsers":"SSO profile check",

View File

@ -198,6 +198,7 @@
"claimName":"Claim name",
"checkboxes":"Checkboxes",
"checkDevOps":"Activation",
"checkDevOpsDownload":"Download file",
"checkState":"Activation",
"checkStateSecret":"Shared secret",
"checkUsers":"SSO profile check",

View File

@ -198,6 +198,7 @@
"claimName":"Claim name",
"checkboxes":"Checkboxes",
"checkDevOps":"Activation",
"checkDevOpsDownload":"Download file",
"checkState":"Activación",
"checkStateSecret":"Secreto compartido",
"checkUsers":"Comprobación de perfil SSO",

View File

@ -198,6 +198,7 @@
"claimName":"Nom de la revendication",
"checkboxes":"Cases à cocher",
"checkDevOps":"Activation",
"checkDevOpsDownload":"Télécharger un fichier",
"checkState":"Activation",
"checkStateSecret":"Secret partagé",
"checkUsers":"Vérification des profils SSO",

View File

@ -198,6 +198,7 @@
"claimName":"Claim name",
"checkboxes":"Checkboxes",
"checkDevOps":"Activation",
"checkDevOpsDownload":"Download file",
"checkState":"Attivazione",
"checkStateSecret":"Segreto condiviso",
"checkUsers":"Controllo del profilo SSO",

View File

@ -198,6 +198,7 @@
"claimName":"Nazwa roszczenia",
"checkboxes":"Pola wyboru",
"checkDevOps":"Activation",
"checkDevOpsDownload":"Download file",
"checkState":"Aktywacja",
"checkStateSecret":"Współdzielony sekret",
"checkUsers":"Sprawdź Profil SSO",

View File

@ -198,6 +198,7 @@
"claimName":"İstek adı",
"checkboxes":"Onay kutuları",
"checkDevOps":"Activation",
"checkDevOpsDownload":"Download file",
"checkState":"Aktivasyon",
"checkStateSecret":"Paylaşılan sır",
"checkUsers":"TOA profil Kontrolü",

View File

@ -198,6 +198,7 @@
"claimName":"Claim name",
"checkboxes":"Checkboxes",
"checkDevOps":"Activation",
"checkDevOpsDownload":"Download file",
"checkState":"Kích hoạt",
"checkStateSecret":"Chia sẻ bí mật",
"checkUsers":"SSO profile check",

View File

@ -198,6 +198,7 @@
"claimName":"Claim name",
"checkboxes":"Checkboxes",
"checkDevOps":"Activation",
"checkDevOpsDownload":"Download file",
"checkState":"激活",
"checkStateSecret":"Shared secret",
"checkUsers":"SSO profile check",

View File

@ -198,6 +198,7 @@
"claimName":"要求名稱",
"checkboxes":"勾選框",
"checkDevOps":"Activation",
"checkDevOpsDownload":"Download file",
"checkState":"啟用",
"checkStateSecret":"已分享的祕密",
"checkUsers":"SSO 設定檔檢查",

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -4,7 +4,7 @@ package Lemonldap::NG::Portal::Main::Constants;
use strict;
use Exporter 'import';
our $VERSION = '2.0.11';
our $VERSION = '2.0.12';
use constant HANDLER => 'Lemonldap::NG::Handler::PSGI::Main';
use constant {

View File

@ -3,9 +3,11 @@ package Lemonldap::NG::Portal::Plugins::CheckDevOps;
use strict;
use Mouse;
use JSON qw(from_json);
use Lemonldap::NG::Common::UserAgent;
use Lemonldap::NG::Portal::Main::Constants qw(
PE_OK
PE_ERROR
PE_BADURL
PE_NOTOKEN
PE_TOKENEXPIRED
PE_BAD_DEVOPS_FILE
@ -31,6 +33,16 @@ has ott => (
}
);
has ua => (
is => 'rw',
lazy => 1,
builder => sub {
my $ua = Lemonldap::NG::Common::UserAgent->new( $_[0]->{conf} );
$ua->env_proxy();
return $ua;
}
);
sub init {
my ($self) = @_;
$self->addAuthRoute( checkdevops => 'run', ['POST'] )
@ -50,6 +62,7 @@ sub display {
MAIN_LOGO => $self->conf->{portalMainLogo},
SKIN => $self->p->getSkin($req),
LANGS => $self->conf->{showLanguages},
DOWNLOAD => $self->conf->{checkDevOpsDownload},
MSG => 'checkDevOps',
ALERTE => 'alert-info',
FILE => '',
@ -68,7 +81,7 @@ sub display {
sub run {
my ( $self, $req ) = @_;
my ( $headers, $rules ) = ( [], [] );
my ( $msg, $alert );
my ( $msg, $json, $alert, $url );
# Check token
if ( $self->ottRule->( $req, {} ) ) {
@ -94,6 +107,7 @@ sub run {
MAIN_LOGO => $self->conf->{portalMainLogo},
SKIN => $self->p->getSkin($req),
LANGS => $self->conf->{showLanguages},
DOWNLOAD => $self->conf->{checkDevOpsDownload},
MSG => "PE$msg",
ALERTE => 'alert-warning',
FILE => '',
@ -107,16 +121,60 @@ sub run {
if $msg;
}
my $json = eval { from_json( $req->param('checkDevOpsFile') ) };
if ($@) {
# Check URL if allowed and exists
if ( $self->conf->{checkDevOpsDownload} and $url = $req->param('url') ) {
undef $url if $self->p->checkXSSAttack( 'CheckDevOps URL', $url );
if ( $url && $url =~ m#^(?:https?://)?([^/]*)(.*)#i ) {
# Prepare form params
$msg = 'PE' . PE_BAD_DEVOPS_FILE;
$alert = 'alert-danger';
$json = '';
$self->userLogger->error("CheckDevOps: bad 'rules.json' file ($@)");
# Reformat url
my ( $vhost, $appuri ) = $url =~ m#^(?:https?://)?([^/]*)(.*)#i;
my ($proto) = $url =~ m#^(https?://).*#i;
$proto ||= 'http://';
$url = "$proto$vhost/rules.json";
my $response =
$self->ua->get( $url, 'Accept' => 'application/json' );
$self->logger->debug( "Message/Code from $url: "
. $response->{_msg} . '/'
. $response->{_rc} );
$self->logger->debug(
"Content from $url/rules.json: " . $response->{_content} );
$json = eval { from_json( $response->{_content} ) };
if ($@) {
# Prepare form params
$msg = 'PE' . PE_BAD_DEVOPS_FILE;
$alert = 'alert-danger';
$json = '';
$self->userLogger->error(
"CheckDevOps: bad 'rules.json' file retrieved from $url/rules.json ($@)"
);
}
}
else {
# Prepare form params
$msg = 'PE' . PE_BADURL;
$alert = 'alert-danger';
$json = '';
$self->userLogger->error('CheckDevOps: bad URL provided');
}
}
else {
unless ( $json || $msg ) {
$json = eval { from_json( $req->param('checkDevOpsFile') ) };
if ($@) {
# Prepare form params
$msg = 'PE' . PE_BAD_DEVOPS_FILE;
$alert = 'alert-danger';
$json = '';
$self->userLogger->error(
"CheckDevOps: bad 'rules.json' file provided ($@)");
}
}
# Parse JSON
if ($json) {
my $handler = $self->p->HANDLER;
my $vhost = $handler->resolveAlias($req);
@ -164,11 +222,13 @@ sub run {
MAIN_LOGO => $self->conf->{portalMainLogo},
SKIN => $self->p->getSkin($req),
LANGS => $self->conf->{showLanguages},
DOWNLOAD => $self->conf->{checkDevOpsDownload},
MSG => $msg,
ALERTE => $alert,
FILE => $json,
HEADERS => $headers,
RULES => $rules,
URL => $url,
TOKEN => (
$self->ottRule->( $req, {} )
? $self->ott->createToken()

View File

@ -29,10 +29,16 @@
</div>
</TMPL_IF>
<form id="checkDevOps" action="/checkdevops" method="post" class="password" role="form">
<pre><textarea id="checkDevOpsFile" name="checkDevOpsFile" class="form-control rounded-1" rows="6" trplaceholder="pasteHere" required><TMPL_VAR NAME="FILE"></textarea></pre>
<TMPL_IF NAME="TOKEN">
<input type="hidden" name="token" value="<TMPL_VAR NAME="TOKEN">" />
</TMPL_IF>
<TMPL_IF NAME="DOWNLOAD">
<input id="urlfield" name="url" type="text" class="form-control" value="<TMPL_VAR NAME="URL">" trplaceholder="URL / DNS" aria-required="true"/>
<pre><textarea id="checkDevOpsFile" name="checkDevOpsFile" class="form-control rounded-1" rows="6" trplaceholder="pasteHere"><TMPL_VAR NAME="FILE"></textarea></pre>
<TMPL_ELSE>
<pre><textarea id="checkDevOpsFile" name="checkDevOpsFile" class="form-control rounded-1" rows="6" trplaceholder="pasteHere" required><TMPL_VAR NAME="FILE"></textarea></pre>
</TMPL_IF>
<div class="buttons">
<button type="submit" class="btn btn-success">
<span class="fa fa-check-circle"></span>