Merge branch 'v2.0'

This commit is contained in:
Christophe Maudoux 2020-02-25 20:26:05 +01:00
commit d67f32d2b2
308 changed files with 4756 additions and 8252 deletions

View File

@ -115,6 +115,11 @@ License: CC-3
Comment: This work, "star1.png", is a derivative of
"Golden star with red border.png" by ANGELUS, under CC-BYSA-3.0.
Files: lemonldap-ng-portal/site/htdocs/static/common/icons/notifsExplorer.png
Copyright: Various artists
License: CC-BY-NC-ND-3.0 or GFDL-1.3
Comment: downloaded from https://commons.wikimedia.org
Files: lemonldap-ng-portal/site/htdocs/static/common/icons/decryptValue.png
Copyright: Christophe Maudoux <chrmdx@gmail.com>
License: CC-3

View File

@ -1130,6 +1130,11 @@ tidy: clean
find lemon*/ -type f \( -name '*.pm' -or -name '*.pl' -or -name '*.fcgi' -or -name '*.t' \) -print -exec perltidy -se -b {} \; ; \
else echo "Wrong perltidy version, please install Perl::Tidy@20181120" ; exit 1 ;\
fi
for f in `find lemon*/ -type f \( -name '*.pm' -or -name '*.pl' -or -name '*.fcgi' -or -name '*.t' \)`; do \
echo -n $$f; \
perltidy -se -b $$f; \
echo; \
done
find lemon*/ -name '*.bak' -delete
$(MAKE) json

View File

@ -34,6 +34,9 @@
RewriteCond "%{REQUEST_URI}" "!^/(?:(?:static|javascript|favicon).*|.*\.fcgi(?:/.*)?)$"
RewriteRule "^/(.+)$" "/index.fcgi/$1" [PT]
# Uncomment this to mitigate memory leaks when using Perl 5.16
# FcgidMaxRequestsPerProcess 500
# Note that Content-Security-Policy header is generated by portal itself
<Files *.fcgi>
SetHandler fcgid-script

View File

@ -35,6 +35,9 @@
RewriteCond "%{REQUEST_URI}" "!^/(?:(?:static|javascript|favicon).*|.*\.fcgi(?:/.*)?)$"
RewriteRule "^/(.+)$" "/index.fcgi/$1" [PT]
# Uncomment this to mitigate memory leaks when using Perl 5.16
# FcgidMaxRequestsPerProcess 500
# Note that Content-Security-Policy header is generated by portal itself
<Files *.fcgi>
SetHandler fcgid-script

View File

@ -30,6 +30,9 @@
RewriteCond "%{REQUEST_URI}" "!^/(?:(?:static|javascript|favicon).*|.*\.fcgi(?:/.*)?)$"
RewriteRule "^/(.+)$" "/index.fcgi/$1" [PT]
# Uncomment this to mitigate memory leaks when using Perl 5.16
# FcgidMaxRequestsPerProcess 500
# Note that Content-Security-Policy header is generated by portal itself
<Files *.fcgi>
SetHandler fcgid-script

2
debian/control vendored
View File

@ -43,7 +43,9 @@ Build-Depends-Indep: libapache-session-perl <!nocheck>,
libstring-random-perl <!nocheck>,
libtest-mockobject-perl <!nocheck>,
libtest-pod-perl <!nocheck>,
libtest-output-perl <!nocheck>,
libtext-unidecode-perl <!nocheck>,
libtime-fake-perl <!nocheck>,
libunicode-string-perl <!nocheck>,
liburi-perl <!nocheck>,
libwww-perl <!nocheck>,

View File

@ -21,3 +21,14 @@ GROUP=__GROUP__
#ENGINE=FCGI::EV
#ENGINE=FCGI::Engine
#ENGINE=FCGI::Engine::ProcManager
# Process recycling
# When running with Perl 5.16, you might encounter memory
# leaks when running the FastCGI server
# By default, we restart each worker after 500 requests to mitigate
# the leak. You can finetune these settings here.
# See also FCGI::ProcManager::Constrained(3)
PM_MAX_REQUESTS=500
#PM_SIZECHECK_NUM_REQUESTS=10
#PM_MAX_SIZE=100000

View File

@ -10,9 +10,9 @@ use Lemonldap::NG::Handler::Main::Reload;
our $VERSION = '2.1.0';
our (
$foreground, $engine, $nproc, $pidFile,
$socket, $user, $listen, $group,
$customFunctionsFile, %plackOptions
$foreground, $engine, $nproc, $pidFile,
$socket, $user, $listen, $group,
$procmanager, $customFunctionsFile, %plackOptions
);
my %_apps;
@ -29,6 +29,9 @@ $user ||= $ENV{USER};
$group ||= $ENV{GROUP};
$customFunctionsFile ||= $ENV{CUSTOM_FUNCTIONS_FILE};
# If the user specified any PM_ constrains, run under ::Constrained
$procmanager = "FCGI::ProcManager::Constrained" if grep /^PM_/, keys %ENV;
#Getopt::Long::Configure ("bundling_values");
GetOptions(
'foreground' => \$foreground,
@ -93,7 +96,7 @@ my %builder = (
die "Unable to load $_[0]->{SCRIPT_FILENAME}";
}
return $_apps{$script}->(@_);
}
}
},
);
@ -126,6 +129,7 @@ $server->parse_options(
'--proc-title' => 'llng-fastcgi-server',
( $foreground ? () : '--daemonize' ),
'--no-default-middleware',
( $procmanager ? ( '--manager', $procmanager ) : () ),
%plackOptions,
);
@ -216,14 +220,10 @@ Plack::Handler engine, default to FCGI (see below)
=item --plackOptions:
other options to pass to Plack. This multi-valued parameter must have
"key=value" values.
other options to pass to the Plack handler. This multi-valued parameter must
have "key=value" values.
Example to use L<FCGI::ProcManager::Constrained> instead of default FCGI manager
(L<FCGI::ProcManager>):
llng-fastcgi-server -u nobody -g nobody -s /run/llng.sock -e FCGI -n 10 \
--plackOptions manager=FCGI::ProcManager::Constrained
See Plack::Handler::FCGI for a list of options for the default FCGI engine
=back
@ -236,22 +236,6 @@ other engines can be used:
It uses L<FCGI::ProcManager> as manager. Other managers:
=over
=item L<FCGI::ProcManager::Constrained>
Example to launch it:
llng-fastcgi-server -u nobody -g nobody -s /run/llng.sock -e FCGI -n 10 \
--plackOptions manager=FCGI::ProcManager::Constrained
You can then set environment values (in /etc/default/llng-fastcgi-server file
for example):
PM_MAX_REQUESTS=10000
PM_SIZECHECK_NUM_REQUESTS=100
PM_MAX_SIZE=300000
=item L<FCGI::ProcManager::Dynamic>
llng-fastcgi-server -u nobody -g nobody -s /run/llng.sock -e FCGI -n 10 \

View File

