Append an option in Manager to define RULES_URL param (#2627)

This commit is contained in:
Christophe Maudoux 2021-10-01 22:59:30 +02:00
parent ed56a171b9
commit 732c8631eb
29 changed files with 276 additions and 22 deletions

View File

@ -515,6 +515,8 @@ Some options are available:
required level, he is redirected to an upgrade page in the portal. required level, he is redirected to an upgrade page in the portal.
This default level is required for ALL locations relative to this virtual host. This default level is required for ALL locations relative to this virtual host.
It can be overrided for each locations. It can be overrided for each locations.
- **DevOps rules file URL**: option to define URL to retreive DevOps rules file.
This option can be overriden with ``uwsgi_param/fastcgi_param RULES_URL`` parameter.
- **ServiceToken timeout**: by default, ServiceToken is just valid during 30 - **ServiceToken timeout**: by default, ServiceToken is just valid during 30
seconds. This TTL can be customized for each virtual host. seconds. This TTL can be customized for each virtual host.

View File

@ -30,7 +30,7 @@ our $oidcOPMetaDataNodeKeys = 'oidcOPMetaData(?:Options(?:C(?:lient(?:Secret|ID)
our $oidcRPMetaDataNodeKeys = 'oidcRPMetaData(?:Options(?:A(?:llow(?:(?:ClientCredentials|Password)Grant|Offline)|ccessToken(?:Expiration|SignAlg|Claims|JWT)|uth(?:orizationCodeExpiration|nLevel)|dditionalAudiences)|I(?:DToken(?:ForceClaims|Expiration|SignAlg)|con)|R(?:e(?:directUris|freshToken|quirePKCE)|ule)|Logout(?:SessionRequired|Type|Url)|P(?:ostLogoutRedirectUris|ublic)|UserI(?:nfoSignAlg|DAttr)|OfflineSessionExpiration|Client(?:Secret|ID)|BypassConsent|DisplayName|ExtraClaims)|(?:ExportedVar|ScopeRule|Macro)s)'; our $oidcRPMetaDataNodeKeys = 'oidcRPMetaData(?:Options(?:A(?:llow(?:(?:ClientCredentials|Password)Grant|Offline)|ccessToken(?:Expiration|SignAlg|Claims|JWT)|uth(?:orizationCodeExpiration|nLevel)|dditionalAudiences)|I(?:DToken(?:ForceClaims|Expiration|SignAlg)|con)|R(?:e(?:directUris|freshToken|quirePKCE)|ule)|Logout(?:SessionRequired|Type|Url)|P(?:ostLogoutRedirectUris|ublic)|UserI(?:nfoSignAlg|DAttr)|OfflineSessionExpiration|Client(?:Secret|ID)|BypassConsent|DisplayName|ExtraClaims)|(?:ExportedVar|ScopeRule|Macro)s)';
our $samlIDPMetaDataNodeKeys = 'samlIDPMetaData(?:Options(?:(?:Check(?:S[LS]OMessageSignatur|Audienc|Tim)|EncryptionMod|UserAttribut|DisplayNam)e|S(?:ign(?:S[LS]OMessage|atureMethod)|toreSAMLToken|[LS]OBinding|ortNumber)|A(?:llow(?:LoginFromIDP|ProxiedAuthn)|daptSessionUtime)|Re(?:questedAuthnContext|solutionRule|layStateURL)|Force(?:Authn|UTF8)|I(?:sPassive|con)|NameIDFormat)|ExportedAttributes|XML)'; our $samlIDPMetaDataNodeKeys = 'samlIDPMetaData(?:Options(?:(?:Check(?:S[LS]OMessageSignatur|Audienc|Tim)|EncryptionMod|UserAttribut|DisplayNam)e|S(?:ign(?:S[LS]OMessage|atureMethod)|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(?:S(?:ign(?:S[LS]OMessage|atureMethod)|essionNotOnOrAfterTimeout)|N(?:ameID(?:SessionKey|Format)|otOnOrAfterTimeout)|(?:CheckS[LS]OMessageSignatur|OneTimeUs|Rul)e|En(?:ableIDPInitiatedURL|cryptionMode)|AuthnLevel|ForceUTF8)|(?:ExportedAttribute|Macro)s|XML)'; our $samlSPMetaDataNodeKeys = 'samlSPMetaData(?:Options(?:S(?:ign(?:S[LS]OMessage|atureMethod)|essionNotOnOrAfterTimeout)|N(?:ameID(?:SessionKey|Format)|otOnOrAfterTimeout)|(?:CheckS[LS]OMessageSignatur|OneTimeUs|Rul)e|En(?:ableIDPInitiatedURL|cryptionMode)|AuthnLevel|ForceUTF8)|(?:ExportedAttribute|Macro)s|XML)';
our $virtualHostKeys = '(?:vhost(?:A(?:ccessToTrace|uthnLevel|liases)|(?:Maintenanc|Typ)e|ServiceTokenTTL|Https|Port)|(?:exportedHeader|locationRule)s|post)'; our $virtualHostKeys = '(?:vhost(?:A(?:ccessToTrace|uthnLevel|liases)|(?:Maintenanc|Typ)e|ServiceTokenTTL|DevOpsRulesUrl|Https|Port)|(?:exportedHeader|locationRule)s|post)';
our $authParameters = { our $authParameters = {
adParams => [qw(ADPwdMaxAge ADPwdExpireWarning)], adParams => [qw(ADPwdMaxAge ADPwdExpireWarning)],

View File

@ -74,12 +74,14 @@ t/60-Lemonldap-NG-Handler-PSGI.t
t/61-Lemonldap-NG-Handler-PSGI-Server.t t/61-Lemonldap-NG-Handler-PSGI-Server.t
t/62-Lemonldap-NG-Handler-Nginx.t t/62-Lemonldap-NG-Handler-Nginx.t
t/63-Lemonldap-NG-Handler-PSGI-Try.t t/63-Lemonldap-NG-Handler-PSGI-Try.t
t/64-Lemonldap-NG-Handler-PSGI-DevOps-vhostOptions.t
t/64-Lemonldap-NG-Handler-PSGI-DevOps-with-param.t
t/64-Lemonldap-NG-Handler-PSGI-DevOps.t t/64-Lemonldap-NG-Handler-PSGI-DevOps.t
t/65-Lemonldap-NG-Handler-Nginx-ServiceToken.t t/65-Lemonldap-NG-Handler-Nginx-ServiceToken.t
t/65-Lemonldap-NG-Handler-PSGI-ServiceToken.t t/65-Lemonldap-NG-Handler-PSGI-ServiceToken.t
t/66-Lemonldap-NG-Handler-PSGI-wildcard.t t/66-Lemonldap-NG-Handler-PSGI-wildcard.t
t/67-Lemonldap-NG-Handler-PSGI-vhostoptions-with-reload.t t/67-Lemonldap-NG-Handler-PSGI-vhostOptions-with-reload.t
t/67-Lemonldap-NG-Handler-PSGI-vhostoptions.t t/67-Lemonldap-NG-Handler-PSGI-vhostOptions.t
t/68-Lemonldap-NG-Handler-PSGI-Zimbra.t t/68-Lemonldap-NG-Handler-PSGI-Zimbra.t
t/69-Lemonldap-NG-Handler-PSGI-SecureToken.t t/69-Lemonldap-NG-Handler-PSGI-SecureToken.t
t/70-Lemonldap-NG-Handler-PSGI-AuthBasic.t t/70-Lemonldap-NG-Handler-PSGI-AuthBasic.t

View File

@ -31,9 +31,11 @@ sub _loadVhostConfig {
my ( $class, $req, $vhost ) = @_; my ( $class, $req, $vhost ) = @_;
my ( $json, $rUrl, $rVhost ); my ( $json, $rUrl, $rVhost );
if ( $class->tsv->{useSafeJail} ) { if ( $class->tsv->{useSafeJail} ) {
if ( $req->env->{RULES_URL} ) { if ( $req->env->{RULES_URL} || $class->tsv->{devOpsRulesUrl}->{$vhost} )
$rUrl = $req->{env}->{RULES_URL}; {
$rVhost = ( $req->env->{RULES_URL} =~ m#^https?://([^/]*).*# )[0]; $rUrl = $req->{env}->{RULES_URL}
|| $class->tsv->{devOpsRulesUrl}->{$vhost};
$rVhost = ( $rUrl =~ m#^https?://([^/]*).*# )[0];
$rVhost =~ s/:\d+$//; $rVhost =~ s/:\d+$//;
} }
else { else {
@ -44,22 +46,25 @@ sub _loadVhostConfig {
$rVhost = $vhost; $rVhost = $vhost;
} }
$class->logger->debug("Try to retrieve 'rules.json' from $rUrl"); $class->logger->debug("Try to retrieve rules file from $rUrl");
my $get = HTTP::Request->new( GET => $rUrl ); my $get = HTTP::Request->new( GET => $rUrl );
$class->logger->debug("Set Host header with $rVhost"); $class->logger->debug("Set Host header with $rVhost");
$get->header( Host => $rVhost ); $get->header( Host => $rVhost );
my $resp = $class->ua->request($get); my $resp = $class->ua->request($get);
if ( $resp->is_success ) { if ( $resp->is_success ) {
$class->logger->debug('Response is success');
eval { eval {
$json = from_json( $resp->content, { allow_nonref => 1 } ); }; $json = from_json( $resp->content, { allow_nonref => 1 } ); };
if ($@) { if ($@) {
$class->logger->error( $class->logger->debug('Bad json file received');
"Bad 'rules.json' retrieved from $rVhost for $vhost, skipping ($@)" $class->logger->error(
"Bad rules file retrieved from $rUrl for $vhost, skipping ($@)"
); );
} }
else { else {
$class->logger->debug('Good json file received');
$class->logger->info( $class->logger->info(
"Compiling 'rules.json' retrieved from $rVhost for $vhost"); "Compiling rules retrieved from $rUrl for $vhost");
} }
} }
} }

View File

@ -248,6 +248,8 @@ sub defaultValuesInit {
$conf->{vhostOptions}->{$vhost}->{vhostServiceTokenTTL}; $conf->{vhostOptions}->{$vhost}->{vhostServiceTokenTTL};
$class->tsv->{accessToTrace}->{$vhost} = $class->tsv->{accessToTrace}->{$vhost} =
$conf->{vhostOptions}->{$vhost}->{vhostAccessToTrace}; $conf->{vhostOptions}->{$vhost}->{vhostAccessToTrace};
$class->tsv->{devOpsRulesUrl}->{$vhost} =
$conf->{vhostOptions}->{$vhost}->{vhostDevOpsRulesUrl};
} }
} }
return 1; return 1;

View File

@ -0,0 +1,112 @@
use Test::More;
use JSON;
use MIME::Base64;
use LWP::UserAgent;
use Data::Dumper;
BEGIN {
require 't/test-psgi-lib.pm';
}
init(
'Lemonldap::NG::Handler::Server',
{
vhostOptions => {
'test3.example.com' => {
vhostDevOpsRulesUrl => 'http://devops.example.com/myfile.json',
},
},
}
);
my $res;
# Authorized queries
ok(
$res = $client->_get(
'/', undef,
'test3.example.com', "lemonldap=$sessionId",
VHOSTTYPE => 'DevOps',
),
'Authorized query'
);
ok( $res->[0] == 200, 'Code is 200' ) or explain( $res->[0], 200 );
my %headers = @{ $res->[1] };
ok( $headers{User} eq 'dwho', "'User' => 'dwho'" )
or explain( \%headers, 'dwho' );
ok( $headers{Name} eq '', "'Name' => ''" ) or explain( \%headers, 'No Name' );
ok( $headers{Mail} eq '', "'Mail' => ''" ) or explain( \%headers, 'No Mail' );
ok( keys %headers == 7, "Seven headers sent" )
or explain( \%headers, 'Seven headers' );
count(6);
ok(
$res = $client->_get(
'/testyes', undef,
'test3.example.com', "lemonldap=$sessionId",
VHOSTTYPE => 'DevOps',
),
'Authorized query'
);
ok( $res->[0] == 200, 'Code is 200' ) or explain( $res->[0], 200 );
count(2);
# Denied queries
ok(
$res = $client->_get(
'/deny', undef,
'test3.example.com', "lemonldap=$sessionId",
VHOSTTYPE => 'DevOps',
),
'Denied query'
);
ok( $res->[0] == 403, 'Code is 403' ) or explain( $res->[0], 403 );
count(2);
ok(
$res = $client->_get(
'/testno', undef,
'test3.example.com', "lemonldap=$sessionId",
VHOSTTYPE => 'DevOps',
),
'Denied query'
);
ok( $res->[0] == 403, 'Code is 403' ) or explain( $res->[0], 403 );
count(2);
done_testing( count() );
clean();
# Redefine LWP methods for tests
no warnings 'redefine';
sub LWP::UserAgent::request {
my ( $self, $req ) = @_;
ok( $req->header('host') eq 'devops.example.com',
'Host header found' )
or explain( $req->headers(), 'devops.example.com' );
ok( $req->as_string() =~ m#http://devops.example.com/myfile.json#,
'Rules file URL found' )
or explain( $req->as_string(), 'GET http://devops.example.com/myfile.json' );
count(2);
my $httpResp;
my $s = '{
"rules": {
"^/deny": "deny",
"^/testno": "$uid ne qq{dwho}",
"^/testyes": "$uid eq qq{dwho}",
"default": "accept"
},
"headers": {
"User": "$uid",
"Mail": "$mail",
"Name": "$cn"
}
}';
$httpResp = HTTP::Response->new( 200, 'OK' );
$httpResp->header( 'Content-Type', 'application/json' );
$httpResp->header( 'Content-Length', length($s) );
$httpResp->content($s);
return $httpResp;
}

View File

@ -0,0 +1,107 @@
use Test::More;
use JSON;
use MIME::Base64;
use LWP::UserAgent;
use Data::Dumper;
BEGIN {
require 't/test-psgi-lib.pm';
}
init('Lemonldap::NG::Handler::Server');
my $res;
# Authorized queries
ok(
$res = $client->_get(
'/', undef,
'test3.example.com', "lemonldap=$sessionId",
VHOSTTYPE => 'DevOps',
RULES_URL => 'http://devops.example.com/file.json'
),
'Authorized query'
);
ok( $res->[0] == 200, 'Code is 200' ) or explain( $res->[0], 200 );
my %headers = @{ $res->[1] };
ok( $headers{User} eq 'dwho', "'User' => 'dwho'" )
or explain( \%headers, 'dwho' );
ok( $headers{Name} eq '', "'Name' => ''" ) or explain( \%headers, 'No Name' );
ok( $headers{Mail} eq '', "'Mail' => ''" ) or explain( \%headers, 'No Mail' );
ok( keys %headers == 7, "Seven headers sent" )
or explain( \%headers, 'Seven headers' );
count(6);
ok(
$res = $client->_get(
'/testyes', undef,
'test3.example.com', "lemonldap=$sessionId",
VHOSTTYPE => 'DevOps',
RULES_URL => 'http://devops.example.com/file.json'
),
'Authorized query'
);
ok( $res->[0] == 200, 'Code is 200' ) or explain( $res->[0], 200 );
count(2);
# Denied queries
ok(
$res = $client->_get(
'/deny', undef,
'test3.example.com', "lemonldap=$sessionId",
VHOSTTYPE => 'DevOps',
RULES_URL => 'http://devops.example.com/file.json'
),
'Denied query'
);
ok( $res->[0] == 403, 'Code is 403' ) or explain( $res->[0], 403 );
count(2);
ok(
$res = $client->_get(
'/testno', undef,
'test3.example.com', "lemonldap=$sessionId",
VHOSTTYPE => 'DevOps',
RULES_URL => 'http://devops.example.com/file.json'
),
'Denied query'
);
ok( $res->[0] == 403, 'Code is 403' ) or explain( $res->[0], 403 );
count(2);
done_testing( count() );
clean();
# Redefine LWP methods for tests
no warnings 'redefine';
sub LWP::UserAgent::request {
my ( $self, $req ) = @_;
ok( $req->header('host') eq 'devops.example.com',
'Host header found' )
or explain( $req->headers(), 'devops.example.com' );
ok( $req->as_string() =~ m#http://devops.example.com/file.json#,
'Rules file URL found' )
or explain( $req->as_string(), 'GET http://devops.example.com/file.json' );
count(2);
my $httpResp;
my $s = '{
"rules": {
"^/deny": "deny",
"^/testno": "$uid ne qq{dwho}",
"^/testyes": "$uid eq qq{dwho}",
"default": "accept"
},
"headers": {
"User": "$uid",
"Mail": "$mail",
"Name": "$cn"
}
}';
$httpResp = HTTP::Response->new( 200, 'OK' );
$httpResp->header( 'Content-Type', 'application/json' );
$httpResp->header( 'Content-Length', length($s) );
$httpResp->content($s);
return $httpResp;
}

View File

@ -18,7 +18,6 @@ ok(
'/', undef, '/', undef,
'test3.example.com', "lemonldap=$sessionId", 'test3.example.com', "lemonldap=$sessionId",
VHOSTTYPE => 'DevOps', VHOSTTYPE => 'DevOps',
RULES_URL => 'http://devops.example.com'
), ),
'Authorized query' 'Authorized query'
); );
@ -37,7 +36,6 @@ ok(
'/testyes', undef, '/testyes', undef,
'test3.example.com', "lemonldap=$sessionId", 'test3.example.com', "lemonldap=$sessionId",
VHOSTTYPE => 'DevOps', VHOSTTYPE => 'DevOps',
RULES_URL => 'http://devops.example.com'
), ),
'Authorized query' 'Authorized query'
); );
@ -50,7 +48,6 @@ ok(
'/deny', undef, '/deny', undef,
'test3.example.com', "lemonldap=$sessionId", 'test3.example.com', "lemonldap=$sessionId",
VHOSTTYPE => 'DevOps', VHOSTTYPE => 'DevOps',
RULES_URL => 'http://devops.example.com'
), ),
'Denied query' 'Denied query'
); );
@ -62,7 +59,6 @@ ok(
'/testno', undef, '/testno', undef,
'test3.example.com', "lemonldap=$sessionId", 'test3.example.com', "lemonldap=$sessionId",
VHOSTTYPE => 'DevOps', VHOSTTYPE => 'DevOps',
RULES_URL => 'http://devops.example.com'
), ),
'Denied query' 'Denied query'
); );
@ -78,10 +74,13 @@ no warnings 'redefine';
sub LWP::UserAgent::request { sub LWP::UserAgent::request {
my ( $self, $req ) = @_; my ( $self, $req ) = @_;
ok( $req->header('host') eq 'devops.example.com', ok( $req->header('host') eq 'test3.example.com',
'Host header found' ) 'Host header found' )
or explain( $req->headers(), 'Header' ); or explain( $req->headers(), 'test3.example.com' );
count(1); ok( $req->as_string() =~ m#http://127.0.0.1:80/rules.json#,
'Rules file URL found' )
or explain( $req->as_string(), 'GET http://127.0.0.1:80/rules.json' );
count(2);
my $httpResp; my $httpResp;
my $s = '{ my $s = '{
"rules": { "rules": {

View File

@ -211,6 +211,7 @@ site/htdocs/static/logos/it.png
site/htdocs/static/logos/llng-icon-32.png site/htdocs/static/logos/llng-icon-32.png
site/htdocs/static/logos/llng-logo-32.png site/htdocs/static/logos/llng-logo-32.png
site/htdocs/static/logos/pl.png site/htdocs/static/logos/pl.png
site/htdocs/static/logos/pt_BR.png
site/htdocs/static/logos/tr.png site/htdocs/static/logos/tr.png
site/htdocs/static/logos/vi.png site/htdocs/static/logos/vi.png
site/htdocs/static/logos/zh.png site/htdocs/static/logos/zh.png

View File

@ -4375,6 +4375,9 @@ qr/^(?:(?:(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.])*(?:[a-zA-Z][-a-
'vhostAuthnLevel' => { 'vhostAuthnLevel' => {
'type' => 'int' 'type' => 'int'
}, },
'vhostDevOpsRulesUrl' => {
'type' => 'url'
},
'vhostHttps' => { 'vhostHttps' => {
'default' => -1, 'default' => -1,
'type' => 'trool' 'type' => 'trool'

View File

@ -2374,7 +2374,8 @@ sub attributes {
default => 'Main', default => 'Main',
documentation => 'Handler type', documentation => 'Handler type',
}, },
vhostAuthnLevel => { type => 'int' }, vhostAuthnLevel => { type => 'int' },
vhostDevOpsRulesUrl => { type => 'url' },
# SecureToken parameters # SecureToken parameters
secureTokenAllowOnError => { secureTokenAllowOnError => {

View File

@ -14,7 +14,7 @@
package Lemonldap::NG::Manager::Build::CTrees; package Lemonldap::NG::Manager::Build::CTrees;
our $VERSION = '2.0.12'; our $VERSION = '2.0.14';
sub cTrees { sub cTrees {
return { return {
@ -30,7 +30,8 @@ sub cTrees {
'vhostPort', 'vhostHttps', 'vhostPort', 'vhostHttps',
'vhostMaintenance', 'vhostAliases', 'vhostMaintenance', 'vhostAliases',
'vhostAccessToTrace', 'vhostType', 'vhostAccessToTrace', 'vhostType',
'vhostAuthnLevel', 'vhostServiceTokenTTL' 'vhostAuthnLevel', 'vhostDevOpsRulesUrl',
'vhostServiceTokenTTL'
], ],
}, },
], ],

View File

@ -1475,6 +1475,11 @@ function templates(tpl,key) {
"title" : "vhostAuthnLevel", "title" : "vhostAuthnLevel",
"type" : "int" "type" : "int"
}, },
{
"get" : tpl+"s/"+key+"/"+"vhostDevOpsRulesUrl",
"id" : tpl+"s/"+key+"/"+"vhostDevOpsRulesUrl",
"title" : "vhostDevOpsRulesUrl"
},
{ {
"default" : -1, "default" : -1,
"get" : tpl+"s/"+key+"/"+"vhostServiceTokenTTL", "get" : tpl+"s/"+key+"/"+"vhostServiceTokenTTL",

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1186,6 +1186,7 @@
"vhostAccessToTrace":"Access to trace", "vhostAccessToTrace":"Access to trace",
"vhostAliases":"اسماء مستعارة", "vhostAliases":"اسماء مستعارة",
"vhostAuthnLevel":"مستوى إثبات الهوية واجب", "vhostAuthnLevel":"مستوى إثبات الهوية واجب",
"vhostDevOpsRulesUrl": "DevOps rules file URL",
"vhostHttps":"إتش تي تي بي س", "vhostHttps":"إتش تي تي بي س",
"vhostMaintenance":"وضع الصيانة", "vhostMaintenance":"وضع الصيانة",
"vhostOptions":"الخيارات", "vhostOptions":"الخيارات",

View File

@ -1186,6 +1186,7 @@
"vhostAccessToTrace":"Access to trace", "vhostAccessToTrace":"Access to trace",
"vhostAliases":"Aliases", "vhostAliases":"Aliases",
"vhostAuthnLevel":"Required authentication level", "vhostAuthnLevel":"Required authentication level",
"vhostDevOpsRulesUrl": "DevOps rules file URL",
"vhostHttps":"HTTPS", "vhostHttps":"HTTPS",
"vhostMaintenance":"Maintenance mode", "vhostMaintenance":"Maintenance mode",
"vhostOptions":"Options", "vhostOptions":"Options",

View File

@ -1186,6 +1186,7 @@
"vhostAccessToTrace":"Access to trace", "vhostAccessToTrace":"Access to trace",
"vhostAliases":"Aliases", "vhostAliases":"Aliases",
"vhostAuthnLevel":"Required authentication level", "vhostAuthnLevel":"Required authentication level",
"vhostDevOpsRulesUrl": "DevOps rules file URL",
"vhostHttps":"HTTPS", "vhostHttps":"HTTPS",
"vhostMaintenance":"Maintenance mode", "vhostMaintenance":"Maintenance mode",
"vhostOptions":"Options", "vhostOptions":"Options",

View File

@ -1186,6 +1186,7 @@
"vhostAccessToTrace":"Access to trace", "vhostAccessToTrace":"Access to trace",
"vhostAliases":"Aliases", "vhostAliases":"Aliases",
"vhostAuthnLevel":"Nivel de autentificación requerido", "vhostAuthnLevel":"Nivel de autentificación requerido",
"vhostDevOpsRulesUrl": "DevOps rules file URL",
"vhostHttps":"HTTPS", "vhostHttps":"HTTPS",
"vhostMaintenance":"Maintenance mode", "vhostMaintenance":"Maintenance mode",
"vhostOptions":"Options", "vhostOptions":"Options",

View File

@ -1186,6 +1186,7 @@
"vhostAccessToTrace":"Accès à tracer", "vhostAccessToTrace":"Accès à tracer",
"vhostAliases":"Alias", "vhostAliases":"Alias",
"vhostAuthnLevel":"Niveau d'authentification requis", "vhostAuthnLevel":"Niveau d'authentification requis",
"vhostDevOpsRulesUrl": "URL du fichier de règles DevOps",
"vhostHttps":"HTTPS", "vhostHttps":"HTTPS",
"vhostMaintenance":"Mode maintenance", "vhostMaintenance":"Mode maintenance",
"vhostOptions":"Options", "vhostOptions":"Options",

View File

@ -1186,6 +1186,7 @@
"vhostAccessToTrace":"Access to trace", "vhostAccessToTrace":"Access to trace",
"vhostAliases":"Alias", "vhostAliases":"Alias",
"vhostAuthnLevel":"Livello di autenticazione richiesto", "vhostAuthnLevel":"Livello di autenticazione richiesto",
"vhostDevOpsRulesUrl": "DevOps rules file URL",
"vhostHttps":"HTTPS", "vhostHttps":"HTTPS",
"vhostMaintenance":"Modalità di manutenzione", "vhostMaintenance":"Modalità di manutenzione",
"vhostOptions":"Opzioni", "vhostOptions":"Opzioni",

View File

@ -1186,6 +1186,7 @@
"vhostAccessToTrace":"Dostęp do śledzenia", "vhostAccessToTrace":"Dostęp do śledzenia",
"vhostAliases":"Aliasy", "vhostAliases":"Aliasy",
"vhostAuthnLevel":"Wymagany poziom uwierzytelnienia", "vhostAuthnLevel":"Wymagany poziom uwierzytelnienia",
"vhostDevOpsRulesUrl": "DevOps rules file URL",
"vhostHttps":"HTTPS", "vhostHttps":"HTTPS",
"vhostMaintenance":"Tryb konserwacji", "vhostMaintenance":"Tryb konserwacji",
"vhostOptions":"Opcje", "vhostOptions":"Opcje",

View File

@ -1186,6 +1186,7 @@
"vhostAccessToTrace":"İzlemeye erişim", "vhostAccessToTrace":"İzlemeye erişim",
"vhostAliases":"Takma adlar", "vhostAliases":"Takma adlar",
"vhostAuthnLevel":"Gereken doğrulama seviyesi", "vhostAuthnLevel":"Gereken doğrulama seviyesi",
"vhostDevOpsRulesUrl": "DevOps rules file URL",
"vhostHttps":"HTTPS", "vhostHttps":"HTTPS",
"vhostMaintenance":"Bakım modu", "vhostMaintenance":"Bakım modu",
"vhostOptions":"Seçenekler", "vhostOptions":"Seçenekler",

View File

@ -1186,6 +1186,7 @@
"vhostAccessToTrace":"Access to trace", "vhostAccessToTrace":"Access to trace",
"vhostAliases":"Bí danh", "vhostAliases":"Bí danh",
"vhostAuthnLevel":"Mức xác thực bắt buộc", "vhostAuthnLevel":"Mức xác thực bắt buộc",
"vhostDevOpsRulesUrl": "DevOps rules file URL",
"vhostHttps":"HTTPS", "vhostHttps":"HTTPS",
"vhostMaintenance":"Chế độ bảo trì", "vhostMaintenance":"Chế độ bảo trì",
"vhostOptions":"Tùy chọn", "vhostOptions":"Tùy chọn",

View File

@ -1186,6 +1186,7 @@
"vhostAccessToTrace":"Access to trace", "vhostAccessToTrace":"Access to trace",
"vhostAliases":"Aliases", "vhostAliases":"Aliases",
"vhostAuthnLevel":"Required authentication level", "vhostAuthnLevel":"Required authentication level",
"vhostDevOpsRulesUrl": "DevOps rules file URL",
"vhostHttps":"HTTPS", "vhostHttps":"HTTPS",
"vhostMaintenance":"Maintenance mode", "vhostMaintenance":"Maintenance mode",
"vhostOptions":"Options", "vhostOptions":"Options",

View File

@ -1186,6 +1186,7 @@
"vhostAccessToTrace":"存取追蹤", "vhostAccessToTrace":"存取追蹤",
"vhostAliases":"別名", "vhostAliases":"別名",
"vhostAuthnLevel":"需要的驗證等級", "vhostAuthnLevel":"需要的驗證等級",
"vhostDevOpsRulesUrl": "DevOps rules file URL",
"vhostHttps":"HTTPS", "vhostHttps":"HTTPS",
"vhostMaintenance":"維護模式", "vhostMaintenance":"維護模式",
"vhostOptions":"選項", "vhostOptions":"選項",

View File

@ -367,6 +367,7 @@ site/htdocs/static/common/modules/WebID.png
site/htdocs/static/common/nl.png site/htdocs/static/common/nl.png
site/htdocs/static/common/pl.png site/htdocs/static/common/pl.png
site/htdocs/static/common/pt.png site/htdocs/static/common/pt.png
site/htdocs/static/common/pt_BR.png
site/htdocs/static/common/ro.png site/htdocs/static/common/ro.png
site/htdocs/static/common/tr.png site/htdocs/static/common/tr.png
site/htdocs/static/common/vi.png site/htdocs/static/common/vi.png
@ -382,6 +383,7 @@ site/htdocs/static/languages/it.json
site/htdocs/static/languages/nl.json site/htdocs/static/languages/nl.json
site/htdocs/static/languages/pl.json site/htdocs/static/languages/pl.json
site/htdocs/static/languages/pt.json site/htdocs/static/languages/pt.json
site/htdocs/static/languages/pt_BR.json
site/htdocs/static/languages/ro.json site/htdocs/static/languages/ro.json
site/htdocs/static/languages/tr.json site/htdocs/static/languages/tr.json
site/htdocs/static/languages/vi.json site/htdocs/static/languages/vi.json
@ -461,6 +463,7 @@ site/templates/common/mail/fi.json
site/templates/common/mail/fr.json site/templates/common/mail/fr.json
site/templates/common/mail/it.json site/templates/common/mail/it.json
site/templates/common/mail/ms.json site/templates/common/mail/ms.json
site/templates/common/mail/pt_BR.json
site/templates/common/mail/tr.json site/templates/common/mail/tr.json
site/templates/common/mail/vi.json site/templates/common/mail/vi.json
site/templates/common/mail/zh_CN.json site/templates/common/mail/zh_CN.json