@ -107,6 +107,7 @@ sub store {
$req->content( to_json($conf) );
$req->header( 'Content-Type' => 'application/json' );
my $resp = $self->ua->request($req);
if ( $resp->is_success ) {
my $res;
eval { $res = from_json( $resp->content, { allow_nonref => 1 } ) };

View File

@ -41,7 +41,7 @@ sub available {
closedir D;
@conf =
sort { $a <=> $b }
map { /lmConf-(\d+)\.yaml/ ? ( $1 + 0 ) : () } @conf;
map { /lmConf-(\d+)\.yaml/ ? ( $1 + 0 ) : () } @conf;
return @conf;
}

View File

@ -24,7 +24,7 @@ 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)|issuerDBGetParamete)r|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)|s(?:aml(?:S(?:PMetaData(?:(?:ExportedAttribute|Option|Macro)s|Node|XML)|torageOptions)|IDPMetaData(?:(?:ExportedAttribute|Option)s|Node|XML))|essionDataToRemember|laveExportedVars|fExtra)|c(?:as(?:A(?:ppMetaData(?:(?:ExportedVar|Option|Macro)s|Node)|ttributes)|S(?:rvMetaData(?:(?:ExportedVar|Option)s|Node)|torageOptions))|(?:ustom(?:Plugins|Add)Param|ombModule)s)|p(?:ersistentStorageOptions|o(?:rtalSkinRules|st))|a(?:ut(?:hChoiceMod|oSigninR)ules|pplicationList)|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)|ingle(?:Session(?:UserByIP)?|(?:UserBy)?IP)|oap(?:Session|Config)Server|t(?:ayConnecte|orePasswor)d|kipRenewConfirmation|fRemovedUseNotif|laveDisplayLogo|howLanguages|slByAjax)|o(?:idc(?:RPMetaDataOptions(?:Re(?:freshToken|quirePKCE)|LogoutSessionRequired|IDTokenForceClaims|BypassConsent|AllowOffline|Public)|ServiceAllow(?:(?:AuthorizationCode|Implicit|Hybrid)Flow|DynamicRegistration)|OPMetaDataOptions(?:(?:CheckJWTSignatur|UseNonc)e|StoreIDToken))|ldNotifFormat)|p(?:ortal(?:Display(?:Re(?:setPassword|gister)|GeneratePassword|PasswordPolicy)|ErrorOn(?:ExpiredSession|MailNotFound)|(?:CheckLogin|Statu)s|OpenLinkInNewWindow|RequireOldPassword|ForceAuthn|AntiFrame)|roxyUseSoap)|c(?:a(?:ptcha_(?:register|login|mail)_enabled|sSrvMetaDataOptions(?:Gateway|Renew))|heck(?:User(?:Display(?:PersistentInfo|EmptyValues))?|State|XSS)|o(?:ntextSwitchingStopWithLogout|mpactConf|rsEnabled)|da)|l(?:dap(?:(?:Group(?:DecodeSearchedValu|Recursiv)|UsePasswordResetAttribut)e|(?:AllowResetExpired|Set)Password|ChangePasswordAsUser|PpolicyControl|ITDS)|oginHistoryEnabled)|i(?:ssuerDB(?:OpenID(?:Connect)?|SAML|CAS|Get)Activation|mpersonationSkipEmptyValues)|no(?:tif(?:ication(?:Server(?:(?:POS|GE)T|DELETE)?)?|y(?:Deleted|Other))|AjaxHook)|to(?:tp2f(?:UserCan(?:Chang|Remov)eKey|DisplayExistingSecret)|kenUseGlobalStorage)|u(?:se(?:RedirectOn(?:Forbidden|Error)|SafeJail)|2fUserCanRemoveKey|pgradeSession)|re(?:st(?:(?:Session|Config)Server|ExportSecretKeys)|freshSessions)|(?:mai(?:lOnPasswordChang|ntenanc)|vhostMaintenanc)e|d(?:isablePersistentStorage|biDynamicHashEnabled)|br(?:owsersDontStorePassword|uteForceProtection)|(?:(?:globalLogout|active)Tim|wsdlServ)er|h(?:ideOldPassword|ttpOnly)|yubikey2fUserCanRemoveKey|krb(?:RemoveDomain|ByJs))$/;
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)|ingle(?:Session(?:UserByIP)?|(?:UserBy)?IP)|oap(?:Session|Config)Server|t(?:ayConnecte|orePasswor)d|kipRenewConfirmation|fRemovedUseNotif|laveDisplayLogo|howLanguages|slByAjax)|o(?:idc(?:RPMetaDataOptions(?:Re(?:freshToken|quirePKCE)|LogoutSessionRequired|IDTokenForceClaims|BypassConsent|AllowOffline|Public)|ServiceAllow(?:(?:AuthorizationCode|Implicit|Hybrid)Flow|DynamicRegistration)|OPMetaDataOptions(?:(?:CheckJWTSignatur|UseNonc)e|StoreIDToken))|ldNotifFormat)|p(?:ortal(?:Display(?:Re(?:setPassword|gister)|GeneratePassword|PasswordPolicy)|ErrorOn(?:ExpiredSession|MailNotFound)|(?:CheckLogin|Statu)s|OpenLinkInNewWindow|RequireOldPassword|ForceAuthn|AntiFrame)|roxyUseSoap)|c(?:a(?:ptcha_(?:register|login|mail)_enabled|sSrvMetaDataOptions(?:Gateway|Renew))|heck(?:User(?:Display(?:PersistentInfo|EmptyValues))?|State|XSS)|o(?:ntextSwitchingStopWithLogout|mpactConf|rsEnabled)|da)|l(?:dap(?:(?:Group(?:DecodeSearchedValu|Recursiv)|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)|br(?:uteForceProtection(?:IncrementalTempo)?|owsersDontStorePassword)|re(?:st(?:(?:Session|Config)Server|ExportSecretKeys)|freshSessions)|(?:mai(?:lOnPasswordChang|ntenanc)|vhostMaintenanc)e|d(?:isablePersistentStorage|biDynamicHashEnabled)|g(?:roupsBeforeMacros|lobalLogoutTimer)|h(?:ideOldPassword|ttpOnly)|yubikey2fUserCanRemoveKey|(?:activeTim|wsdlServ)er|krb(?:RemoveDomain|ByJs))$/;
our @sessionTypes = ( 'remoteGlobal', 'global', 'localSession', 'persistent', 'saml', 'oidc', 'cas' );

View File

@ -19,8 +19,10 @@ sub defaultValues {
'authentication' => 'Demo',
'available2F' => 'UTOTP,TOTP,U2F,REST,Mail2F,Ext2F,Yubikey,Radius',
'available2FSelfRegistration' => 'TOTP,U2F,Yubikey',
'bruteForceProtectionLockTimes' => '5 15 60 300 600',
'bruteForceProtectionMaxAge' => 300,
'bruteForceProtectionMaxFailed' => 3,
'bruteForceProtectionMaxLockTime' => 900,
'bruteForceProtectionTempo' => 30,
'captcha_mail_enabled' => 1,
'captcha_register_enabled' => 1,
@ -172,6 +174,7 @@ sub defaultValues {
'notificationServerPOST' => 1,
'notificationServerSentAttributes' =>
'uid reference date title subtitle text check',
'notificationsMaxRetrieve' => 3,
'notificationStorage' => 'File',
'notificationStorageOptions' => {
'dirName' => '/var/lib/lemonldap-ng/notifications'
@ -220,6 +223,8 @@ sub defaultValues {
'passwordPolicyMinSize' => 0,
'passwordPolicyMinUpper' => 0,
'passwordResetAllowedRetries' => 3,
'persistentSessionAttributes' =>
'_loginHistory _2fDevices notification_',
'port' => -1,
'portal' => 'http://auth.example.com/',
'portalAntiFrame' => 1,
@ -321,6 +326,7 @@ sub defaultValues {
'slaveExportedVars' => {},
'SMTPServer' => '',
'SMTPTLS' => '',
'soapProxyUrn' => 'urn:Lemonldap/NG/Common/PSGI/SOAPService',
'SSLAuthnLevel' => 5,
'SSLVar' => 'SSL_CLIENT_S_DN_Email',
'SSLVarIf' => {},

View File

@ -198,8 +198,8 @@ sub virtualHosts {
# If rule contains a comment or an AuthLevel, split them
if ( $query eq 'locationRules' ) {
$res->{comment} = '';
$res->{level} = '';
$res->{level} = $1 if ( $r =~ s/\(\?#AuthnLevel=(-?\d+)\)// );
$res->{level} = '';
$res->{level} = $1 if ( $r =~ s/\(\?#AuthnLevel=(-?\d+)\)// );
if ( $r =~ s/\(\?#(.*?)\)// ) {
$res->{title} = $res->{comment} = $1;
}
@ -710,9 +710,9 @@ sub combModules {
my $res = [];
foreach my $mod ( keys %$val ) {
my $tmp;
$tmp->{title} = $mod;
$tmp->{id} = "combModules/$mod";
$tmp->{type} = 'cmbModule';
$tmp->{title} = $mod;
$tmp->{id} = "combModules/$mod";
$tmp->{type} = 'cmbModule';
$tmp->{data}->{$_} = $val->{$mod}->{$_} foreach (qw(type for));
my $over = $val->{$mod}->{over} // {};
$tmp->{data}->{over} = [ map { [ $_, $over->{$_} ] } keys %$over ];
@ -786,8 +786,8 @@ sub metadata {
}
# Find next and previous conf
my @a = $self->confAcc->available;
my $id = -1;
my @a = $self->confAcc->available;
my $id = -1;
my ($ind) = map { $id++; $_ == $res->{cfgNum} ? ($id) : () } @a;
if ($ind) { $res->{prev} = $a[ $ind - 1 ]; }
if ( defined $ind and $ind < $#a ) {

View File

@ -166,9 +166,9 @@ sub serviceToXML {
foreach (@param_assertion) {
my @_tab = split( /;/, $self->getValue( $_, $conf ) );
$template->param( $_ . 'Default', $_tab[0] ? 'true' : 'false' );
$template->param( $_ . 'Index', $_tab[1] );
$template->param( $_ . 'Binding', $_tab[2] );
$template->param( $_ . 'Default', $_tab[0] ? 'true' : 'false' );
$template->param( $_ . 'Index', $_tab[1] );
$template->param( $_ . 'Binding', $_tab[2] );
$template->param( $_ . 'Location', $_tab[3] );
}

View File

@ -19,6 +19,11 @@ sub import {
}
}
has extension => (
is => 'rw',
default => 'json'
);
has notifField => (
is => 'rw',
builder => sub {
@ -31,6 +36,14 @@ has notifField => (
}
);
sub BUILD {
my $self = shift;
$self->extension('xml') if $self->p->conf->{oldNotifFormat};
$self->logger->debug( 'Use extension "'
. $self->extension
. '" to store notification files' );
}
sub getNotifications {
my ( $self, $uid ) = @_;
my $forAll = $self->get( $self->conf->{notificationWildcard} );
@ -49,4 +62,17 @@ sub getNotifications {
}
}
sub getAcceptedNotifs {
my ( $self, $uid, $ref ) = @_;
my $forAll =
$self->getAccepted( $self->conf->{notificationWildcard}, $ref );
my $forUser = $self->getAccepted( $uid, $ref );
if ( $forUser and $forAll ) {
return { %$forUser, %$forAll };
}
else {
return ( ( $forUser ? $forUser : $forAll ), $forUser );
}
}
1;

View File

@ -92,6 +92,38 @@ sub get {
return $result;
}
# Returns accepted notifications corresponding to the user $uid.
# If $ref is set, returns only notification corresponding to this reference.
sub getAccepted {
my ( $self, $uid, $ref ) = @_;
return () unless ($uid);
$self->_execute(
"SELECT * FROM "
. $self->dbiTable
. " WHERE done IS NOT NULL AND uid=?"
. ( $ref ? " AND ref=?" : '' )
. " ORDER BY date",
$uid,
( $ref ? $ref : () )
) or return ();
my $result;
while ( my $h = $self->sth->fetchrow_hashref() ) {
# Get XML message
my $xml = $h->{xml};
# Decode it to get the correct uncoded string
Encode::from_to( $xml, "utf8", "iso-8859-1", Encode::FB_CROAK );
# Store message in result
my $identifier =
&getIdentifier( $self, $h->{uid}, $h->{ref}, $h->{date} );
$result->{$identifier} = $xml;
}
$self->logger->warn( $self->sth->err() ) if ( $self->sth->err() );
return $result;
}
## @method hashref getAll()
# Return all pending notifications.
# @return hashref where keys are internal reference and values are hashref with

View File

@ -14,11 +14,8 @@ our $VERSION = '2.1.0';
extends 'Lemonldap::NG::Common::Notifications';
our $ext = 'json';
sub import {
shift;
$ext = 'xml' if ( $_[0] eq 'XML' );
return Lemonldap::NG::Common::Notifications->import(@_);
}
@ -38,6 +35,7 @@ has fileNameSeparator => ( is => 'rw', default => '_' );
# If $ref is set, returns only notification corresponding to this reference.
sub get {
my ( $self, $uid, $ref ) = @_;
my $ext = $self->extension;
return () unless ($uid);
my $fns = $self->{fileNameSeparator};
my $identifier = &getIdentifier( $self, $uid, $ref );
@ -58,12 +56,37 @@ sub get {
return $files;
}
# Returns accepted notification corresponding to the user $uid.
# If $ref is set, returns only notification corresponding to this reference.
sub getAccepted {
my ( $self, $uid, $ref ) = @_;
return () unless ($uid);
my $fns = $self->{fileNameSeparator};
my $identifier = &getIdentifier( $self, $uid, $ref );
opendir D, $self->{dirName};
my @notif = grep /^\d{8}${fns}${identifier}\S*\.done$/, readdir(D);
closedir D;
my $files;
foreach my $file (@notif) {
unless ( open F, '<', $self->{dirName} . "/$file" ) {
$self->logger->error(
"Unable to read notification $self->{dirName}/$file");
next;
}
$files->{$file} = join( '', <F> );
}
return $files;
}
## @method hashref getAll()
# Return all pending notifications.
# @return hashref where keys are internal reference and values are hashref with
# keys date, uid, ref and condition.
sub getAll {
my $self = shift;
my $ext = $self->extension;
opendir D, $self->{dirName};
my @notif;
my $fns = $self->{fileNameSeparator};
@ -89,6 +112,7 @@ sub getAll {
# keys date, uid, ref and condition.
sub getExisting {
my $self = shift;
my $ext = $self->extension;
opendir D, $self->{dirName};
my @notif;
my $fns = $self->{fileNameSeparator};
@ -113,6 +137,7 @@ sub getExisting {
# @param $myref identifier returned by get() or getAll()
sub delete {
my ( $self, $myref ) = @_;
my $ext = $self->extension;
my $new = ( $myref =~ /(.*?)(?:\.$ext)$/ )[0] . '.done';
return rename( $self->{dirName} . "/$myref", $self->{dirName} . "/$new" );
}
@ -129,6 +154,7 @@ sub purge {
# Insert a new notification
sub newNotif {
my ( $self, $date, $uid, $ref, $condition, $content ) = @_;
my $ext = $self->extension;
my $fns = $self->{fileNameSeparator};
$fns ||= '_';
my @t = split( /\D+/, $date );

View File

@ -42,10 +42,11 @@ sub newNotification {
unless ( exists $notif->{condition} ) {
$self->userLogger->info(
"Set defaultCondition ($defaultCond) for notification $notif->{reference}");
"Set defaultCondition ($defaultCond) for notification $notif->{reference}"
);
$notif->{condition} = $defaultCond;
}
push @data, ( $notif->{condition} );
$notif->{date} =~ s/^(\d{4}-\d{2}-\d{2}).*$/$1/;
my $body = to_json($notif);

View File

@ -9,7 +9,7 @@ package Lemonldap::NG::Common::Notifications::LDAP;
use strict;
use Mouse;
use Time::Local;
use MIME::Base64;
use MIME::Base64 qw/encode_base64url/;
use Net::LDAP;
use utf8;
@ -45,6 +45,16 @@ has ldapBindDN => (
}
);
has ldapBindPassword => (
is => 'ro',
lazy => 1,
default => sub {
$_[0]
->p->logger->warn('Warning: "ldapBindPassword" parameter is not set');
return '';
}
);
# Returns notifications corresponding to the user $uid.
# If $ref is set, returns only notification corresponding to this reference.
sub get {
@ -57,7 +67,39 @@ sub get {
. ( $ref ? '(description={ref}' . $ref . ')' : '' ) . ')';
my @entries = _search( $self, $filter );
my $result = {};
my $result;
foreach my $entry (@entries) {
my @notifValues = $entry->get_value('description');
my $f = {};
foreach (@notifValues) {
my ( $k, $v ) = ( $_ =~ /\{(.*?)\}(.*)/smg );
$v = decodeLdapValue($v);
$f->{$k} = $v;
}
my $xml = $f->{xml};
utf8::encode($xml);
my $identifier =
&getIdentifier( $self, $f->{uid}, $f->{ref}, $f->{date} );
$result->{$identifier} = "$xml";
$self->logger->info("notification $identifier found");
}
return $result;
}
# Returns accepted notifications corresponding to the user $uid.
# If $ref is set, returns only notification corresponding to this reference.
sub getAccepted {
my ( $self, $uid, $ref ) = @_;
return () unless ($uid);
my $filter =
'(&(objectClass=applicationProcess)(description={done}*)'
. "(description={uid}$uid)"
. ( $ref ? '(description={ref}' . $ref . ')' : '' ) . ')';
my @entries = _search( $self, $filter );
my $result;
foreach my $entry (@entries) {
my @notifValues = $entry->get_value('description');
my $f = {};
@ -195,9 +237,8 @@ sub newNotif {
return ( 0, "Bad date" ) if ($@);
$date =~ s/-//g;
return ( 0, "Bad date" ) unless ( $date =~ /^\d{8}/ );
my $cn = "${date}${fns}${uid}${fns}" . encode_base64( $ref, '' );
$cn .= "${fns}" . encode_base64( $condition, '' ) if $condition;
$xml = $xml->serialize();
my $cn = "${date}${fns}${uid}${fns}" . encode_base64url( $ref, '' );
$cn .= "${fns}" . encode_base64url( $condition, '' ) if $condition;
my $fields =
$condition =~ /.+/
@ -347,7 +388,7 @@ sub _store {
);
if ( $add->code ) {
$self->logError($add);
$self->logger->error( $add->error );
return 0;
}

View File

@ -45,7 +45,7 @@ sub newNotification {
$self->logger->error("$err");
return 0;
}
# Prevent to store time. Keep date only
$tmp =~ s/^(\d{4}-\d{2}-\d{2}).*$/$1/;
push @data, $tmp;
@ -59,8 +59,8 @@ sub newNotification {
}
else {
$self->userLogger->info(
"Set defaultCondition ($defaultCond) for notification " . $notif->{reference}
);
"Set defaultCondition ($defaultCond) for notification "
. $notif->{reference} );
push @data, $defaultCond;
}
}

View File

@ -35,17 +35,17 @@ sub init {
foreach my $k ( keys %$args ) {
$self->{$k} = $args->{$k} unless ( $k eq 'logger' );
}
unless ( $self->logger and $self->userLogger ) {
unless ( ref( $self->logger ) and ref( $self->userLogger ) ) {
my $logger =
$args->{logger}
|| $ENV{LLNG_DEFAULTLOGGER}
|| 'Lemonldap::NG::Common::Logger::Std';
unless ( $self->logger ) {
unless ( ref $self->logger ) {
eval "require $logger";
die $@ if ($@);
$self->logger( $logger->new($self) );
}
unless ( $self->userLogger ) {
unless ( ref $self->userLogger ) {
$logger = $ENV{LLNG_USERLOGGER} || $args->{userLogger} || $logger;
eval "require $logger";
die $@ if ($@);

View File

@ -52,7 +52,8 @@ sub userData {
return $self->{userData}
|| {
( $Lemonldap::NG::Handler::Main::tsv->{whatToTrace}
|| '_whatToTrace' ) => $self->{user}, };
|| '_whatToTrace' ) => $self->{user},
};
}
sub respHeaders {

View File

@ -64,8 +64,8 @@ sub checkLogonHours {
# Use time_correction
if ($time_correction) {
my ( $sign, $time ) = ( $time_correction =~ /([+|-]?)(\d+)/ );
if ( $sign =~ /-/ ) { $hourpos -= $time; }
else { $hourpos += $time; }
if ( $sign =~ /-/ ) { $hourpos -= $time; }
else { $hourpos += $time; }
}
# Get the corresponding byte

View File

@ -131,6 +131,7 @@ sub BUILD {
if ( $self->{info} ) {
foreach ( keys %{ $self->{info} } ) {
next if ( $_ eq "_session_id" and $data->{_session_id} );
if ( defined $self->{info}->{$_} ) {
$data->{$_} = $self->{info}->{$_};
}

View File

@ -283,11 +283,11 @@ sub getMod {
my ( $self, $req ) = @_;
my ( $s, $m );
unless ( $s = $req->params('sessionType') ) {
$self->error($req->error('Session type is required'));
$self->error( $req->error('Session type is required') );
return ();
}
unless ( $m = $self->sessionTypes->{$s} ) {
$self->error($req->error('Unknown (or unconfigured) session type'));
$self->error( $req->error('Unknown (or unconfigured) session type') );
return ();
}
if ( my $kind = $req->params('kind') ) {

View File

@ -22,7 +22,7 @@ for ( my $i = 0 ; $i < @ARGV ; $i++ ) {
$action ||= "help";
if ( $action =~ /^(?:[gs]et|(?:add|del)Key|save|restore)$/ ) {
if ( $action =~ /^(?:[gs]et|(?:add|del)Key|save|restore|rollback)$/ ) {
eval { require Lemonldap::NG::Manager::Cli; };
die "Manager libraries not available, aborting ($@)" if ($@);
Lemonldap::NG::Manager::Cli->run(@ARGV);
@ -50,6 +50,14 @@ Available actions:
- save : export configuration to STDOUT
- restore - : import configuration from STDIN
- restore <file> : import configuration from file
- rollback : restore previous configuration
Options:
- yes <0|1> : accept confirmation prompt automatically
- log <msg> : set configuration log message
- safe <0|1> : fail in case the requested configuration is invalid
- force <0|1> : allow overwrite of existing config number
- cfgNum <num> : set new configuration number (requires -force 1)
See Lemonldap::NG::Manager::Cli(3) for more
};
@ -75,6 +83,7 @@ Update local configuration cache
Save configuration
$ lemonldap-ng-cli save >conf.json
$ lemonldap-ng-cli -cfgNum 19 save >conf-19.json
Restore configuration
@ -82,6 +91,10 @@ Restore configuration
# OR
$ lemonldap-ng-cli restore - <conf.json
Cancel the last configuration change
$ lemonldap-ng-cli rollback
Get a configuration parameter value
$ lemonldap-ng-cli get portal domain cookieName
@ -92,6 +105,12 @@ Set some values
# add or set a key
$ lemonldap-ng-cli addKey macro fullname '$givenName." ".$lastName'
# without changing the version number
$ lemonldap-ng-cli -force 1 -cfgNum 1 set portal http://auth.e.com/ domain e.com
# without asking for confirmation
$ lemonldap-ng-cli -yes 1 set portal http://auth.e.com/ domain e.com
=head1 DESCRIPTION
lemonldap-ng-cli is a command line interface to interact with Lemonldap::NG
@ -120,6 +139,32 @@ and L<Lemonldap::NG::Common::Cli>
=back
=head2 Available options
=over
=item -yes
Confirm modification automatically (default: 0)
=item -log
Allows you to set the log message that will be displayed in the manager
=item -safe
The configuration change will be aborted if it contains errors (default: 0)
=item -cfgNum
Choose a particular configuration number (default: latest)
=item -force
Allows you to force overwriting an existing configuration (default: 0)
=back
=head1 SEE ALSO
L<Lemonldap::NG::Manager::Cli>, L<Lemonldap::NG::Common::Cli>

View File

@ -64,4 +64,3 @@ t/lmConf-1.json
t/sessions/lock/Apache-Session-f5eec18ebb9bc96352595e2d8ce962e8ecf7af7c9a98cb9a43f9cd181cf4b545.lock
t/test-psgi-lib.pm
t/test.pm
t/Time-Fake.pm

View File

@ -66,7 +66,7 @@ q"I refuse to compile rules.json when useSafeJail isn't activated! Yes I know, I
$json->{rules} ||= { default => 1 };
$json->{headers} //= { 'Auth-User' => '$uid' };
$class->locationRulesInit( undef, { $vhost => $json->{rules} } );
$class->headersInit( undef, { $vhost => $json->{headers} } );
$class->headersInit( undef, { $vhost => $json->{headers} } );
$class->tsv->{lastVhostUpdate}->{$vhost} = time;
return;
}

View File

@ -75,11 +75,13 @@ sub fetchId {
# Get access token session
my $infos = $class->getOIDCInfos($access_token);
# If this token is tied to a regular session ID
if ( my $_session_id = $infos->{user_session_id} ) {
$class->logger->debug( 'Get user session id ' . $_session_id );
return $_session_id;
}
# If this token is tied to an Offline session
if ( my $_session_id = $infos->{offline_session_id} ) {
$class->logger->debug( 'Get offline session id ' . $_session_id );

View File

@ -45,7 +45,8 @@ sub fetchId {
}
# Is token in good interval ?
my $ttl = $class->localConfig->{vhostOptions}->{$vhost}->{vhostServiceTokenTTL}
my $ttl =
$class->localConfig->{vhostOptions}->{$vhost}->{vhostServiceTokenTTL}
|| $class->tsv->{serviceTokenTTL}->{$vhost};
$ttl = $class->tsv->{handlerServiceTokenTTL} unless ( $ttl and $ttl > 0 );
my $now = time;

View File

@ -68,7 +68,8 @@ sub run {
my ( $user, $uri, $code ) = ( $1, $2, $3 );
# Portal error translation
$code = portalConsts->{$code} || $code if ( $code =~ /^\-?\d+$/ );
$code = portalConsts->{$code} || $code
if ( $code =~ /^\-?\d+$/ );
# Per user activity
$status->{user}->{$user}->{$code}++;

View File

@ -440,14 +440,15 @@ sub fetchId {
my ( $class, $req ) = @_;
my $t = $req->{env}->{HTTP_COOKIE} or return 0;
my $vhost = $class->resolveAlias($req);
$class->logger->debug("VH $vhost is HTTPS") if $class->_isHttps( $req, $vhost );
$class->logger->debug("VH $vhost is HTTPS")
if $class->_isHttps( $req, $vhost );
my $lookForHttpCookie = ( $class->tsv->{securedCookie} =~ /^(2|3)$/
and not $class->_isHttps( $req, $vhost ) );
my $cn = $class->tsv->{cookieName};
my $value =
$lookForHttpCookie
? ( $t =~ /${cn}http=([^,; ]+)/o ? $1 : 0 )
: ( $t =~ /$cn=([^,; ]+)/o ? $1 : 0 );
: ( $t =~ /$cn=([^,; ]+)/o ? $1 : 0 );
if ( $value && $lookForHttpCookie && $class->tsv->{securedCookie} == 3 ) {
$value = $class->tsv->{cipher}->decryptHex( $value, "http" );

View File

@ -49,7 +49,7 @@ sub addAuthRouteWithRedirect {
sub _auth_and_redirect {
my ( $self, $req ) = @_;
$self->api->goToPortal( $req, $req->{env}->{REQUEST_URI} );
return [ 302, [$req->spliceHdrs], [] ];
return [ 302, [ $req->spliceHdrs ], [] ];
}
sub defaultAuthRoute {
@ -73,7 +73,7 @@ sub _run {
if ( $res->[0] < 300 ) {
$self->routes( $self->authRoutes );
$req->userData( $self->api->data );
$req->respHeaders($res->[1]);
$req->respHeaders( $res->[1] );
}
elsif ( $res->[0] != 403 and not $req->data->{noTry} ) {

View File

@ -36,17 +36,17 @@ ok(
( defined($code) and ref($code) eq 'CODE' ),
'encode_base64 function is defined'
);
ok( $res = &$code, "Function works" );
ok( $res = &$code, "Function works" );
ok( $res eq 'dGVzdA==', 'Get good result' );
$sub = "sub { return ( listMatch('ABC; DEF; GHI','abc',1) ) }";
my $code = $jail->jail_reval($sub);
$sub = "sub { return ( listMatch('ABC; DEF; GHI','abc',1) ) }";
$code = $jail->jail_reval($sub);
ok( ( defined($code) and ref($code) eq 'CODE' ),
'listMatch function is defined' );
ok( &$code eq '1', 'Get good result' );
$sub = "sub { return ( listMatch('ABC; DEF; GHI','ab',1) ) }";
my $code = $jail->jail_reval($sub);
$sub = "sub { return ( listMatch('ABC; DEF; GHI','ab',1) ) }";
$code = $jail->jail_reval($sub);
ok( ( defined($code) and ref($code) eq 'CODE' ),
'listMatch function is defined' );
ok( &$code eq '0', 'Get good result' );
@ -58,5 +58,5 @@ ok(
'checkDate extended function is defined'
);
ok( $res = &$code, "Function works" );
ok( $res == 1, 'Get good result' );
ok( $res == 1, 'Get good result' );

View File

@ -49,8 +49,8 @@ ok( ( defined($listMatch) and ref($listMatch) eq 'CODE' ),
'listMatch function is defined' );
ok( &$listMatch eq '1', 'Get good result' );
my $sub5 = "sub { return ( listMatch('ABC; DEF; GHI','ab', 1) ) }";
my $listMatch = $jail->jail_reval($sub5);
my $sub5 = "sub { return ( listMatch('ABC; DEF; GHI','ab', 1) ) }";
$listMatch = $jail->jail_reval($sub5);
ok( ( defined($listMatch) and ref($listMatch) eq 'CODE' ),
'listMatch function is defined' );
ok( &$listMatch eq '0', 'Get good result' );

View File

@ -41,33 +41,55 @@ ok( $h{'Auth-User'} eq 'dwho', 'Header Auth-User is set to "dwho"' )
count(1);
# Request an URI protected by custom function -> allowed
ok( $res = $client->_get( '/test-restricted_uri/dwho/', undef, undef, "lemonldap=$sessionId" ),
'Authentified query' );
ok( $res->[0] == 200, '/test-restricted_uri -> Code is 200' ) or explain( $res, 200 );
ok(
$res = $client->_get(
'/test-restricted_uri/dwho/', undef, undef, "lemonldap=$sessionId"
),
'Authentified query'
);
ok( $res->[0] == 200, '/test-restricted_uri -> Code is 200' )
or explain( $res, 200 );
count(2);
# Request an URI protected by custom function -> denied
ok( $res = $client->_get( '/test-restricted_uri/dwho', undef, undef, "lemonldap=$sessionId" ),
'Denied query' );
ok( $res->[0] == 403, '/test-restricted_uri -> Code is 403' ) or explain( $res->[0], 403 );
ok(
$res = $client->_get(
'/test-restricted_uri/dwho', undef, undef, "lemonldap=$sessionId"
),
'Denied query'
);
ok( $res->[0] == 403, '/test-restricted_uri -> Code is 403' )
or explain( $res->[0], 403 );
count(2);
# Request an URI protected by custom function -> allowed
ok( $res = $client->_get( '/test-uri2/dwho/dummy', undef, undef, "lemonldap=$sessionId" ),
'Authentified query' );
ok(
$res = $client->_get(
'/test-uri2/dwho/dummy', undef, undef, "lemonldap=$sessionId"
),
'Authentified query'
);
ok( $res->[0] == 200, '/test-uri2 -> Code is 200' ) or explain( $res, 200 );
count(2);
# Request an URI protected by custom function -> denied
ok( $res = $client->_get( '/test-uri1/dwho/', undef, undef, "lemonldap=$sessionId" ),
'Denied query' );
ok( $res->[0] == 403, '/test-uri1 -> Code is 403' ) or explain( $res->[0], 403 );
ok(
$res =
$client->_get( '/test-uri1/dwho/', undef, undef, "lemonldap=$sessionId" ),
'Denied query'
);
ok( $res->[0] == 403, '/test-uri1 -> Code is 403' )
or explain( $res->[0], 403 );
count(2);
# Request an URI protected by custom function -> denied
ok( $res = $client->_get( '/test-uri1/dwh', undef, undef, "lemonldap=$sessionId" ),
'Denied query' );
ok( $res->[0] == 403, '/test-uri1 -> Code is 403' ) or explain( $res->[0], 403 );
ok(
$res =
$client->_get( '/test-uri1/dwh', undef, undef, "lemonldap=$sessionId" ),
'Denied query'
);
ok( $res->[0] == 403, '/test-uri1 -> Code is 403' )
or explain( $res->[0], 403 );
count(2);
# Denied query

View File

@ -45,27 +45,42 @@ ok( $h{'Headervalue1'} eq 'dwho', 'Headervalue1 is set to "dwho"' )
count(2);
# Request an URI protected by custom function -> allowed
ok( $res = $client->_get( '/test-uri1/dwho', undef, undef, "lemonldap=$sessionId" ),
'Authentified query' );
ok(
$res =
$client->_get( '/test-uri1/dwho', undef, undef, "lemonldap=$sessionId" ),
'Authentified query'
);
ok( $res->[0] == 200, '/test-uri1 -> Code is 200' ) or explain( $res, 200 );
count(2);
# Request an URI protected by custom function -> allowed
ok( $res = $client->_get( '/test-uri2/dwho/dummy', undef, undef, "lemonldap=$sessionId" ),
'Authentified query' );
ok(
$res = $client->_get(
'/test-uri2/dwho/dummy', undef, undef, "lemonldap=$sessionId"
),
'Authentified query'
);
ok( $res->[0] == 200, '/test-uri2 -> Code is 200' ) or explain( $res, 200 );
count(2);
# Request an URI protected by custom function -> denied
ok( $res = $client->_get( '/test-uri1/dwho/', undef, undef, "lemonldap=$sessionId" ),
'Denied query' );
ok( $res->[0] == 403, '/test-uri1 -> Code is 403' ) or explain( $res->[0], 403 );
ok(
$res =
$client->_get( '/test-uri1/dwho/', undef, undef, "lemonldap=$sessionId" ),
'Denied query'
);
ok( $res->[0] == 403, '/test-uri1 -> Code is 403' )
or explain( $res->[0], 403 );
count(2);
# Request an URI protected by custom function -> denied
ok( $res = $client->_get( '/test-uri1/dwh', undef, undef, "lemonldap=$sessionId" ),
'Denied query' );
ok( $res->[0] == 403, '/test-uri1 -> Code is 403' ) or explain( $res->[0], 403 );
ok(
$res =
$client->_get( '/test-uri1/dwh', undef, undef, "lemonldap=$sessionId" ),
'Denied query'
);
ok( $res->[0] == 403, '/test-uri1 -> Code is 403' )
or explain( $res->[0], 403 );
count(2);
# Denied query

View File

@ -39,7 +39,7 @@ my $res;
# Unauth tests
ok( $res = $client->_get('/test'), 'Get response' );
ok( $res->[0] == 200, 'Response code is 200' )
ok( $res->[0] == 200, 'Response code is 200' )
or print "Expect 200, got $res->[0]\n";
ok( $res->[2]->[0] eq 'Unauth', 'Get unauth result' )
or print "Expect Unauth, got $res->[2]->[0]\n";
@ -64,7 +64,7 @@ count(3);
# Bad path test
ok( $res = $client->_get('/[]/test'), 'Try a bad path' );
ok( $res->[0] == 400, 'Response is 400' );
ok( $res->[0] == 400, 'Response is 400' );
count(2);
clean();

View File

@ -119,7 +119,7 @@ ok(
ok( $res->[0] == 200, 'Code is 200' ) or explain( $res->[0], 200 );
count(2);
my %headers = @{ $res->[1] };
%headers = @{ $res->[1] };
ok( $headers{'zero'} eq '0', 'Found "zero" header with "0"' )
or print STDERR Data::Dumper::Dumper( $res->[1] );
ok( $headers{'empty'} eq '', 'Found "empty" header without value' )

View File

@ -38,8 +38,9 @@ my $crypt = Lemonldap::NG::Common::Crypto->new('qwertyui');
my $token = $crypt->encrypt(
join ':', time,
$sessionId, 'test1.example.com',
'XFromVH=app1-auth.example.com', "serviceHeader1=$sessionId","serviceHeader2=$sessionId",
'test2.example.com', '*.example.com'
'XFromVH=app1-auth.example.com', "serviceHeader1=$sessionId",
"serviceHeader2=$sessionId", 'test2.example.com',
'*.example.com'
);
ok(

View File

@ -1,235 +0,0 @@
package Time::Fake;
use Carp;
use strict;
use vars '$VERSION';
$VERSION = "0.11";
#####################
my $OFFSET = 0;
*CORE::GLOBAL::time = sub() { CORE::time() + $OFFSET };
*CORE::GLOBAL::localtime = sub(;$) {
@_ ? CORE::localtime($_[0])
: CORE::localtime(CORE::time() + $OFFSET);
};
*CORE::GLOBAL::gmtime = sub(;$) {
@_ ? CORE::gmtime($_[0])
: CORE::gmtime(CORE::time() + $OFFSET);
};
sub import {
my $pkg = shift;
$pkg->offset(shift);
}
sub offset {
my $pkg = shift;
return $OFFSET if !@_;
my $old_offset = $OFFSET;
$OFFSET = _to_offset(shift);
return $old_offset;
}
sub reset {
shift->offset(0);
}
my %mult = (
s => 1,
m => 60,
h => 60*60,
d => 60*60*24,
M => 60*60*24*30,
y => 60*60*24*365,
);
sub _to_offset {
my $t = shift || return 0;
if ($t =~ m/^([+-]\d+)([smhdMy]?)$/) {
$t = $1 * $mult{ $2 || "s" };
} elsif ($t !~ m/\D/) {
$t = $t - CORE::time;
} else {
croak "Invalid time offset: `$t'";
}
return $t;
}
1;
__END__
=head1 NAME
Time::Fake - Simulate different times without changing your system clock
=head1 SYNOPSIS
Pretend we are running 1 day in the future:
use Time::Fake '+1d';
Pretend we are running 1 year in the past:
use Time::Fake '-1y';
Pretend the script started at epoch time 1234567:
use Time::Fake 1234567;
See what an existing script would do if run 20 years in the future:
% perl -MTime::Fake="+20y" test.pl
Run a section of code in a time warp:
use Time::Fake;
# do some setup
Time::Fake->offset("+1y");
run_tests(); # thinks it's a year ahead
Time::Fake->reset; # back to the present
=head1 DESCRIPTION
Use this module to achieve the effect of changing your system clock, but
without actually changing your system clock. It overrides the Perl builtin
subs C<time>, C<localtime>, and C<gmtime>, causing them to return a
"faked" time of your choice. From the script's point of view, time still
flows at the normal rate, but it is just offset as if it were executing
in the past or present.
You may find this module useful in writing test scripts for code that has
time-sensitive logic.
=head1 USAGE
=head2 Using and importing:
use Time::Fake $t;
Is equivalent to:
use Time::Fake;
Time::Fake->offset($t);
See below for arguments to C<offset>. This usage makes it easy to
fake the time for existing scripts, as in:
% perl -MTime::Fake=+1y script.pl
=head2 offset
Time::Fake->offset( [$t] );
C<$t> is either an epoch time, or a relative offset of the following
form:
+3 # 3 seconds in the future
-3s # 3 seconds in the past
+1h # 1 hour in the future
etc..
Relative offsets must begin with a plus or minus symbol. The supported
units are:
s second
m minute
h hour
d day (24 hours)
M month (30 days)
y year (365 days)
If C<$t> is an epoch time, then C<time>, C<localtime>, and C<gmtime>
will act as though the the current time (when C<offset> was called) was
actually at C<$t> epoch seconds.
Otherwise, the offset C<$t> will be added to the times returned by these
builtin subs.
When C<$t> is false, C<time>, C<localtime>, C<gmtime>
remain overridden, but their behavior resets to reflect the actual
system time.
When C<$t> is omitted, nothing is changed, but C<offset> returns the
current additive offset (in seconds). Otherwise, its return value is
the I<previous> offset.
C<offset> may be called several times. However, I<The effect of multiple
calls is NOT CUMULATIVE.> That is:
Time::Fake->offset("+1h");
Time::Fake->offset("+1h");
## same as
# Time::Fake->offset("+1h");
## NOT the same as
# Time::Fake->offset("+2h");
Each call to C<offset> completely cancels out the effect of any
previous calls. To make the effect cumulative, use the return value
of calling C<offset> with no arguments:
Time::Fake->offset("+1h");
...
Time::Fake->offset( Time::Fake->offset + 3600 ); # add another hour
=head2 reset
Time::Fake->reset;
Is the same as:
Time::Fake->offset(0);
That is, it returns all the affected builtin subs to their
default behavior -- reporing the actual system time.
=head1 KNOWN CAVEATS
Time::Fake must be loaded at C<BEGIN>-time (e.g., with a standard
C<use> statement). It must be loaded before perl I<compiles> any code
that uses C<time>, C<localtime>, or C<gmtime>. Due to inherent
limitations in overriding builtin subs, any code that was compiled
before loading Time::Fake will not be affected.
Because the system clock is not being changed, only Perl code that
uses C<time>, C<localtime>, or C<gmtime> will be fooled about the date.
In particular, the operating system is not fooled,
nor are other programs. If your Perl code modifies a file for example,
the file's modification time will reflect the B<actual> (not faked) time.
Along the same lines, if your Perl script obtains the time from somewhere
other than the affected builtins subs (e.g., C<qx/date/>), the actual
(not faked) time will be reflected.
Time::Fake doesn't affect -M, -A, -C filetest operators in the way you'd
probably want. These still report the B<actual> (not faked) script start
time minus file access time.
Time::Fake has not been tested with other modules that override the time
builtins, e.g., Time::HiRes.
=head1 SEE ALSO
Time::Warp, which uses XS to fool more of Perl.
=head1 AUTHOR
Time::Fake is written by Mike Rosulek E<lt>mike@mikero.comE<gt>. Feel
free to contact me with comments, questions, patches, or whatever.
=head1 COPYRIGHT
Copyright (c) 2008 Mike Rosulek. All rights reserved. This module is free
software; you can redistribute it and/or modify it under the same terms as Perl
itself.

View File

@ -4,15 +4,12 @@ use strict;
use 5.10.0;
use POSIX 'strftime';
use Data::Dumper;
use Time::Fake;
use_ok('Lemonldap::NG::Common::PSGI::Cli::Lib');
our $client;
our $count = 1;
BEGIN {
require 't/Time-Fake.pm';
}
no warnings 'redefine';
my $module;

View File

@ -26,6 +26,7 @@ lib/Lemonldap/NG/Manager/Conf/Parser.pm
lib/Lemonldap/NG/Manager/Conf/Tests.pm
lib/Lemonldap/NG/Manager/Conf/Zero.pm
lib/Lemonldap/NG/Manager/Notifications.pm
lib/Lemonldap/NG/Manager/Plugin.pm
lib/Lemonldap/NG/Manager/Sessions.pm
lib/Lemonldap/NG/Manager/Viewer.pm
Makefile.PL

View File

@ -24,6 +24,9 @@ extends 'Lemonldap::NG::Common::Conf::AccessLib',
has csp => ( is => 'rw' );
has loadedPlugins => ( is => 'rw', default => sub { [] } );
has hLoadedPlugins => ( is => 'rw', default => sub { {} } );
## @method boolean init($args)
# Launch initialization method
#
@ -52,32 +55,50 @@ sub init {
return 0;
}
my $conf = $self->confAcc->getConf;
$conf->{$_} = $args->{$_} foreach ( keys %$args );
$self->{enabledModules} ||= "conf, sessions, notifications, 2ndFA, api";
my @links;
my @enabledModules =
map { push @links, $_; "Lemonldap::NG::Manager::" . ucfirst($_) }
map {
my @res = ( "Lemonldap::NG::Manager::" . ucfirst($_) );
if ( my $tmp = $self->loadPlugin( @res, $conf ) ) {
$self->logger->debug("Plugin $_ loaded");
push @links, $_;
push @{ $self->loadedPlugins }, $tmp;
$self->hLoadedPlugins->{$_} = $tmp;
}
else {
$self->logger->error("Unable to load $_, skipping");
@res = ();
}
(@res);
}
split( /[,\s]+/, $self->{enabledModules} );
extends 'Lemonldap::NG::Handler::PSGI::Router', @enabledModules;
my @working;
my $conf = $self->confAcc->getConf;
unless (@enabledModules) {
$self->logger->error('No plugins loaded, aborting');
return 0;
}
unless ($conf) {
require Lemonldap::NG::Manager::Conf::Zero;
$conf = Lemonldap::NG::Manager::Conf::Zero::zeroConf();
}
for ( my $i = 0 ; $i < @enabledModules ; $i++ ) {
my $mod = $enabledModules[$i];
no strict 'refs';
if ( &{"${mod}::addRoutes"}( $self, $conf ) ) {
$self->logger->debug("Module $mod enabled");
push @working, $mod;
}
else {
$links[$i] = undef;
$self->logger->error(
"Module $mod can not be enabled: " . $self->error );
}
}
return 0 unless (@working);
# TODO: -> loadPlugin
#for ( my $i = 0 ; $i < @enabledModules ; $i++ ) {
# my $mod = $enabledModules[$i];
# no strict 'refs';
# if ( &{"${mod}::addRoutes"}( $self, $conf ) ) {
# $self->logger->debug("Module $mod enabled");
# push @working, $mod;
# }
# else {
# $links[$i] = undef;
# $self->logger->error(
# "Module $mod can not be enabled: " . $self->error );
# }
#}
$self->addRoute( links => 'links', ['GET'] );
$self->addRoute( 'psgi.js' => 'sendJs', ['GET'] );
@ -88,13 +109,14 @@ sub init {
);
# Avoid restricted users to access configuration by default route
my $defaultMod = $self->{defaultModule} || 'conf';
my $defaultMod = $self->{defaultModule} =
$self->{defaultModule} || $enabledModules[0];
$self->logger->debug("Default module -> $defaultMod");
my ($index) =
grep { $working[$_] =~ /::$defaultMod$/i } ( 0 .. $#working );
$index //= $#working;
grep { $enabledModules[$_] eq $defaultMod } ( 0 .. $#enabledModules );
$index //= 0;
$self->logger->debug("Default index -> $index");
$self->defaultRoute( $working[$index]->defaultRoute );
$self->defaultRoute( $self->loadedPlugins->[$index]->defaultRoute );
# Find out more glyphicones at https://www.w3schools.com/icons/bootstrap_icons_glyphicons.asp
my $linksIcons = {
@ -110,7 +132,7 @@ sub init {
next unless ( defined $links[$i] );
push @{ $self->links },
{
target => $enabledModules[$i]->defaultRoute,
target => $self->loadedPlugins->[$i]->defaultRoute,
title => $links[$i],
icon => $linksIcons->{ $links[$i] }
};
@ -141,15 +163,20 @@ sub init {
sub tplParams {
my ( $self, $req ) = @_;
my $res = $self->brwRule->( $req, $req->{userData} ) || 0;
my $res = eval {
$self->hLoadedPlugins->{viewer}->brwRule->( $req, $req->{userData} );
} || 0;
return ( VERSION => $VERSION, ALLOWBROWSER => $res );
}
sub javascript {
my ( $self, $req ) = @_;
my $res = $self->diffRule->( $req, $req->{userData} ) || 0;
my $impPrefix = $self->{impersonationPrefix};
my $ttl = $self->{timeout} || 72000;
my $res = eval {
$self->hLoadedPlugins->{viewer}->diffRule->( $req, $req->{userData} );
} || 0;
print STDERR $@ if $@;
my $impPrefix = $self->{impersonationPrefix} || 'real_';
my $ttl = $self->{timeout} || 72000;
return
'var formPrefix=staticPrefix+"forms/";var confPrefix=scriptname+"confs/";var viewPrefix=scriptname+"view/";'
@ -176,6 +203,32 @@ sub sendHtml {
return $res;
}
sub loadPlugin {
my ( $self, $plugin, $conf ) = @_;
unless ($plugin) {
require Carp;
Carp::confess('Calling loadPugin without arg !');
}
my $obj;
$plugin = "Lemonldap::NG::Manager$plugin" if ( $plugin =~ /^::/ );
eval "require $plugin";
if ($@) {
$self->logger->error("$plugin load error: $@");
return 0;
}
eval {
$obj = $plugin->new( { p => $self, conf => $conf } );
$self->logger->debug("Module $plugin loaded");
};
if ($@) {
$self->error("Unable to build $plugin object: $@");
return 0;
}
( $obj and $obj->init($conf) ) or return 0;
return $obj;
}
1;
__END__

View File

@ -13,7 +13,8 @@ use Lemonldap::NG::Common::Conf::ReConstants;
use feature 'state';
extends 'Lemonldap::NG::Common::Conf::AccessLib',
extends 'Lemonldap::NG::Manager::Plugin',
'Lemonldap::NG::Common::Conf::AccessLib',
'Lemonldap::NG::Common::Session::REST';
our $VERSION = '2.1.0';
@ -24,7 +25,7 @@ our $VERSION = '2.1.0';
use constant defaultRoute => '2ndfa.html';
sub addRoutes {
sub init {
my ( $self, $conf ) = @_;
# Remote Procedure are defined in Lemonldap::NG::Common::Session::REST
@ -46,6 +47,7 @@ sub addRoutes {
$self->{multiValuesSeparator} ||= '; ';
$self->{hiddenAttributes} //= "_password";
$self->{TOTPCheck} = $self->{U2FCheck} = $self->{UBKCheck} = '1';
return 1;
}
###################

View File

@ -5,14 +5,15 @@ use 5.10.0;
use utf8;
use Mouse;
extends 'Lemonldap::NG::Common::Conf::RESTServer',
extends 'Lemonldap::NG::Manager::Plugin',
'Lemonldap::NG::Common::Conf::RESTServer',
'Lemonldap::NG::Common::Session::REST';
use Lemonldap::NG::Manager::Api::2F;
use Lemonldap::NG::Manager::Api::Providers::OidcRp;
use Lemonldap::NG::Manager::Api::Providers::SamlSp;
our $VERSION = '2.0.7';
our $VERSION = '2.0.8';
#############################
# I. INITIALIZATION METHODS #
@ -20,7 +21,7 @@ our $VERSION = '2.0.7';
use constant defaultRoute => 'api.html';
sub addRoutes {
sub init {
my ( $self, $conf ) = @_;
# HTML template
@ -148,6 +149,7 @@ sub addRoutes {
$self->{multiValuesSeparator} ||= '; ';
$self->{hiddenAttributes} //= "_password";
$self->{TOTPCheck} = $self->{U2FCheck} = $self->{UBKCheck} = '1';
return 1;
}
1;

View File

@ -60,8 +60,8 @@ sub _hasAllowedAttributes {
sub _listAttributes {
my ( $self, $rootNode ) = @_;
my $mainTree = Lemonldap::NG::Manager::Build::CTrees::cTrees();
my $rootNodes = [ grep { ref($_) eq "HASH" } @{ $mainTree->{$rootNode} } ];
my $mainTree = Lemonldap::NG::Manager::Build::CTrees::cTrees();
my $rootNodes = [ grep { ref($_) eq "HASH" } @{ $mainTree->{$rootNode} } ];
my @attributes = map { $self->_listNodeAttributes($_) } @$rootNodes;
return @attributes;

View File

@ -621,6 +621,14 @@ sub attributes {
'default' => 0,
'type' => 'bool'
},
'bruteForceProtectionIncrementalTempo' => {
'default' => 0,
'type' => 'bool'
},
'bruteForceProtectionLockTimes' => {
'default' => '5 15 60 300 600',
'type' => 'text'
},
'bruteForceProtectionMaxAge' => {
'default' => 300,
'type' => 'int'
@ -629,6 +637,10 @@ sub attributes {
'default' => 3,
'type' => 'int'
},
'bruteForceProtectionMaxLockTime' => {
'default' => 900,
'type' => 'int'
},
'bruteForceProtectionTempo' => {
'default' => 30,
'type' => 'int'
@ -1326,6 +1338,10 @@ qr/^(?:\*\.)?(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.])*(?:[a-zA-Z][
},
'type' => 'keyTextContainer'
},
'groupsBeforeMacros' => {
'default' => 0,
'type' => 'bool'
},
'handlerInternalCache' => {
'default' => 15,
'type' => 'int'
@ -1883,6 +1899,14 @@ qr/^(?:\*\.)?(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.])*(?:[a-zA-Z][
'default' => 'uid reference date title subtitle text check',
'type' => 'text'
},
'notificationsExplorer' => {
'default' => 0,
'type' => 'bool'
},
'notificationsMaxRetrieve' => {
'default' => 3,
'type' => 'int'
},
'notificationStorage' => {
'default' => 'File',
'type' => 'PerlModule'
@ -2422,6 +2446,10 @@ qr/^(?:\*\.)?(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.])*(?:[a-zA-Z][
qr/^(?:(?:(?:(?:(?:[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])[.]?))?$/,
'type' => 'text'
},
'persistentSessionAttributes' => {
'default' => '_loginHistory _2fDevices notification_',
'type' => 'text'
},
'persistentStorage' => {
'type' => 'PerlModule'
},
@ -3631,6 +3659,10 @@ qr/^(?:(?:(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.])*(?:[a-zA-Z][-a-
'default' => 0,
'type' => 'bool'
},
'soapProxyUrn' => {
'default' => 'urn:Lemonldap/NG/Common/PSGI/SOAPService',
'type' => 'text'
},
'soapSessionServer' => {
'default' => 0,
'type' => 'bool'
@ -3652,7 +3684,10 @@ qr/^(?:(?:(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.])*(?:[a-zA-Z][-a-
},
'SSLVarIf' => {
'default' => {},
'type' => 'keyTextContainer'
'keyTest' => sub {
1;
},
'type' => 'keyTextContainer'
},
'staticPrefix' => {
'type' => 'text'

View File

@ -456,15 +456,16 @@ our \$VERSION = '$Lemonldap::NG::Manager::Build::Attributes::VERSION';
use constant HANDLER => 'Lemonldap::NG::Handler::PSGI::Main';
use constant {
EOF
for my $pe ( sort { $portalConstants{$a} <=> $portalConstants{$b} }
keys %portalConstants )
for my $pe (
sort { $portalConstants{$a} <=> $portalConstants{$b} }
keys %portalConstants
)
{
my $str = $portalConstants{$pe};
$content .= " $pe => $str,\n";
}
my $exports = join ", ",
map { "'$_'" }
my $exports = join ", ", map { "'$_'" }
sort { $portalConstants{$a} <=> $portalConstants{$b} }
keys %portalConstants;
@ -569,7 +570,7 @@ sub scanTree {
# Subnode
elsif ( ref($leaf) ) {
$jleaf->{title} = $jleaf->{id} = $leaf->{title};
$jleaf->{type} = $leaf->{form} if ( $leaf->{form} );
$jleaf->{type} = $leaf->{form} if ( $leaf->{form} );
if ( $leaf->{title} =~ /^((?:oidc|saml|cas)Service)MetaData$/ ) {
no strict 'refs';
my @tmp = $self->scanLeaf( $leaf->{nodes} );

View File

@ -358,7 +358,7 @@ sub attributes {
msgFail => '__badUrl__',
},
portalCustomCss => {
type => 'text',
type => 'text',
documentation => 'Path to custom CSS file',
},
portalStatus => {
@ -413,6 +413,11 @@ sub attributes {
type => 'text',
documentation => 'Prefix of static files for HTML templates',
},
groupsBeforeMacros => {
type => 'bool',
default => 0,
documentation => 'Compute groups before macros',
},
multiValuesSeparator => {
type => 'authParamsText',
default => '; ',
@ -788,6 +793,23 @@ sub attributes {
documentation =>
'Brute force attack protection -> Max allowed failed login',
},
bruteForceProtectionMaxLockTime => {
default => 900,
type => 'int',
documentation =>
'Brute force attack protection -> Max lock time',
},
bruteForceProtectionIncrementalTempo => {
default => 0,
help => 'bruteforceprotection.html',
type => 'bool',
documentation => 'Enable incremental lock time for brute force attack protection',
},
bruteForceProtectionLockTimes => {
type => 'text',
default => '5 15 60 300 600',
documentation => 'Incremental lock time values for brute force attack protection',
},
grantSessionRules => {
type => 'grantContainer',
keyTest => sub { return perlExpr(@_) },
@ -800,6 +822,11 @@ sub attributes {
default => '_password _2fDevices',
documentation => 'Name of attributes to hide in logs',
},
persistentSessionAttributes => {
type => 'text',
default => '_loginHistory _2fDevices notification_',
documentation => 'Persistent session attributes to hide',
},
key => {
type => 'password',
documentation => 'Secret key',
@ -1142,6 +1169,16 @@ sub attributes {
type => 'bool',
documentation => 'Notification activation',
},
notificationsExplorer => {
default => 0,
type => 'bool',
documentation => 'Notifications explorer activation',
},
notificationsMaxRetrieve => {
default => 3,
type => 'int',
documentation => 'Max number of displayed notifications',
},
notificationServer => {
default => 0,
type => 'bool',
@ -1985,6 +2022,13 @@ sub attributes {
documentation => 'Enable /portal.wsdl server',
},
# SOAP Procy client
soapProxyUrn => {
default => 'urn:Lemonldap/NG/Common/PSGI/SOAPService',
type => 'text',
documentation => 'SOAP URN for Proxy',
},
# AutoSignin
autoSigninRules => {
type => 'keyTextContainer',
@ -3159,6 +3203,7 @@ m{^(?:ldapi://[^/]*/?|\w[\w\-\.]*(?::\d{1,5})?|ldap(?:s|\+tls)?://\w[\w\-\.]*(?:
},
SSLVarIf => {
type => 'keyTextContainer',
keyTest => sub { 1 },
default => {}
},
sslByAjax => {

View File

@ -584,7 +584,7 @@ sub tree {
{
title => 'reloadParams',
help => 'configlocation.html#configuration_reload',
nodes => [ 'reloadUrls', 'reloadTimeout', 'compactConf' ]
nodes => [ 'reloadTimeout', 'compactConf', 'reloadUrls' ]
},
{
title => 'plugins',
@ -620,11 +620,12 @@ sub tree {
help => 'notifications.html',
nodes => [
'notification',
'notificationsExplorer',
'notificationWildcard',
'oldNotifFormat',
'notificationXSLTfile',
'notificationStorage',
'notificationStorageOptions',
'notificationWildcard',
'notificationXSLTfile',
{
title => 'serverNotification',
help => 'notifications.html#server',
@ -818,6 +819,25 @@ sub tree {
'u2fLogo',
]
},
{
title => 'yubikey2f',
help => 'yubikey2f.html',
form => 'simpleInputContainer',
nodes => [
'yubikey2fActivation',
'yubikey2fSelfRegistration',
'yubikey2fClientID',
'yubikey2fSecretKey',
'yubikey2fNonce',
'yubikey2fUrl',
'yubikey2fPublicIDSize',
'yubikey2fUserCanRemoveKey',
'yubikey2fTTL',
'yubikey2fAuthnLevel',
'yubikey2fLabel',
'yubikey2fLogo',
],
},
{
title => 'mail2f',
help => 'mail2f.html',
@ -865,25 +885,6 @@ sub tree {
'rest2fLabel', 'rest2fLogo',
]
},
{
title => 'yubikey2f',
help => 'yubikey2f.html',
form => 'simpleInputContainer',
nodes => [
'yubikey2fActivation',
'yubikey2fSelfRegistration',
'yubikey2fClientID',
'yubikey2fSecretKey',
'yubikey2fNonce',
'yubikey2fUrl',
'yubikey2fPublicIDSize',
'yubikey2fUserCanRemoveKey',
'yubikey2fTTL',
'yubikey2fAuthnLevel',
'yubikey2fLabel',
'yubikey2fLogo',
],
},
'sfExtra',
{
title => 'sfRemovedNotification',
@ -904,6 +905,7 @@ sub tree {
nodes => [
'customFunctions',
'multiValuesSeparator',
'groupsBeforeMacros',
{
title => 'SMTP',
help => 'smtp.html',
@ -937,12 +939,21 @@ sub tree {
'trustedDomains',
'useSafeJail',
'checkXSS',
'bruteForceProtection',
'requireToken',
'formTimeout',
'tokenUseGlobalStorage',
{
title => 'bruteForceAttackProtection',
help => 'bruteforceprotection.html',
form => 'simpleInputContainer',
nodes => [
'bruteForceProtection',
'bruteForceProtectionIncrementalTempo',
]
},
'lwpOpts',
'lwpSslOpts',
{
title => 'contentSecurityPolicy',
help => 'security.html#portal',

View File

@ -26,6 +26,7 @@ has req => ( is => 'ro' );
has sep => ( is => 'rw', isa => 'Str', default => '/' );
has format => ( is => 'rw', isa => 'Str', default => "%-25s | %-25s | %-25s" );
has yes => ( is => 'rw', isa => 'Bool', default => 0 );
has safe => ( is => 'rw', isa => 'Bool', default => 0 );
has force => ( is => 'rw', isa => 'Bool', default => 0 );
has logger => ( is => 'ro', lazy => 1, builder => sub { $_[0]->mgr->logger } );
has userLogger =>
@ -75,7 +76,7 @@ sub set {
}
}
require Clone;
my $new = Clone::clone( $self->mgr->currentConf );
my $new = Clone::clone( $self->mgr->hLoadedPlugins->{conf}->currentConf );
foreach my $key ( keys %pairs ) {
$self->_setKey( $new, $key, $pairs{$key} );
}
@ -100,7 +101,7 @@ sub addKey {
push @list, [ $root, $newKey, $value ];
}
require Clone;
my $new = Clone::clone( $self->mgr->currentConf );
my $new = Clone::clone( $self->mgr->hLoadedPlugins->{conf}->currentConf );
foreach my $el (@list) {
my @path = split $sep, $el->[0];
if ( $#path == 0 ) {
@ -141,7 +142,7 @@ sub delKey {
push @list, [ $root, $key ];
}
require Clone;
my $new = Clone::clone( $self->mgr->currentConf );
my $new = Clone::clone( $self->mgr->hLoadedPlugins->{conf}->currentConf );
foreach my $el (@list) {
my @path = split $sep, $el->[0];
if ( $#path == 0 ) {
@ -199,7 +200,7 @@ sub lastCfg {
sub save {
my ($self) = @_;
my $conf = $self->jsonResponse( '/confs/latest', 'full=1' );
my $conf = $self->jsonResponse( '/confs/' . $self->cfgNum, 'full=1' );
my $json = JSON->new->indent->canonical;
print $json->encode($conf);
}
@ -228,6 +229,33 @@ sub restore {
print STDERR Dumper($res);
}
sub rollback {
my ($self) = @_;
my $lastCfg = $self->mgr->confAcc->lastCfg;
my $previousCfg = $lastCfg - 1;
my $conf =
$self->mgr->confAcc->getConf( { cfgNum => $previousCfg, raw => 1 } )
or die $Lemonldap::NG::Common::Conf::msg;
$conf->{cfgNum} = $lastCfg;
$conf->{cfgAuthor} = scalar( getpwuid $< ) . '(command-line-interface)';
chomp $conf->{cfgAuthor};
$conf->{cfgAuthorIP} = '127.0.0.1';
$conf->{cfgDate} = time;
$conf->{cfgVersion} = $Lemonldap::NG::Manager::VERSION;
$conf->{cfgLog} = $self->log // "Rolled back configuration $lastCfg";
my $s = $self->mgr->confAcc->saveConf($conf);
if ( $s > 0 ) {
$self->logger->info("CLI: Configuration $lastCfg has been rolled back");
print STDERR "Configuration $lastCfg has been rolled back\n";
}
else {
$self->logger->error("CLI: Failed to rollback configuration $lastCfg");
print STDERR "Failed to rollback configuration $lastCfg\n";
}
}
sub _getKey {
my ( $self, $key ) = @_;
my $sep = $self->sep;
@ -236,7 +264,8 @@ sub _getKey {
warn "Malformed key $base";
return ();
}
my $value = $self->mgr->getConfKey( $self->req, $base, noCache => 1 );
my $value = $self->mgr->hLoadedPlugins->{conf}
->getConfKey( $self->req, $base, noCache => 1 );
if ( $self->req->error ) {
die $self->req->error;
}
@ -272,14 +301,19 @@ sub _save {
require Lemonldap::NG::Manager::Conf::Parser;
my $parser = Lemonldap::NG::Manager::Conf::Parser->new( {
newConf => $new,
refConf => $self->mgr->currentConf,
refConf => $self->mgr->hLoadedPlugins->{conf}->currentConf,
req => $self->req
}
);
unless ( $parser->testNewConf( $self->localConf ) ) {
$self->logger->error(
"CLI: Configuration rejected with message: $parser->{message}");
printf STDERR "Modifications rejected: %s:\n", $parser->{message};
my $msg = "Configuration rejected with message: " . $parser->message;
$self->logger->error("CLI: $msg");
if ( $self->safe ) {
die "$msg";
}
else {
print STDERR "$msg\n";
}
}
my $saveParams = { force => $self->force };
if ( $self->force and $self->cfgNum ) {
@ -304,11 +338,13 @@ sub _save {
"CLI: Configuration $s has been saved by $new->{cfgAuthor}");
$self->logger->info("CLI: Configuration $s saved");
print STDERR "Saved under number $s\n";
$parser->{status} = [ $self->mgr->applyConf($new) ];
$parser->{status} =
[ $self->mgr->hLoadedPlugins->{conf}->applyConf($new) ];
}
else {
$self->logger->error("CLI: Configuration not saved!");
printf STDERR "Modifications rejected: %s:\n", $parser->{message};
printf STDERR "Modifications rejected: %s:\n", $parser->{message}
if $parser->{message};
print STDERR Dumper($parser);
}
foreach (qw(errors warnings status)) {
@ -346,7 +382,7 @@ sub run {
}
$self->cfgNum( $self->lastCfg ) unless ( $self->cfgNum );
my $action = shift;
unless ( $action =~ /^(?:get|set|addKey|delKey|save|restore)$/ ) {
unless ( $action =~ /^(?:get|set|addKey|delKey|save|restore|rollback)$/ ) {
die "Unknown action $action. Only get, set, addKey or delKey allowed";
}

View File

@ -17,7 +17,8 @@ use URI::URL;
use feature 'state';
extends 'Lemonldap::NG::Common::Conf::RESTServer';
extends 'Lemonldap::NG::Manager::Plugin',
'Lemonldap::NG::Common::Conf::RESTServer';
our $VERSION = '2.1.0';
@ -27,36 +28,11 @@ our $VERSION = '2.1.0';
use constant defaultRoute => 'manager.html';
has ua => ( is => 'rw' );
has diffRule => ( is => 'rw', default => sub { 0 } );
has brwRule => ( is => 'rw', default => sub { 0 } );
has ua => ( is => 'rw' );
sub addRoutes {
sub init {
my ( $self, $conf ) = @_;
$self->ua( Lemonldap::NG::Common::UserAgent->new($conf) );
my $hd = "Lemonldap::NG::Handler::PSGI::Main";
# Parse Diff activation rule
$self->logger->debug(
"Diff activation rule -> " . ( $self->{viewerAllowDiff} // 0 ) );
my $rule = $hd->buildSub( $hd->substitute( $self->{viewerAllowDiff} ) );
unless ($rule) {
$self->error(
"Bad Diff activation rule -> " . $hd->tsv->{jail}->error );
return 0;
}
$self->diffRule($rule);
# Parse Browser activation rule
$self->logger->debug(
"Browser activation rule -> " . ( $self->{viewerAllowBrowser} // 0 ) );
$rule = $hd->buildSub( $hd->substitute( $self->{viewerAllowBrowser} ) );
unless ($rule) {
$self->error(
"Bad Browser activation rule -> " . $hd->tsv->{jail}->error );
return 0;
}
$self->brwRule($rule);
# HTML template
$self->addRoute( 'manager.html', undef, ['GET'] )
@ -95,6 +71,7 @@ sub addRoutes {
# Url loader
->addRoute( 'prx', undef, ['POST'] );
return 1;
}
# 35 - New RSA key pair on demand
@ -268,7 +245,7 @@ sub newConf {
}
}
if ( $res->{result} ) {
if ( $self->{demoMode} ) {
if ( $self->p->{demoMode} ) {
$res->{message} = '__demoModeOn__';
}
else {
@ -279,7 +256,7 @@ sub newConf {
unless ( @{ $parser->{needConfirmation} } && !$args{force} );
if ( $s > 0 ) {
$self->userLogger->notice(
'User ' . $self->userId($req) . " has stored conf $s" );
'User ' . $self->p->userId($req) . " has stored conf $s" );
$res->{result} = 1;
$res->{cfgNum} = $s;
if ( my $status = $self->applyConf( $parser->newConf ) ) {
@ -291,7 +268,7 @@ sub newConf {
else {
$self->userLogger->notice(
'Saving attempt rejected, asking for confirmation to '
. $self->userId($req) );
. $self->p->userId($req) );
$res->{result} = 0;
if ( $s == CONFIG_WAS_CHANGED ) {
$res->{needConfirm} = 1;
@ -324,7 +301,7 @@ sub newRawConf {
}
my $res = {};
if ( $self->{demoMode} ) {
if ( $self->p->{demoMode} ) {
$res->{message} = '__demoModeOn__';
}
else {
@ -332,15 +309,16 @@ sub newRawConf {
# chances to be equal to last config cfgNum
my $s = $self->confAcc->saveConf( $new, force => 1 );
if ( $s > 0 ) {
$self->userLogger->notice(
'User ' . $self->userId($req) . " has stored (raw) conf $s" );
$self->userLogger->notice( 'User '
. $self->p->userId($req)
. " has stored (raw) conf $s" );
$res->{result} = 1;
$res->{cfgNum} = $s;
}
else {
$self->userLogger->notice(
'Raw saving attempt rejected, asking for confirmation to '
. $self->userId($req) );
. $self->p->userId($req) );
$res->{result} = 0;
$res->{needConfirm} = 1 if ( $s == CONFIG_WAS_CHANGED );
$res->{message} .= '__needConfirmation__';
@ -359,7 +337,7 @@ sub applyConf {
my $status;
# 1 Apply conf locally
$self->api->checkConf();
$self->p->api->checkConf();
# Get apply section values
my %reloadUrls =

View File

@ -51,7 +51,10 @@ has changes => ( is => 'rw', isa => 'ArrayRef', default => sub { return [] } );
has message => (
is => 'rw',
isa => 'Str',
default => '',
lazy => 1,
default => sub {
return join( ', ', map { $_->{message} } @{ $_[0]->errors } );
},
trigger => sub {
hdebug( "Message becomes " . $_[0]->{message} );
}
@ -1111,7 +1114,8 @@ sub _unitTest {
my $res = 1;
foreach my $key ( keys %$conf ) {
if ( $localConf->{skippedUnitTests}
if ( $localConf
and $localConf->{skippedUnitTests}
and $localConf->{skippedUnitTests} =~ /\b$key\b/ )
{
$localConf->logger->debug("-> Ignore test for $key\n");
@ -1121,7 +1125,8 @@ sub _unitTest {
my $attr = $attrs->{$key};
my $type = $types->{ $attr->{type} } if $attr;
unless ( $type or $attr->{test} ) {
$localConf->logger->debug("Unknown attribute $key, deleting it\n");
$localConf->logger->debug("Unknown attribute $key, deleting it\n")
if $localConf;
delete $conf->{$key};
next;
}
@ -1156,8 +1161,6 @@ sub _unitTest {
if ( $key =~ /^(?:$simpleHashKeys|$doubleHashKeys)$/o
or $attr->{type} =~ /Container$/ )
{
my $keyMsg = $attr->{keyMsgFail} // $type->{keyMsgFail};
my $msg = $attr->{msgFail} // $type->{msgFail};
$res = 0
unless (
$self->_execTest( {
@ -1244,8 +1247,7 @@ sub _execTest {
#
#@return true if tests succeed
sub _globalTest {
my $self = shift;
my $localConf = shift;
my ( $self, $localConf ) = @_;
require Lemonldap::NG::Manager::Conf::Tests;
hdebug('# _globalTest()');
@ -1253,10 +1255,12 @@ sub _globalTest {
my $tests = &Lemonldap::NG::Manager::Conf::Tests::tests( $self->newConf );
foreach my $name ( keys %$tests ) {
if ( $localConf->{skippedGlobalTests}
if ( $localConf
and $localConf->{skippedGlobalTests}
and $localConf->{skippedGlobalTests} =~ /\b$name\b/ )
{
$localConf->logger->debug("-> Ignore test for $name\n");
$localConf->logger->debug("-> Ignore test for $name\n")
if $localConf;
next;
}
my $sub = $tests->{$name};
@ -1278,7 +1282,7 @@ sub _globalTest {
};
if ($@) {
push @{ $self->warnings }, "Test $name failed: $@";
$localConf->logger->debug("Test $name failed: $@\n");
$localConf->logger->debug("Test $name failed: $@\n") if $localConf;
}
}
return $result;

View File

@ -607,11 +607,11 @@ sub tests {
# Warn if issuers token TTL is higher than 30s
issuersTimeout => sub {
return 1 unless ( defined $conf->{issuerTimeout} );
return 1 unless ( defined $conf->{issuersTimeout} );
return ( 0, "Issuers token TTL must be higher than 30s" )
unless ( $conf->{issuerTimeout} > 30 );
unless ( $conf->{issuersTimeout} > 30 );
return ( 1, "Issuers token TTL should not be higher than 2mn" )
if ( $conf->{issuerTimeout} > 120 );
if ( $conf->{issuersTimeout} > 120 );
# Return
return 1;
@ -755,6 +755,27 @@ sub tests {
return ( $res, join( ', ', @msg ) );
},
# RS* OIDC algs require a signing key
oidcRPNeedRSAKey => sub {
return 1
unless ( $conf->{oidcRPMetaDataOptions}
and %{ $conf->{oidcRPMetaDataOptions} } );
my @usingRSA = grep {
$conf->{oidcRPMetaDataOptions}->{$_}
->{oidcRPMetaDataOptionsIDTokenSignAlg}
and $conf->{oidcRPMetaDataOptions}->{$_}
->{oidcRPMetaDataOptionsIDTokenSignAlg} =~ /^RS/
} keys %{ $conf->{oidcRPMetaDataOptions} };
if ( @usingRSA and not $conf->{oidcServicePrivateKeySig} ) {
my $msg =
join( ", ", @usingRSA )
. ": using RS-type encryption, but no RSA key is defined in global OIDC configuration";
return ( 0, $msg );
}
return 1;
},
};
}

View File

@ -13,7 +13,8 @@ require Lemonldap::NG::Common::Notifications;
use feature 'state';
extends 'Lemonldap::NG::Common::Conf::AccessLib';
extends 'Lemonldap::NG::Manager::Plugin',
'Lemonldap::NG::Common::Conf::AccessLib';
our $VERSION = '2.1.0';
@ -26,7 +27,7 @@ has notifFormat => ( is => 'rw' );
use constant defaultRoute => 'notifications.html';
sub addRoutes {
sub init {
my ( $self, $conf ) = @_;
if ( $conf->{oldNotifFormat} ) {
@ -74,6 +75,7 @@ sub addRoutes {
{ done => { ':notificationId' => 'deleteDoneNotification' } },
['DELETE']
);
return 1;
}
sub setNotifAccess {

View File

@ -0,0 +1,51 @@
package Lemonldap::NG::Manager::Plugin;
use strict;
use Mouse;
our $VERSION = '2.0.8';
extends 'Lemonldap::NG::Common::Module';
has _confAcc => (
is => 'rw',
lazy => 1,
default => sub { return $_[0]->p->{_confAcc} },
);
sub sendError {
my $self = shift;
return $self->p->sendError(@_);
}
sub sendJSONresponse {
my $self = shift;
return $self->p->sendJSONresponse(@_);
}
sub addRoute {
my ( $self, $word, $subName, $methods, $transform ) = @_;
$transform //= sub {
my ($sub) = @_;
if ( ref $sub ) {
return sub {
shift;
return $sub->( $self, @_ );
}
}
else {
return sub {
shift;
return $self->$sub(@_);
}
}
};
$self->p->addRoute( $word, $subName, $methods, $transform );
return $self;
}
sub loadTemplate {
my $self = shift;
return $self->p->loadTemplate(@_);
}
1;

View File

@ -14,7 +14,8 @@ use Lemonldap::NG::Common::IPv6;
use feature 'state';
extends 'Lemonldap::NG::Common::Conf::AccessLib',
extends 'Lemonldap::NG::Manager::Plugin',
'Lemonldap::NG::Common::Conf::AccessLib',
'Lemonldap::NG::Common::Session::REST';
our $VERSION = '2.1.0';
@ -25,7 +26,7 @@ our $VERSION = '2.1.0';
use constant defaultRoute => 'sessions.html';
sub addRoutes {
sub init {
my ( $self, $conf ) = @_;
# HTML template
@ -55,6 +56,7 @@ sub addRoutes {
$self->{multiValuesSeparator} ||= '; ';
$self->{impersonationPrefix} = $conf->{impersonationPrefix} || 'real_';
$self->{hiddenAttributes} //= "_password";
return 1;
}
#######################
@ -98,7 +100,7 @@ sub sessions {
or return $self->sendError( $req, undef, 400 );
my $params = $req->parameters();
my $type = delete $params->{sessionType};
$type = $type eq 'global' ? 'SSO' : ucfirst($type);
$type = $type eq 'global' ? 'SSO' : ucfirst($type);
$type = $type eq 'Offline' ? 'OIDCI' : ucfirst($type);
my $res;

View File

@ -11,6 +11,9 @@ use feature 'state';
extends 'Lemonldap::NG::Manager::Conf';
has diffRule => ( is => 'rw', default => sub { 0 } );
has brwRule => ( is => 'rw', default => sub { 0 } );
our $VERSION = '2.1.0';
#############################
@ -21,11 +24,36 @@ use constant defaultRoute => 'viewer.html';
has ua => ( is => 'rw' );
sub addRoutes {
sub init {
my ( $self, $conf ) = @_;
$self->ua( Lemonldap::NG::Common::UserAgent->new($conf) );
my $hd = "Lemonldap::NG::Handler::PSGI::Main";
my $hiddenKeys = $self->{viewerHiddenKeys} || '';
# Parse Diff activation rule
$conf->{viewerAllowDiff} //= 0;
$self->logger->debug(
"Diff activation rule -> " . ( $conf->{viewerAllowDiff} ) );
my $rule = $hd->buildSub( $hd->substitute( $conf->{viewerAllowDiff} ) );
unless ($rule) {
$self->logger->error(
"Bad Diff activation rule -> " . $hd->tsv->{jail}->error );
return 0;
}
$self->diffRule($rule);
# Parse Browser activation rule
$conf->{viewerAllowBrowser} //= 0;
$self->logger->debug(
"Browser activation rule -> " . ( $conf->{viewerAllowBrowser} ) );
$rule = $hd->buildSub( $hd->substitute( $conf->{viewerAllowBrowser} ) );
unless ($rule) {
$self->logger->error(
"Bad Browser activation rule -> " . $hd->tsv->{jail}->error );
return 0;
}
$self->brwRule($rule);
my $hiddenKeys = $conf->{viewerHiddenKeys} || '';
my @enabledKeys = ();
my @keys = qw(virtualHosts samlIDPMetaDataNodes samlSPMetaDataNodes
applicationList oidcOPMetaDataNodes oidcRPMetaDataNodes
@ -67,6 +95,7 @@ sub addRoutes {
# Other keys
->addRoute( view => { ':cfgNum' => { '*' => 'viewKey' } }, ['GET'] );
return 1;
}
sub getConfByNum {
@ -80,7 +109,8 @@ sub viewDiff {
# Check Diff activation rule
unless ( $self->diffRule->( $req, $req->{userData} ) ) {
my $user = $req->{userData}->{_whatToTrace} || 'anonymous';
$self->userLogger->warn("$user is not authorized to compare configurations");
$self->userLogger->warn(
"$user is not authorized to compare configurations");
return $self->sendJSONresponse( $req, { 'value' => '_Hidden_' } );
}
@ -144,7 +174,8 @@ sub viewKey {
$self->logger->debug(
" $req->{env}->{REQUEST_URI} -> URI FORBIDDEN");
my $user = $req->{userData}->{_whatToTrace} || 'anonymous';
$self->userLogger->warn("$user is not authorized to browse configurations");
$self->userLogger->warn(
"$user is not authorized to browse configurations");
$self->rejectKey( $req, @args );
}
}

View File

@ -63,18 +63,17 @@ if (`diff $refFile $editFile`) {
# Update author and date
$VAR1->{cfgAuthor} = $ENV{SUDO_USER} || $ENV{LOGNAME} || "lmConfigEditor";
$VAR1->{cfgAuthorIP} = $ENV{SSH_CONNECTION} || "localhost";
$VAR1->{cfgDate} = time();
$VAR1->{cfgDate} = time();
$VAR1->{cfgLog} ||= 'Edited by lmConfigEditor';
# Test new configuration
my $parser = Lemonldap::NG::Manager::Conf::Parser->new(
{
my $parser = Lemonldap::NG::Manager::Conf::Parser->new( {
refConf => $refConf,
newConf => $VAR1,
req => 1,
}
);
unless ( $parser->testNewConf ) {
unless ( $parser->testNewConf(undef) ) {
print STDERR "Configuration seems to have some errors:\n ";
print STDERR Dumper(
{ errors => $parser->errors, warnings => $parser->warnings } );

View File

@ -13,7 +13,7 @@ displayError = (j, status, err) ->
console.log 'Error', err
res = JSON.parse j.responseText
if res and res.error
res = res.error.replace /.* /, ''
res = res.error.replace(/.* /, '')
console.log 'Returned error', res
setMsg res, 'warning'

View File

@ -193,6 +193,14 @@ llapp.controller 'NotificationsExplorerCtrl', [ '$scope', '$translator', '$locat
, (resp) ->
$scope.waiting = false
# Highlight current selection
console.log "Selection", $scope.type
$scope.activesStyle = {color: '#777'}
$scope.doneStyle = {color: '#777'}
$scope.newStyle = {color: '#777'}
$scope.activesStyle = {color: '#333'} if $scope.type == 'actives'
$scope.doneStyle = {color: '#333'} if $scope.type == 'done'
$scope.displayNotification = (scope) ->
$scope.waiting = true
$scope.currentScope = scope
@ -206,11 +214,16 @@ llapp.controller 'NotificationsExplorerCtrl', [ '$scope', '$translator', '$locat
reference: node.reference
condition: node.condition
if $scope.type == 'actives'
notif = JSON.parse response.data.notifications
$scope.currentNotification.text = notif.text
$scope.currentNotification.title = notif.title
$scope.currentNotification.subtitle = notif.subtitle
$scope.currentNotification.notifications = response.data.notifications
try
console.log "Try to parse a JSON formated notification..."
notif = JSON.parse response.data.notifications
$scope.currentNotification.date = $scope.notifDate(notif.date)
$scope.currentNotification.text = notif.text
$scope.currentNotification.title = notif.title
$scope.currentNotification.subtitle = notif.subtitle
catch e
console.log "Unable to parse JSON"
$scope.currentNotification.notifications = response.data.notifications
else
$scope.currentNotification.done = response.data.done
$scope.waiting = false
@ -266,16 +279,19 @@ llapp.controller 'NotificationsExplorerCtrl', [ '$scope', '$translator', '$locat
message: data.error
$scope.showModal "alert.html"
$scope.waiting = false
$scope.form.date = new Date()
, (response) ->
$scope.message =
title: 'notificationNotCreated'
message: response.statusText
$scope.showModal "alert.html"
$scope.waiting = false
$scope.form.date = new Date()
else
$scope.message =
title: 'incompleteForm'
$scope.showModal "alert.html"
$scope.form.date = new Date()
$scope.init = ->
$scope.waiting = true
@ -297,6 +313,9 @@ llapp.controller 'NotificationsExplorerCtrl', [ '$scope', '$translator', '$locat
$scope.myStyle = {color: '#ffb84d'}
$scope.displayCreateForm = ->
$scope.activesStyle = {color: '#777'}
$scope.doneStyle = {color: '#777'}
$scope.newStyle = {color: '#333'}
$scope.waiting = true
$translator.init($scope.lang).then ->
$scope.currentNotification = null

View File

@ -492,6 +492,15 @@ llapp.controller 'SessionsExplorerCtrl', ['$scope', '$translator', '$location',
, (resp) ->
$scope.waiting = false
# Highlight current selection
console.log "Selection", sessionType
$scope.navssoStyle = {color: '#777'}
$scope.offlineStyle = {color: '#777'}
$scope.persistentStyle = {color: '#777'}
$scope.navssoStyle = {color: '#333'} if sessionType == 'global'
$scope.offlineStyle = {color: '#333'} if sessionType == 'offline'
$scope.persistentStyle = {color: '#333'} if sessionType == 'persistent'
# Intialization function
# Simply set $scope.waiting to false during $translator and tree root
# initialization

View File

@ -86,7 +86,7 @@
</div>
<div class="modal-body">
<div class="row text-center">
<div class="col-md-2" ng-repeat="i in ['attach.png', 'bell.png', 'bookmark.png', 'configure.png', 'database.png', 'demo.png', 'docs.png', 'folder.png', 'gear.png', 'help.png', 'llng.png', 'mailappt.png', 'money.png', 'network.png', 'terminal.png', 'thumbnail.png','tools.png', 'tux.png', 'web.png', 'wheels.png']">
<div class="col-md-2" ng-repeat="i in ['attach.png', 'bell.png', 'bookmark.png', 'configure.png', 'database.png', 'demo.png', 'folder.png', 'gear.png', 'help.png', 'llng.png', 'mailappt.png', 'money.png', 'terminal.png', 'thumbnail.png', 'tux.png', 'web.png']">
<button class="btn llcontainer" ng-class="{'btn-default':currentNode.data.logo!=i,'btn-info':currentNode.data.logo==i}" ng-click="ok(currentNode.data.logo=i)">
<img ng-src="{{elem('portal').data}}static/common/apps/{{i}}" title="{{i}}" alt="{{i}}" />
</button>

View File

@ -1,4 +1,4 @@
// Generated by CoffeeScript 1.12.7
// Generated by CoffeeScript 1.12.8
/*
* 2ndFA Session explorer

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -195,7 +195,7 @@
over = 0;
}
if ($scope.type === 'done' || $scope.type === 'actives') {
return $http.get(scriptname + "notifications/" + $scope.type + "?" + query).then(function(response) {
$http.get(scriptname + "notifications/" + $scope.type + "?" + query).then(function(response) {
var data, i, len, n, ref;
data = response.data;
if (data.result) {
@ -221,6 +221,26 @@
return $scope.waiting = false;
});
}
console.log("Selection", $scope.type);
$scope.activesStyle = {
color: '#777'
};
$scope.doneStyle = {
color: '#777'
};
$scope.newStyle = {
color: '#777'
};
if ($scope.type === 'actives') {
$scope.activesStyle = {
color: '#333'
};
}
if ($scope.type === 'done') {
return $scope.doneStyle = {
color: '#333'
};
}
};
$scope.displayNotification = function(scope) {
var node, notificationId;
@ -232,18 +252,25 @@
notificationId = node.uid + "_" + node.reference;
}
$http.get(scriptname + "notifications/" + $scope.type + "/" + notificationId).then(function(response) {
var notif;
var e, notif;
$scope.currentNotification = {
uid: node.uid,
reference: node.reference,
condition: node.condition
};
if ($scope.type === 'actives') {
notif = JSON.parse(response.data.notifications);
$scope.currentNotification.text = notif.text;
$scope.currentNotification.title = notif.title;
$scope.currentNotification.subtitle = notif.subtitle;
$scope.currentNotification.notifications = response.data.notifications;
try {
console.log("Try to parse a JSON formated notification...");
notif = JSON.parse(response.data.notifications);
$scope.currentNotification.date = $scope.notifDate(notif.date);
$scope.currentNotification.text = notif.text;
$scope.currentNotification.title = notif.title;
$scope.currentNotification.subtitle = notif.subtitle;
} catch (error) {
e = error;
console.log("Unable to parse JSON");
$scope.currentNotification.notifications = response.data.notifications;
}
} else {
$scope.currentNotification.done = response.data.done;
}
@ -300,7 +327,7 @@
$scope.formPost.reference = $scope.form.reference;
$scope.formPost.condition = $scope.form.condition;
$scope.formPost.xml = $scope.form.xml;
return $http.post('notifications/actives', $scope.formPost).then(function(response) {
$http.post('notifications/actives', $scope.formPost).then(function(response) {
var data;
data = response.data;
$scope.form = {};
@ -315,21 +342,24 @@
};
}
$scope.showModal("alert.html");
return $scope.waiting = false;
$scope.waiting = false;
return $scope.form.date = new Date();
}, function(response) {
$scope.message = {
title: 'notificationNotCreated',
message: response.statusText
};
$scope.showModal("alert.html");
return $scope.waiting = false;
$scope.waiting = false;
return $scope.form.date = new Date();
});
} else {
$scope.message = {
title: 'incompleteForm'
};
return $scope.showModal("alert.html");
$scope.showModal("alert.html");
}
return $scope.form.date = new Date();
};
$scope.init = function() {
$scope.waiting = true;
@ -349,6 +379,15 @@
};
};
$scope.displayCreateForm = function() {
$scope.activesStyle = {
color: '#777'
};
$scope.doneStyle = {
color: '#777'
};
$scope.newStyle = {
color: '#333'
};
$scope.waiting = true;
return $translator.init($scope.lang).then(function() {
$scope.currentNotification = null;

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -537,7 +537,7 @@
} else {
over = 0;
}
return $http.get(scriptname + "sessions/" + sessionType + "?" + query).then(function(response) {
$http.get(scriptname + "sessions/" + sessionType + "?" + query).then(function(response) {
var data, i, len, n, ref;
data = response.data;
if (data.result) {
@ -565,6 +565,31 @@
}, function(resp) {
return $scope.waiting = false;
});
console.log("Selection", sessionType);
$scope.navssoStyle = {
color: '#777'
};
$scope.offlineStyle = {
color: '#777'
};
$scope.persistentStyle = {
color: '#777'
};
if (sessionType === 'global') {
$scope.navssoStyle = {
color: '#333'
};
}
if (sessionType === 'offline') {
$scope.offlineStyle = {
color: '#333'
};
}
if (sessionType === 'persistent') {
return $scope.persistentStyle = {
color: '#333'
};
}
};
$scope.init = function() {
$scope.waiting = true;

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -104,7 +104,9 @@
"browserIdSiteName":"اسم الموقع",
"browserIdVerificationURL":"التحقق في اليو آر إل",
"browseTree":"تصفح الهيكل",
"bruteForceProtection":"Brute-force attack protection",
"bruteForceProtection":"Activation",
"bruteForceAttackProtection":"Brute-force attack protection",
"bruteForceProtectionIncrementalTempo":"Incremental lock times",
"cancel":"إلغاء",
"captcha_login_enabled":"التفعيل في استمارة تسجيل الدخول",
"captcha_mail_enabled":"التفعيل في إعادة تعيين كلمة المرور بواسطة استمارة البريد",
@ -142,7 +144,7 @@
"casStorageOptions":" خيارات وحدة جلسات كاس",
"categoryName":"اسم الفئة",
"cda":"نطاقات متعددة",
"certificateMailContent":"Certificate mail content",
"certificateMailContent":"Mail content",
"certificateResetByMailManagement":"Certificate management",
"certificateResetByMailURL":"Reset page URL",
"certificateResetByMailCeaAttribute":"Certificate attibute name",
@ -175,7 +177,7 @@
"corsAllow_Origin":"Access-Control-Allow-Origin",
"corsExpose_Headers":"Access-Control-Expose-Headers",
"corsMax_Age":"Access-Control-Max-Age",
"cfgLog":"استئنف",
"cfgLog":"Summary",
"cfgVersion":"عملية ضبط الإصدارات",
"checkXSS":"تحقق من هجمات XSS",
"clickHereToForce":"انقر هنا لإجبار",
@ -326,6 +328,7 @@
"gpgParams":"GPG parameters",
"grantSessionRules":"ظروف الافتتاح",
"groups":"المجموعات",
"groupsBeforeMacros":"Compute groups before macros",
"hashkey":"المفتاح",
"headers":"هيدر إتش تي تي بي ",
"hGroups":"المجموعات (هاش ريف)",
@ -459,7 +462,7 @@
"mailCharset":"charset",
"mailConfirmBody":"تأكيد محتوى البريد",
"mailConfirmSubject":"تأكيد عنوان بريد",
"mailContent":"Password mail content",
"mailContent":"Mail content",
"mailFrom":"مرسل البريد",
"mailHeaders":"هيدر البريد",
"mailLDAPFilter":"فلتر البريد",
@ -526,6 +529,7 @@
"notificationDeleted":"تم حذف الإشعار",
"notificationDone":"انتهى الإشعار",
"notificationsDone":"انتهت الإشعارات",
"notificationsExplorer":"Explorer",
"notificationNotCreated":"لم يتم إنشاء الإشعار",
"notificationNotDeleted":"لم يتم وضع علامة على الإشعار بأنه تم الاطلاع عليه",
"notificationNotFound":"لم يتم العثور على الإشعار",
@ -801,7 +805,7 @@
"saveReport":"احفظ التقرير",
"savingConfirmation":"حفظ التأكيد",
"scope":"نطاق",
"search":"Search ...",
"search":"Search...",
"secondFactors":"Second factors",
"securedCookie":"ملفات تعريف الارتباط المضمونة (سسل)",
"security":"الحماية",
@ -1111,4 +1115,4 @@
"samlRelayStateTimeout":"تناوب حالة مهلة الجلسة ",
"samlUseQueryStringSpecific":"استخدام أسلوب query_string المعين",
"samlOverrideIDPEntityID":"Override Entity ID when acting as IDP"
}
}

View File

@ -104,7 +104,9 @@
"browserIdSiteName":"Site name",
"browserIdVerificationURL":"Verification URL",
"browseTree":"Browse tree",
"bruteForceProtection":"Brute-force attack protection",
"bruteForceProtection":"Activation",
"bruteForceAttackProtection":"Brute-force attack protection",
"bruteForceProtectionIncrementalTempo":"Incremental lock times",
"cancel":"Abbrechen",
"captcha_login_enabled":"Activation in login form",
"captcha_mail_enabled":"Activation in password reset by mail form",
@ -142,7 +144,7 @@
"casStorageOptions":"CAS sessions module options",
"categoryName":"Category name",
"cda":"Mehrere Domains",
"certificateMailContent":"Certificate mail content",
"certificateMailContent":"Mail content",
"certificateResetByMailManagement":"Certificate management",
"certificateResetByMailURL":"Reset page URL",
"certificateResetByMailCeaAttribute":"Certificate CEA attibute",
@ -175,7 +177,7 @@
"corsAllow_Origin":"Access-Control-Allow-Origin",
"corsExpose_Headers":"Access-Control-Expose-Headers",
"corsMax_Age":"Access-Control-Max-Age",
"cfgLog":"Resume",
"cfgLog":"Summary",
"cfgVersion":"Configuration version",
"checkXSS":"Check XSS attacks",
"clickHereToForce":"Click here to force",
@ -326,6 +328,7 @@
"gpgParams":"GPG parameters",
"grantSessionRules":"Opening conditions",
"groups":"Groups",
"groupsBeforeMacros":"Compute groups before macros",
"hashkey":"Key",
"headers":"HTTP Headers",
"hGroups":"Groups (HashRef)",
@ -459,7 +462,7 @@
"mailCharset":"Charset",
"mailConfirmBody":"Confirmation mail content",
"mailConfirmSubject":"Confirmation mail subject",
"mailContent":"Password mail content",
"mailContent":"Mail content",
"mailFrom":"Mail sender",
"mailHeaders":"Mail headers",
"mailLDAPFilter":"Mail filter",
@ -526,6 +529,7 @@
"notificationDeleted":"Notification deleted",
"notificationDone":"Notification done",
"notificationsDone":"Notifications done",
"notificationsExplorer":"Explorer",
"notificationNotCreated":"The notification was not created",
"notificationNotDeleted":"The notification was not marked as done",
"notificationNotFound":"The notification was not found",
@ -801,7 +805,7 @@
"saveReport":"Save report",
"savingConfirmation":"Saving confirmation",
"scope":"Scope",
"search":"Search ...",
"search":"Search...",
"secondFactors":"Second factors",
"securedCookie":"Secured Cookie (SSL)",
"security":"Security",
@ -1111,4 +1115,4 @@
"samlRelayStateTimeout":"RelayState session timeout",
"samlUseQueryStringSpecific":"Use specific query_string method",
"samlOverrideIDPEntityID":"Override Entity ID when acting as IDP"
}
}

View File

@ -104,7 +104,9 @@
"browserIdSiteName":"Site name",
"browserIdVerificationURL":"Verification URL",
"browseTree":"Browse tree",
"bruteForceProtection":"Brute-force attack protection",
"bruteForceProtection":"Activation",
"bruteForceAttackProtection":"Brute-force attack protection",
"bruteForceProtectionIncrementalTempo":"Incremental lock times",
"cancel":"Cancel",
"captcha_login_enabled":"Activation in login form",
"captcha_mail_enabled":"Activation in password reset by mail form",
@ -142,7 +144,7 @@
"casStorageOptions":"CAS sessions module options",
"categoryName":"Category name",
"cda":"Multiple domains",
"certificateMailContent":"Certificate mail content",
"certificateMailContent":"Mail content",
"certificateResetByMailManagement":"Certificate management",
"certificateResetByMailURL":"Reset page URL",
"certificateResetByMailCeaAttribute":"Certificate CEA attibute",
@ -326,6 +328,7 @@
"gpgParams":"GPG parameters",
"grantSessionRules":"Opening conditions",
"groups":"Groups",
"groupsBeforeMacros":"Compute groups before macros",
"hashkey":"Key",
"headers":"HTTP Headers",
"hGroups":"Groups (HashRef)",
@ -459,7 +462,7 @@
"mailCharset":"Charset",
"mailConfirmBody":"Confirmation mail content",
"mailConfirmSubject":"Confirmation mail subject",
"mailContent":"Password mail content",
"mailContent":"Mail content",
"mailFrom":"Mail sender",
"mailHeaders":"Mail headers",
"mailLDAPFilter":"Mail filter",
@ -526,6 +529,7 @@
"notificationDeleted":"Notification deleted",
"notificationDone":"Notification done",
"notificationsDone":"Notifications done",
"notificationsExplorer":"Explorer",
"notificationNotCreated":"The notification was not created",
"notificationNotDeleted":"The notification was not marked as done",
"notificationNotFound":"The notification was not found",

View File

@ -29,11 +29,11 @@
"actives":"Actives",
"activeTimer":"Délai d'acceptation automatique",
"addAppCasPartner":"Ajouter une application CAS",
"addIDPSamlPartner":"Ajouter un IDP SAML",
"addIDPSamlPartner":"Ajouter un FI SAML",
"addOidcOp":"Ajouter un fournisseur OpenID Connect",
"addOidcRp":"Ajouter un client OpenID Connect",
"addSamlAttribute":"Ajouter un attribut",
"addSPSamlPartner":"Ajouter un SP SAML",
"addSPSamlPartner":"Ajouter un FS SAML",
"addSrvCasPartner":"Ajouter un serveur CAS",
"addU2FKey":"Ajouter une clef U2F",
"addTOTPKey":"Ajouter une clef TOTP",
@ -104,7 +104,9 @@
"browserIdSiteName":"Nom du site",
"browserIdVerificationURL":"Adresse de vérification",
"browseTree":"Parcourir l'arbre",
"bruteForceProtection":"Protection contre les attaques par force brute",
"bruteForceProtection":"Activation",
"bruteForceAttackProtection":"Protection contre les attaques par force brute",
"bruteForceProtectionIncrementalTempo":"Temps de verrouillage incrémentiels",
"cancel":"Annuler",
"captcha_login_enabled":"Activation dans le formulaire d'authentification",
"captcha_mail_enabled":"Activation dans le formulaire de réinitialisation par mail",
@ -142,7 +144,7 @@
"casStorageOptions":"Options du module des sessions CAS",
"categoryName":"Nom de la catégorie",
"cda":"Domaines multiples",
"certificateMailContent":"Contenu du mail du certificat",
"certificateMailContent":"Contenu du mail",
"certificateResetByMailManagement":"Gestion des certificats",
"certificateResetByMailURL":"URL de la page de réinitialisation",
"certificateResetByMailCeaAttribute":"Attribut CEA du certificat",
@ -326,6 +328,7 @@
"gpgParams":"Paramètres GPG",
"grantSessionRules":"Conditions d'ouverture",
"groups":"Groupes",
"groupsBeforeMacros":"Calculer les groupes avant les macros",
"hashkey":"Clef",
"headers":"En-têtes HTTP",
"hGroups":"Groupes (HashRef)",
@ -459,7 +462,7 @@
"mailCharset":"Charset",
"mailConfirmBody":"Contenu du message de confirmation",
"mailConfirmSubject":"Sujet du message de confirmation",
"mailContent":"Contenu du mail de mot de passe",
"mailContent":"Contenu du mail",
"mailFrom":"Expéditeur du message",
"mailHeaders":"En-têtes du mail",
"mailLDAPFilter":"Filtre mail",
@ -526,6 +529,7 @@
"notificationDeleted":"La notification a été marquée comme lue",
"notificationDone":"Notification validée",
"notificationsDone":"Notifications validées",
"notificationsExplorer":"Explorateur",
"notificationNotCreated":"La notification n'a pas été créée",
"notificationNotDeleted":"La notification n'a pas été marquée comme lue",
"notificationNotFound":"La notification n'a pas été trouvée",
@ -817,7 +821,7 @@
"sessionStorage":"Stockage des sessions",
"sessionTitle":"Contenu de la session",
"sfaTitle":"Seconds Facteurs d'Authentification",
"sfExtra":"Seconds Facteurs additionnels",
"sfExtra":"Seconds facteurs additionnels",
"sfRequired":"Exiger 2FA",
"sfRemovedNotification":"Afficher un message si un SF expiré a été supprimé",
"sfRemovedMsgRule":"Activation",

View File

@ -104,7 +104,9 @@
"browserIdSiteName":"Nome del sito",
"browserIdVerificationURL":"URL di verifica",
"browseTree":"Naviga albero",
"bruteForceProtection":"Protezione da attacchi brute-force",
"bruteForceProtection":"Activation",
"bruteForceAttackProtection":"Brute-force attack protection",
"bruteForceProtectionIncrementalTempo":"Incremental lock times",
"cancel":"Cancella",
"captcha_login_enabled":"Attivazione nel modulo di login",
"captcha_mail_enabled":"Attivazione della reimpostazione della password tramite modulo di posta",
@ -142,17 +144,17 @@
"casStorageOptions":"Opzioni del modulo sessioni CAS",
"categoryName":"Nome della categoria",
"cda":"Domini multipli",
"certificateMailContent":"Certificate mail content",
"certificateMailContent":"Mail content",
"certificateResetByMailManagement":"Certificate management",
"certificateResetByMailURL":"Reset page URL",
"certificateResetByMailCeaAttribute":"Certificat CEA attribut",
"certificateResetByMailCeaAttribute":"Certificate CEA attibute",
"certificateResetByMailCertificateAttribute":"Certificate attribute name",
"certificateResetByMailStep1Subject":"Reset mail subject",
"certificateResetByMailStep1Body":"Reset mail content",
"certificateResetByMailStep2Subject":"Confirmation mail subject",
"certificateResetByMailStep2Subject":"Soggetto della mail di conferma",
"certificateResetByMailStep2Body":"Confirmation mail content",
"certificateResetByMailValidityDelay":"Minimum duration before expiration",
"portalDisplayCertificateResetByMail":"Reset your crtificate",
"portalDisplayCertificateResetByMail":"Reset your certificate",
"contentSecurityPolicy":"Politica di protezione dei contenuti",
"contextSwitching":"Switch context another user",
"contextSwitchingHiddenAttributes":"Attributi nascosti",
@ -175,7 +177,7 @@
"corsAllow_Origin":"Access-Control-Allow-Origin",
"corsExpose_Headers":"Access-Control-Expose-Headers",
"corsMax_Age":"Access-Control-Max-Age",
"cfgLog":"Riprendi",
"cfgLog":"Summary",
"cfgVersion":"Versione configurazione",
"checkXSS":"Verifica attacchi XSS",
"clickHereToForce":"Clicca qui per forzare",
@ -326,6 +328,7 @@
"gpgParams":"Parametri GPG",
"grantSessionRules":"Condizioni di apertura",
"groups":"Gruppi",
"groupsBeforeMacros":"Compute groups before macros",
"hashkey":"Chiave",
"headers":"Intestazioni HTTP",
"hGroups":"Gruppi (HashRef)",
@ -459,7 +462,7 @@
"mailCharset":"Charset",
"mailConfirmBody":"Contenuto della mail di conferma",
"mailConfirmSubject":"Soggetto della mail di conferma",
"mailContent":"Contenuto della mail di ripristino della password",
"mailContent":"Mail content",
"mailFrom":"Mittente",
"mailHeaders":"Intestazioni di posta",
"mailLDAPFilter":"Filtro mail",
@ -526,6 +529,7 @@
"notificationDeleted":"La notifica è stata cancellata",
"notificationDone":"La notifica è stata effettuata",
"notificationsDone":"Notifiche effettuate",
"notificationsExplorer":"Explorer",
"notificationNotCreated":"La notifica non è stata creata",
"notificationNotDeleted":"La notifica non è stata contrassegnata come eseguita",
"notificationNotFound":"La notifica non é stata trovata",
@ -801,7 +805,7 @@
"saveReport":"Salva report",
"savingConfirmation":"Salvataggio della conferma",
"scope":"Ambito",
"search":"Cerca ...",
"search":"Cerca...",
"secondFactors":"Secondi fattori",
"securedCookie":"Cookie protetti (SSL)",
"security":"Sicurezza",
@ -1111,4 +1115,4 @@
"samlRelayStateTimeout":"Timeout di sessione di RelayState",
"samlUseQueryStringSpecific":"Utilizza il metodo specifico query_string",
"samlOverrideIDPEntityID":"Sostituisci l'ID entità quando agisce come IDP"
}
}

View File

@ -104,7 +104,9 @@
"browserIdSiteName":"Site adı",
"browserIdVerificationURL":"Doğrulama URL'i",
"browseTree":"Ağaca göz at",
"bruteForceProtection":"Kaba kuvvet saldırı koruması",
"bruteForceProtection":"Activation",
"bruteForceAttackProtection":"Brute-force attack protection",
"bruteForceProtectionIncrementalTempo":"Incremental lock times",
"cancel":"İptal Et",
"captcha_login_enabled":"Giriş formunda aktivasyon",
"captcha_mail_enabled":"E-posta formu tarafından parola sıfırlamada aktivasyon",
@ -142,15 +144,15 @@
"casStorageOptions":"CAS oturumları modül seçenekleri",
"categoryName":"Kategori ismi",
"cda":"Çoklu alan adları",
"certificateMailContent":"Certificate mail content",
"certificateMailContent":"Mail content",
"certificateResetByMailManagement":"Certificate management",
"certificateResetByMailURL":"Reset page URL",
"certificateResetByMailURL":"Sayfa URL'sini sıfırla",
"certificateResetByMailCeaAttribute":"Certificate CEA attibute",
"certificateResetByMailCertificateAttribute":"Certificate attribute name",
"certificateResetByMailStep1Subject":"Reset mail subject",
"certificateResetByMailStep1Body":"Reset mail content",
"certificateResetByMailStep2Subject":"Confirmation mail subject",
"certificateResetByMailStep2Body":"Confirmation mail content",
"certificateResetByMailStep2Subject":"Doğrulama e-postası konusu",
"certificateResetByMailStep2Body":"Doğrulama e-postası içeriği",
"certificateResetByMailValidityDelay":"Minimum duration before expiration",
"portalDisplayCertificateResetByMail":"Reset your certificate",
"contentSecurityPolicy":"İçerik güvenlik ilkesi",
@ -175,7 +177,7 @@
"corsAllow_Origin":"Access-Control-Allow-Origin",
"corsExpose_Headers":"Access-Control-Expose-Headers",
"corsMax_Age":"Access-Control-Max-Age",
"cfgLog":"Sürdür",
"cfgLog":"Summary",
"cfgVersion":"Yapılandırma sürümü",
"checkXSS":"XSS saldırılarını kontrol et",
"clickHereToForce":"Zorlamak için buraya tıklayın",
@ -326,6 +328,7 @@
"gpgParams":"GPG parametreleri",
"grantSessionRules":"Açılış koşulları",
"groups":"Gruplar",
"groupsBeforeMacros":"Compute groups before macros",
"hashkey":"Anahtar",
"headers":"HTTP Başlıkları",
"hGroups":"Gruplar (HashRef)",
@ -459,7 +462,7 @@
"mailCharset":"Karakter seti",
"mailConfirmBody":"Doğrulama e-postası içeriği",
"mailConfirmSubject":"Doğrulama e-postası konusu",
"mailContent":"Parola sıfırlama e-postası içeriği",
"mailContent":"Mail content",
"mailFrom":"E-posta göndericisi",
"mailHeaders":"E-posta başlıkları",
"mailLDAPFilter":"E-posta filtresi",
@ -526,6 +529,7 @@
"notificationDeleted":"Bildirim silindi",
"notificationDone":"Bildirim tamamlandı",
"notificationsDone":"Bildirimler tamamlandı",
"notificationsExplorer":"Explorer",
"notificationNotCreated":"Bildirim oluşturulmadı",
"notificationNotDeleted":"Bildirim tamamlandı olarak işaretlenmedi",
"notificationNotFound":"Bildirim bulunamadı",
@ -672,7 +676,7 @@
"passwordDB":"Parola modülü",
"passwordManagement":"Parola yönetimi",
"passwordPolicyMinSize":"Minimum parola uzunluğu",
"passwordPolicyMinLower":"Minimum küçük harf karakter sayısı:",
"passwordPolicyMinLower":"Minimum küçük harf karakter sayısı",
"passwordPolicyMinUpper":"Minimum büyük harf karakter sayısı",
"passwordPolicyMinDigit":"Minimum rakam karakter sayısı",
"passwordResetAllowedRetries":"Maksimum parola sıfırlama denemesi",
@ -801,7 +805,7 @@
"saveReport":"Raporu kaydet",
"savingConfirmation":"Doğrulama kaydediliyor",
"scope":"Kapsam",
"search":"Ara ...",
"search":"Ara...",
"secondFactors":"İki faktörlü kimlik doğrulama",
"securedCookie":"Güvenli Çerez (SSL)",
"security":"Güvenlik",
@ -1111,4 +1115,4 @@
"samlRelayStateTimeout":"RelayState oturum zaman aşımı",
"samlUseQueryStringSpecific":"Spesifik query_string metodu kullan",
"samlOverrideIDPEntityID":"IDP olarak davrandığında Varlık ID'yi geçersiz kıl"
}
}

View File

@ -104,7 +104,9 @@
"browserIdSiteName":"Tên trang web",
"browserIdVerificationURL":"URL xác minh",
"browseTree":"Duyệt cây",
"bruteForceProtection":"Brute-force attack protection",
"bruteForceProtection":"Activation",
"bruteForceAttackProtection":"Brute-force attack protection",
"bruteForceProtectionIncrementalTempo":"Incremental lock times",
"cancel":"Hủy",
"captcha_login_enabled":"Kích hoạt ở dạng đăng nhập",
"captcha_mail_enabled":"Kích hoạt đặt lại mật khẩu bằng biểu mẫu thư",
@ -142,7 +144,7 @@
"casStorageOptions":"Các tùy chọn mô-đun phiên CAS",
"categoryName":"Tên thể loại",
"cda":"Nhiều tên miền",
"certificateMailContent":"Certificate mail content",
"certificateMailContent":"Mail content",
"certificateResetByMailManagement":"Certificate management",
"certificateResetByMailURL":"Reset page URL",
"certificateResetByMailCeaAttribute":"Certificate CEA attibute",
@ -175,7 +177,7 @@
"corsAllow_Origin":"Access-Control-Allow-Origin",
"corsExpose_Headers":"Access-Control-Expose-Headers",
"corsMax_Age":"Access-Control-Max-Age",
"cfgLog":"Tiếp tục",
"cfgLog":"Summary",
"cfgVersion":"Phiên bản cấu hình",
"checkXSS":"Kiểm tra tấn công XSS",
"clickHereToForce":"Nhấp vào đây để bắt buộc",
@ -326,6 +328,7 @@
"gpgParams":"Tham số GPG",
"grantSessionRules":"Điều kiện mở",
"groups":"Nhóm",
"groupsBeforeMacros":"Compute groups before macros",
"hashkey":"Khóa",
"headers":"Tiêu đề HTTP",
"hGroups":"Nhóm (HashRef)",
@ -459,7 +462,7 @@
"mailCharset":"Charset",
"mailConfirmBody":"Xác nhận nội dung thư",
"mailConfirmSubject":"Xác nhận chủ đề thư",
"mailContent":"Password mail content",
"mailContent":"Mail content",
"mailFrom":"Người gửi thư",
"mailHeaders":"Tiêu đề thư",
"mailLDAPFilter":"Bộ lọc thư",
@ -526,6 +529,7 @@
"notificationDeleted":"Thông báo đã xoá",
"notificationDone":"Thông báo được thực hiện",
"notificationsDone":"Thông báo đã hoàn tất",
"notificationsExplorer":"Explorer",
"notificationNotCreated":"Thông báo không được tạo ra",
"notificationNotDeleted":"Thông báo không được đánh dấu là đã hoàn tất",
"notificationNotFound":"Không tìm thấy thông báo",
@ -801,7 +805,7 @@
"saveReport":"Lưu báo cáo",
"savingConfirmation":"Lưu xác nhận",
"scope":"Phạm vi",
"search":"Search ...",
"search":"Search...",
"secondFactors":"Second factors",
"securedCookie":"Cookie bảo mật (SSL)",
"security":"An ninh",
@ -1111,4 +1115,4 @@
"samlRelayStateTimeout":"Thời gian hết hạn phiên RelayState ",
"samlUseQueryStringSpecific":"Sử dụng phương pháp query_string cụ thể",
"samlOverrideIDPEntityID":"Override Entity ID when acting as IDP"
}
}

View File

@ -104,7 +104,9 @@
"browserIdSiteName":"站点名称",
"browserIdVerificationURL":"验证 URL",
"browseTree":"浏览树",
"bruteForceProtection":"Brute-force attack protection",
"bruteForceProtection":"Activation",
"bruteForceAttackProtection":"Brute-force attack protection",
"bruteForceProtectionIncrementalTempo":"Incremental lock times",
"cancel":"取消",
"captcha_login_enabled":" 登录激活",
"captcha_mail_enabled":"通过邮件进行密码重置 激活",
@ -142,7 +144,7 @@
"casStorageOptions":"CAS 会话模块选项",
"categoryName":"分类名称",
"cda":"Multiple domains",
"certificateMailContent":"Certificate mail content",
"certificateMailContent":"Mail content",
"certificateResetByMailManagement":"Certificate management",
"certificateResetByMailURL":"Reset page URL",
"certificateResetByMailCeaAttribute":"Certificate CEA attibute",
@ -175,7 +177,7 @@
"corsAllow_Origin":"Access-Control-Allow-Origin",
"corsExpose_Headers":"Access-Control-Expose-Headers",
"corsMax_Age":"Access-Control-Max-Age",
"cfgLog":"档案",
"cfgLog":"Summary",
"cfgVersion":"配置信息",
"checkXSS":"Check XSS attacks",
"clickHereToForce":"Click here to force",
@ -326,6 +328,7 @@
"gpgParams":"GPG parameters",
"grantSessionRules":"Opening conditions",
"groups":"Groups",
"groupsBeforeMacros":"Compute groups before macros",
"hashkey":"Key",
"headers":"HTTP Headers",
"hGroups":"Groups (HashRef)",
@ -459,7 +462,7 @@
"mailCharset":"Charset",
"mailConfirmBody":"Confirmation mail content",
"mailConfirmSubject":"Confirmation mail subject",
"mailContent":"Password mail content",
"mailContent":"Mail content",
"mailFrom":"邮件发送者",
"mailHeaders":"邮件头",
"mailLDAPFilter":"邮件过滤器",
@ -526,6 +529,7 @@
"notificationDeleted":"Notification deleted",
"notificationDone":"Notification done",
"notificationsDone":"Notifications done",
"notificationsExplorer":"Explorer",
"notificationNotCreated":"The notification was not created",
"notificationNotDeleted":"The notification was not marked as done",
"notificationNotFound":"The notification was not found",
@ -801,7 +805,7 @@
"saveReport":"Save report",
"savingConfirmation":"Saving confirmation",
"scope":"Scope",
"search":"Search ...",
"search":"Search...",
"secondFactors":"Second factors",
"securedCookie":"Secured Cookie (SSL)",
"security":"Security",
@ -1111,4 +1115,4 @@
"samlRelayStateTimeout":"RelayState session timeout",
"samlUseQueryStringSpecific":"Use specific query_string method",
"samlOverrideIDPEntityID":"Override Entity ID when acting as IDP"
}
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -12,9 +12,8 @@
<div class="navbar navbar-default">
<div class="navbar-collapse">
<ul class="nav navbar-nav" role="grid">
<li><a id="a-persistent" role="row"><i class="glyphicon glyphicon-filter"></i> {{translate('2faSessions')}} &nbsp;&nbsp;</a></li>
<form name="filterForm">
<div class="form-check ">&nbsp;&nbsp;&nbsp;
<form class="navbar-form" name="filterForm">
<div class="form-check ">
<input type="checkbox" ng-model="U2FCheck" class="form-check-input" ng-true-value="2" ng-false-value="1" ng-change="search2FA()"/>
<label class="form-check-label" for="U2FCheck">U2F</label>
&nbsp;&nbsp;&&nbsp;&nbsp;
@ -26,8 +25,8 @@
</div>
</form>
</ul>
<div class="col-lg-6 col-md-6 col-sm-8 col-xs-14" >
<form class="navbar-form" role="search">
<div class="col-lg-6 col-md-6 col-sm-8 col-xs-14" >
<form class="navbar-form" role="search">
<div class="input-group add-on">
<input class="form-control" placeholder="{{translate('search')}}" type="text" ng-model="searchString" ng-init="" ng-keyup="search2FA()"/>
<div class="input-group-btn">

View File

@ -15,9 +15,9 @@
<div class="navbar navbar-default">
<div class="navbar-collapse">
<ul class="nav navbar-nav" role="grid">
<li><a id="a-actives" href="#" role="row"><i class="glyphicon glyphicon-eye-open"></i> {{translate('actives')}}</a></li>
<li><a id="a-done" href="#!/done" role="row"><i class="glyphicon glyphicon-check"></i> {{translate('dones')}}</a></li>
<li><a id="a-new" href="#!/new" role="row"><i class="glyphicon glyphicon-plus-sign"></i> {{translate('create')}}</a></li>
<li><a id="a-actives" href="#" role="row" ng-style="activesStyle"><i class="glyphicon glyphicon-eye-open"></i> {{translate('actives')}}</a></li>
<li><a id="a-done" href="#!/done" role="row" ng-style="doneStyle"><i class="glyphicon glyphicon-check"></i> {{translate('dones')}}</a></li>
<li><a id="a-new" href="#!/new" role="row" ng-style="newStyle"><i class="glyphicon glyphicon-plus-sign"></i> {{translate('create')}}</a></li>
</ul>
</div>
</div>
@ -69,6 +69,10 @@
<th>{{translate('reference')}}</th>
<td>{{currentNotification.reference}}</td>
</tr>
<tr ng-if="currentNotification.date">
<th>{{translate('date')}}</th>
<td>{{currentNotification.date}}</td>
</tr>
<tr ng-if="currentNotification.condition">
<th>{{translate('condition')}}</th>
<td>{{currentNotification.condition}}</td>

View File

@ -16,7 +16,7 @@
<div class="navbar-collapse">
<ul class="nav navbar-nav" role="grid">
<li uib-dropdown>
<a id="navsso" name="menu" uib-dropdown-toggle data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false"><i class="glyphicon glyphicon-user"></i> {{translate('ssoSessions')}} <span class="caret"></span></a>
<a id="navsso" name="menu" uib-dropdown-toggle data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false" ng-style="navssoStyle"><i class="glyphicon glyphicon-user"></i> {{translate('ssoSessions')}} <span class="caret"></span></a>
<ul uib-dropdown-menu aria-labelled-by="navsso">
<li><a id="a-users" href="#" role="row"><i class="glyphicon glyphicon-user"></i> {{translate('users')}}</a></li>
<li><a id="a-ip" href="#!/ipAddr" role="row"><i class="glyphicon glyphicon-sort-by-order"></i> {{translate('ipAddresses')}}</a></li>
@ -25,8 +25,8 @@
<li><a id="a-updatetime" href="#!/_updateTime" role="row"><i class="glyphicon glyphicon-hourglass"></i> {{translate('_updateTime')}}</a></li>
</ul>
</li>
<li><a id="a-persistent" href="#!/persistent" role="row"><i class="glyphicon glyphicon-lock"></i> {{translate('persistentSessions')}}</a></li>
<li><a id="a-offline" href="#!/offline" role="row"><i class="glyphicon glyphicon-time"></i> {{translate('offlineSessions')}}</a></li>
<li><a id="a-persistent" href="#!/persistent" role="row" ng-style="persistentStyle"><i class="glyphicon glyphicon-lock"></i> {{translate('persistentSessions')}}</a></li>
<li><a id="a-offline" href="#!/offline" role="row" ng-style="offlineStyle"><i class="glyphicon glyphicon-time"></i> {{translate('offlineSessions')}}</a></li>
</ul>
</div>
</div>

View File

@ -41,7 +41,8 @@ sub newSession {
_session_kind => $kind,
_2fDevices => to_json($sfaDevices),
}
), "New $kind session for $uid"
),
"New $kind session for $uid"
);
count(1);
}
@ -96,7 +97,7 @@ sub checkGet {
my ( $uid, $id ) = splice @_;
my ( $test, $res, $ret );
$test = "$uid should have one 2F with id \"$id\"";
$res = get( $test, $uid, undef, $id );
$res = get( $test, $uid, undef, $id );
check200( $test, $res );
#diag Dumper($res);
@ -110,7 +111,7 @@ sub checkGet404 {
my ( $uid, $id ) = splice @_;
my ( $test, $res, $ret );
$test = "$uid should not have any 2F with id \"$id\"";
$res = get( $test, $uid, undef, $id );
$res = get( $test, $uid, undef, $id );
check404( $test, $res );
}
@ -138,7 +139,7 @@ sub checkGetBadType {
my ( $uid, $type ) = splice @_;
my ( $test, $res );
$test = "Get for uid $uid and type \"$type\" should get rejected.";
$res = get( $test, $uid, $type );
$res = get( $test, $uid, $type );
check405( $test, $res );
}
@ -176,7 +177,7 @@ sub checkDelete {
my ( $uid, $id ) = splice @_;
my ( $test, $res );
$test = "$uid should have a 2F with id \"$id\" to be deleted.";
$res = del( $test, $uid, undef, $id );
$res = del( $test, $uid, undef, $id );
check200( $test, $res );
}
@ -184,7 +185,7 @@ sub checkDelete404 {
my ( $uid, $id ) = splice @_;
my ( $test, $res );
$test = "$uid should not have a 2F with id \"$id\" to be deleted.";
$res = del( $test, $uid, undef, $id );
$res = del( $test, $uid, undef, $id );
check404( $test, $res );
}
@ -209,7 +210,7 @@ sub checkDeleteBadType {
my ( $uid, $type ) = splice @_;
my ( $test, $res );
$test = "Delete for uid $uid and type \"$type\" should get rejected.";
$res = del( $test, $uid, $type );
$res = del( $test, $uid, $type );
check405( $test, $res );
}
@ -218,7 +219,7 @@ my $ret;
## Sessions creation
# msmith
newSession( 'msmith', '127.10.0.1', 'SSO', $sfaDevices );
newSession( 'msmith', '127.10.0.1', 'SSO', $sfaDevices );
newSession( 'msmith', '127.10.0.1', 'Persistent', $sfaDevices );
# dwho
@ -242,7 +243,7 @@ $sfaDevices = [ {
"epoch" => time
}
];
newSession( 'dwho', '127.10.0.1', 'SSO', $sfaDevices );
newSession( 'dwho', '127.10.0.1', 'SSO', $sfaDevices );
newSession( 'dwho', '127.10.0.1', 'Persistent', $sfaDevices );
# rtyler
@ -266,7 +267,7 @@ $sfaDevices = [ {
"epoch" => time
}
];
newSession( 'rtyler', '127.10.0.1', 'SSO', $sfaDevices );
newSession( 'rtyler', '127.10.0.1', 'SSO', $sfaDevices );
newSession( 'rtyler', '127.10.0.1', 'Persistent', $sfaDevices );
# davros
@ -284,7 +285,7 @@ $sfaDevices = [ {
"epoch" => time
}
];
newSession( 'davros', '127.10.0.1', 'SSO', $sfaDevices );
newSession( 'davros', '127.10.0.1', 'SSO', $sfaDevices );
newSession( 'davros', '127.10.0.1', 'Persistent', $sfaDevices );
# tof
@ -296,7 +297,7 @@ $sfaDevices = [ {
"epoch" => time
}
];
newSession( 'tof', '127.10.0.1', 'SSO', $sfaDevices );
newSession( 'tof', '127.10.0.1', 'SSO', $sfaDevices );
newSession( 'tof', '127.10.0.1', 'Persistent', $sfaDevices );
# dwho

View File

@ -90,7 +90,7 @@ sub checkGet {
my $res = get( $test, $type, $confKey );
check200( $test, $res );
my @path = split '/', $attrPath;
my $key = from_json( $res->[2]->[0] );
my $key = from_json( $res->[2]->[0] );
for (@path) {
$key = $key->{$_};
}

View File

@ -58,7 +58,7 @@ while (<F>) {
close F;
ok( $hstruct = from_json($hstruct), 'struct.json is JSON' );
ok( ref $hstruct eq 'ARRAY', 'struct.json is an array' )
ok( ref $hstruct eq 'ARRAY', 'struct.json is an array' )
or print STDERR "Expected: ARRAY, got: " . ( ref $hstruct ) . "\n";
count(2);

View File

@ -25,7 +25,7 @@ ok(
),
"Request succeed"
);
ok( $res->[0] == 200, "Result code is 200" );
ok( $res->[0] == 200, "Result code is 200" );
ok( $key = from_json( $res->[2]->[0] ), 'Response is JSON' );
count(3);

View File

@ -20,7 +20,7 @@ mkdir 't/sessions';
my ( $res, $resBody );
ok( $res = &client->_post( '/confs/', 'cfgNum=1', &body, 'application/json' ),
"Request succeed" );
ok( $res->[0] == 200, "Result code is 200" );
ok( $res->[0] == 200, "Result code is 200" );
ok( $resBody = from_json( $res->[2]->[0] ), "Result body contains JSON text" );
ok( $resBody->{result} == 0, "JSON response contains \"result:0\"" )
@ -248,7 +248,7 @@ sub changes {
},
{
'confCompacted' => '1',
'removedKeys' => 'some; keys'
'removedKeys' => 'some; keys'
}
];
}

View File

@ -25,7 +25,8 @@ ok( $resBody = from_json( $res->[2]->[0] ), "Result body contains JSON text" );
ok( $resBody->{result} == 1, "JSON response contains \"result:1\"" )
or print STDERR Dumper($resBody);
ok(
@{ $resBody->{details}->{__warnings__} } == 2,
$resBody->{details}->{__warnings__}
and @{ $resBody->{details}->{__warnings__} } == 2,
'JSON response contains 2 warnings'
) or print STDERR Dumper($resBody);
@ -38,18 +39,18 @@ foreach my $i ( 0 .. 1 ) {
}
ok(
@{ $resBody->{details}->{__changes__} } == 24,
$resBody->{details}->{__changes__}
and @{ $resBody->{details}->{__changes__} } == 24,
'JSON response contains 24 changes'
) or print STDERR Dumper($resBody);
ok(
$resBody->{details}->{__changes__}->[23]->{confCompacted} == 1,
'Conf. has been compacted'
) or print STDERR Dumper($resBody);
ok( $resBody->{details}->{__changes__}->[23]->{confCompacted} == 1,
'Conf. has been compacted' )
or print STDERR Dumper($resBody);
my @removedKeys = split /; /, $resBody->{details}->{__changes__}->[23]->{removedKeys};
ok(
@removedKeys == 60, 'All removed keys found'
) or print STDERR Dumper(\@removedKeys);
my @removedKeys = split /; /,
$resBody->{details}->{__changes__}->[23]->{removedKeys};
ok( @removedKeys == 60, 'All removed keys found' )
or print STDERR Dumper( \@removedKeys );
#print STDERR Dumper($resBody);
ok( -f $confFiles->[1], 'File is created' );
@ -105,8 +106,7 @@ ok( @c2 == 15, '15 keys changed or created in conf 2' )
count(5);
ok( $res = &client->jsonResponse('/confs/latest'),
'Get last config metadata' );
ok( $res = &client->jsonResponse('/confs/latest'), 'Get last config metadata' );
ok( $res->{prev} == 1, ' Get previous configuration' );
count(2);
@ -119,8 +119,7 @@ done_testing( count() );
`rm -rf t/sessions`;
sub changes {
return [
{
return [ {
'key' => 'portal',
'new' => 'http://auth2.example.com/',
'old' => 'http://auth.example.com/'

View File

@ -16,7 +16,7 @@ unlink 't/conf/lmConf-2.json';
my ( $res, $resBody );
ok( $res = &client->_post( '/confs/', 'cfgNum=1', &body, 'application/json' ),
"Request succeed" );
ok( $res->[0] == 200, "Result code is 200" );
ok( $res->[0] == 200, "Result code is 200" );
ok( $resBody = from_json( $res->[2]->[0] ), "Result body contains JSON text" );
ok( $resBody->{result} == 0, "JSON response contains \"result:0\"" )
or print STDERR Dumper($res);

View File

@ -17,7 +17,7 @@ mkdir 't/sessions';
my ( $res, $resBody );
ok( $res = &client->_post( '/confs/', 'cfgNum=1', &body, 'application/json' ),
"Request succeed" );
ok( $res->[0] == 200, "Result code is 200" );
ok( $res->[0] == 200, "Result code is 200" );
ok( $resBody = from_json( $res->[2]->[0] ), "Result body contains JSON text" );
ok( $resBody->{result} == 1, "JSON response contains \"result:1\"" )
or print STDERR Dumper($res);

View File

@ -1,112 +1,114 @@
use Test::More;
use Test::Output;
use JSON;
use strict;
require 't/test-lib.pm';
my $tests = 14;
my $tests = 17;
use_ok('Lemonldap::NG::Common::Cli');
use_ok('Lemonldap::NG::Manager::Cli');
&cleanConfFiles;
SKIP: {
eval 'use Test::Output;';
if ($@) {
skip 'Test::Output is missing, skipping', $tests - 2;
}
my $client =
Lemonldap::NG::Manager::Cli->new( iniFile => 't/lemonldap-ng.ini' );
my $commonClient =
Lemonldap::NG::Common::Cli->new( iniFile => 't/lemonldap-ng.ini' );
my @cmd;
my $res;
# Test 'set' command
@cmd = qw(-yes 1 set notification 1);
Capture::Tiny::capture_stdout( sub { $client->run(@cmd) } );
# Test 'get' command
@cmd = qw(get notification);
$res = Capture::Tiny::capture_stdout( sub { $client->run(@cmd) } );
ok( $res =~ /^notification\s+=\s+1$/, '"get notification" OK' )
or diag " $res";
# Test 'addKey' command
@cmd = qw(-yes 1 addKey locationRules/test1.example.com ^/reject deny);
Test::Output::combined_like(
sub { $client->run(@cmd) },
qr#'\^/reject' => 'deny'#s,
'"addKey" OK'
);
# Test 'delKey' command
@cmd = qw(-yes 1 delKey locationRules/test1.example.com ^/reject);
Test::Output::combined_unlike(
sub { $client->run(@cmd) },
qr#'\^/reject' => 'deny'#s,
'"delKey" OK'
);
# Test 'get' command with key/subkey
@cmd = qw(get locationRules/test1.example.com);
$res = Capture::Tiny::capture_stdout( sub { $client->run(@cmd) } );
ok( $res =~ m#(?:/logout|default)#, '"get key/subkey" OK' )
or diag "$res";
# Test 'set' command with key/subkey
@cmd = qw(-yes 1 set locationRules/test1.example.com/default deny);
Capture::Tiny::capture_stdout( sub { $client->run(@cmd) } );
# Test 'save' command
@cmd = ('save');
$res = Capture::Tiny::capture_stdout( sub { $client->run(@cmd) } );
ok( $res =~ /^\s*(\{.*\})\s*$/s, '"save" result looks like JSON' );
eval { JSON::from_json($res) };
ok( not($@), ' result is JSON' ) or diag "error: $@";
# Test 'restore' command
close STDIN;
open STDIN, '<', \$res;
@cmd = ( 'restore', '-' );
Test::Output::combined_like( sub { $client->run(@cmd) },
qr/"cfgNum"\s*:\s*"3"/s, 'New config: 3' );
# Test 'set' command with force
@cmd = qw(-yes 1 -force 1 -cfgNum 2 set useSafeJail 0);
Test::Output::combined_like(
sub { $client->run(@cmd) },
qr#cfgNum forced with 2#s,
'"Force cfgNum" OK'
);
# Test 'update-cache' command with force
@cmd = qw(update-cache);
Test::Output::combined_like(
sub { $commonClient->run(@cmd) },
qr#Cache updated to configuration 3#s,
'"update-cache" OK'
);
# Test 'info' command with force
@cmd = qw(info);
Test::Output::combined_like(
$res = sub { $commonClient->run(@cmd) },
qr#\bAuthor IP\b#s,
'"Author IP" OK'
);
Test::Output::combined_like(
$res = sub { $commonClient->run(@cmd) },
qr#\bLog\b#s,
'"Log" OK'
);
Test::Output::combined_like(
$res = sub { $commonClient->run(@cmd) },
qr#\bVersion\b#s,
'"Version" OK'
);
sub llclient {
return Lemonldap::NG::Manager::Cli->new( iniFile => 't/lemonldap-ng.ini' );
}
sub llcommonClient {
return Lemonldap::NG::Common::Cli->new( iniFile => 't/lemonldap-ng.ini' );
}
my @cmd;
my $res;
# Test 'set' command
@cmd = qw(-yes 1 set notification 1);
combined_like(
sub { llclient->run(@cmd) },
qr/Saved under/,
'"addKey" OK'
);
# Test 'get' command
@cmd = qw(get notification);
$res = Test::Output::stdout_from( sub { llclient->run(@cmd) } );
ok( $res =~ /^notification\s+=\s+1$/, '"get notification" OK' )
or diag " $res";
# Test 'addKey' command
@cmd = qw(-yes 1 addKey locationRules/test1.example.com ^/reject deny);
combined_like(
sub { llclient->run(@cmd) },
qr/Saved under/,
'"addKey" OK'
);
# Test 'delKey' command
@cmd = qw(-yes 1 delKey locationRules/test1.example.com ^/reject);
combined_unlike(
sub { llclient->run(@cmd) },
qr#'\^/reject' => 'deny'#s,
'"delKey" OK'
);
# Test 'get' command with key/subkey
@cmd = qw(get locationRules/test1.example.com/default);
$res = Test::Output::stdout_from( sub { llclient->run(@cmd) } );
ok( $res =~ m#accept#, '"get key/subkey" OK' )
or diag "$res";
# Test 'set' command with key/subkey
@cmd = qw(-yes 1 set locationRules/test1.example.com/default deny);
combined_like(
sub { llclient->run(@cmd) },
qr/Saved under/,
'"addKey" OK'
);
# Test 'save' command
@cmd = qw(-cfgNum 1 save);
$res = Test::Output::stdout_from( sub { llclient->run(@cmd) } );
ok( $res =~ /^\s*(\{.*\})\s*$/s, '"save" result looks like JSON' );
my $j;
eval { $j = JSON::from_json($res) };
is( $j->{cfgNum}, 1, "correct version number" );
ok( not($@), ' result is JSON' ) or diag "error: $@";
# Test 'restore' command
my $tmpFile = File::Temp->new();
print $tmpFile $res;
@cmd = ( 'restore', $tmpFile->filename );
combined_like( sub { llclient->run(@cmd) },
qr/"cfgNum"\s*:\s*\d*/s, 'New config' );
# Test 'set' command with force
@cmd = qw(-yes 1 -force 1 -cfgNum 2 set useSafeJail 0);
combined_like(
sub { llclient->run(@cmd) },
qr#cfgNum forced with 2#s,
'"Force cfgNum" OK'
);
# Test 'info' command with force
@cmd = qw(info);
combined_like(
sub { llcommonClient->run(@cmd) },
qr#\bAuthor IP\b#s,
'"Author IP" OK'
);
combined_like( sub { llcommonClient->run(@cmd) },
qr#\bLog\b#s, '"Log" OK' );
combined_like( sub { llcommonClient->run(@cmd) },
qr#\bVersion\b#s, '"Version" OK' );
# Test 'rollback' command
@cmd = qw(rollback);
combined_like(
sub { llclient->run(@cmd) },
qr/Configuration \d+ has been rolled back/,
'"Author IP" OK'
);
count($tests);
done_testing( count() );
&cleanConfFiles;

View File

@ -17,7 +17,7 @@ mkdir 't/sessions';
my ( $res, $resBody );
ok( $res = &client->_post( '/confs/', 'cfgNum=1', &body, 'application/json' ),
"Request succeed" );
ok( $res->[0] == 200, "Result code is 200" );
ok( $res->[0] == 200, "Result code is 200" );
ok( $resBody = from_json( $res->[2]->[0] ), "Result body contains JSON text" );
ok( $resBody->{result} == 1, "JSON response contains \"result:1\"" )
or print STDERR Dumper($res);

View File

@ -148,7 +148,7 @@ count(5);
foreach (@ids) {
my $res;
ok( $res = &client->_del("/sessions/global/$_"), "Delete $_" );
ok( $res->[0] == 200, 'Result code is 200' );
ok( $res->[0] == 200, 'Result code is 200' );
ok( from_json( $res->[2]->[0] )->{result} == 1,
'Body is JSON and result==1' );
count(3);

View File

@ -44,7 +44,8 @@ SKIP: {
$client->jsonPostResponse( 'notifications/actives', '',
IO::String->new($notif),
'application/json', length($notif) );
ok( $res->{error} =~ /^Notification not created: Bad date/, 'Notification not inserted' );
ok( $res->{error} =~ /^Notification not created: Bad date/,
'Notification not inserted' );
$notif =
'{"date":"2099-13-30","uid":"dwho","reference":"Test","xml":"{\"title\":\"Test\"}"}';
@ -52,7 +53,8 @@ SKIP: {
$client->jsonPostResponse( 'notifications/actives', '',
IO::String->new($notif),
'application/json', length($notif) );
ok( $res->{error} =~ /^Notification not created: Bad date/, 'Notification not inserted' );
ok( $res->{error} =~ /^Notification not created: Bad date/,
'Notification not inserted' );
$notif =
'{"date":"2099-05_12","uid":"dwho","reference":"Test","xml":"{\"title\":\"Test\"}"}';
@ -98,7 +100,7 @@ SKIP: {
$res =
$client->jsonResponse( 'notifications/done', 'groupBy=substr(uid,1)' );
ok( $res->{result} == 1, 'Result = 1' );
ok( $res->{count} == 0, 'Count = 0' ) or diag Dumper($res);
ok( $res->{count} == 0, 'Count = 0' ) or diag Dumper($res);
#print STDERR Dumper($res);
}

View File

@ -16,21 +16,23 @@ my $res =
&client->jsonPostResponse( 'notifications/actives', '',
IO::String->new($notif),
'application/json', length($notif) );
ok( $res->{error} =~ /^Notification not created: Bad date/, 'Notification not inserted' );
ok( $res->{error} =~ /^Notification not created: Bad date/,
'Notification not inserted' );
count(1);
$notif =
'{"date":"2099-13-30","uid":"dwho","reference":"Test","xml":"{\"title\":\"Test\"}"}';
my $res =
$res =
&client->jsonPostResponse( 'notifications/actives', '',
IO::String->new($notif),
'application/json', length($notif) );
ok( $res->{error} =~ /^Notification not created: Bad date/, 'Notification not inserted' );
ok( $res->{error} =~ /^Notification not created: Bad date/,
'Notification not inserted' );
count(1);
$notif =
'{"date":"2099-05_12","uid":"dwho","reference":"Test","xml":"{\"title\":\"Test\"}"}';
my $res =
$res =
&client->jsonPostResponse( 'notifications/actives', '',
IO::String->new($notif),
'application/json', length($notif) );

View File

@ -277,7 +277,7 @@ $res = &client->jsonResponse( '/sfa/persistent',
'uid=*&groupBy=substr(uid,0)&U2FCheck=2&TOTPCheck=2&UBKCheck=2' );
ok( $res->{result} == 1,
'Search "uid"=* & UBK & TOTP & UBK - Result code = 1' );
ok( $res->{count} == 1, 'Found 1 result' ) or print STDERR Dumper($res);
ok( $res->{count} == 1, 'Found 1 result' ) or print STDERR Dumper($res);
ok( @{ $res->{values} } == 1, 'List 1 result' );
ok( $res->{values}->[0]->{value} && $res->{values}->[0]->{value} eq 'd',
'Result match "uid=d"' )

Some files were not shown because too many files have changed in this diff Show More