Merge branch 'v2.0' into configuration-api

merge-requests/133/head
Clément OUDOT 6 years ago
commit dbf6550f6a
  1. 8
      lemonldap-ng-handler/lib/Lemonldap/NG/Handler/Main/Reload.pm
  2. 38
      lemonldap-ng-manager/lib/Lemonldap/NG/Manager/Cli.pm
  3. 57
      lemonldap-ng-manager/t/16-cli.t
  4. 3
      lemonldap-ng-portal/lib/Lemonldap/NG/Portal/CertificateResetByMail/LDAP.pm
  5. 2
      lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Main/Run.pm
  6. 7
      lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Plugins/CertificateResetByMail.pm
  7. 1
      lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Register/Base.pm
  8. 2
      lemonldap-ng-portal/site/templates/bootstrap/standardform.tpl
  9. 5
      lemonldap-ng-portal/t/32-OIDC-Register.t
  10. 9
      lemonldap-ng-portal/t/41-Token.t
  11. 13
      lemonldap-ng-portal/t/test-lib.pm

@ -1,6 +1,6 @@
package Lemonldap::NG::Handler::Main::Reload; package Lemonldap::NG::Handler::Main::Reload;
our $VERSION = '2.0.7'; our $VERSION = '2.0.8';
package Lemonldap::NG::Handler::Main; package Lemonldap::NG::Handler::Main;
@ -214,7 +214,7 @@ sub defaultValuesInit {
# Override with vhost options # Override with vhost options
if ( $conf->{vhostOptions} ) { if ( $conf->{vhostOptions} ) {
my $name = 'vhost' . ucfirst($opt); my $name = 'vhost' . ucfirst($opt);
foreach my $vhost ( keys %{ $conf->{vhostOptions} } ) { foreach my $vhost ( sort keys %{ $conf->{vhostOptions} } ) {
$conf->{vhostOptions}->{$vhost} ||= {}; $conf->{vhostOptions}->{$vhost} ||= {};
my $val = $conf->{vhostOptions}->{$vhost}->{$name}; my $val = $conf->{vhostOptions}->{$vhost}->{$name};
@ -228,7 +228,7 @@ sub defaultValuesInit {
} }
} }
if ( $conf->{vhostOptions} ) { if ( $conf->{vhostOptions} ) {
foreach my $vhost ( keys %{ $conf->{vhostOptions} } ) { foreach my $vhost ( sort keys %{ $conf->{vhostOptions} } ) {
$class->tsv->{type}->{$vhost} = $class->tsv->{type}->{$vhost} =
$conf->{vhostOptions}->{$vhost}->{vhostType}; $conf->{vhostOptions}->{$vhost}->{vhostType};
$class->tsv->{authnLevel}->{$vhost} = $class->tsv->{authnLevel}->{$vhost} =
@ -326,7 +326,7 @@ sub locationRulesInit {
## @imethod protected void sessionStorageInit(hashRef args) ## @imethod protected void sessionStorageInit(hashRef args)
# Initialize the Apache::Session::* module choosed to share user's variables # Initialize the Apache::Session::* module choosed to share user's variables
# and the Cache::Cache module choosed to cache sessions # and the Cache::Cache module chosen to cache sessions
# @param $args reference to the configuration hash # @param $args reference to the configuration hash
sub sessionStorageInit { sub sessionStorageInit {
my ( $class, $conf ) = @_; my ( $class, $conf ) = @_;

@ -6,7 +6,7 @@ use Mouse;
use Data::Dumper; use Data::Dumper;
use Lemonldap::NG::Common::Conf::ReConstants; use Lemonldap::NG::Common::Conf::ReConstants;
our $VERSION = '2.0.6'; our $VERSION = '2.0.8';
$Data::Dumper::Useperl = 1; $Data::Dumper::Useperl = 1;
extends('Lemonldap::NG::Manager::Cli::Lib'); extends('Lemonldap::NG::Manager::Cli::Lib');
@ -21,17 +21,15 @@ has cfgNum => (
} }
); );
has sep => ( is => 'rw', isa => 'Str', default => '/' ); has log => ( is => 'rw' );
has req => ( is => 'ro' );
has req => ( is => 'ro' ); has sep => ( is => 'rw', isa => 'Str', default => '/' );
has format => ( is => 'rw', isa => 'Str', default => "%-25s | %-25s | %-25s" ); has format => ( is => 'rw', isa => 'Str', default => "%-25s | %-25s | %-25s" );
has yes => ( is => 'rw', isa => 'Bool', default => 0 );
has yes => ( 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 force => ( is => 'rw', isa => 'Bool', default => 0 ); has userLogger =>
( is => 'ro', lazy => 1, builder => sub { $_[0]->mgr->userLogger } );
has log => ( is => 'rw' );
sub get { sub get {
my ( $self, @keys ) = @_; my ( $self, @keys ) = @_;
@ -60,6 +58,7 @@ sub set {
die "$key seems to be a hash, modification refused"; die "$key seems to be a hash, modification refused";
} }
$oldValue //= ''; $oldValue //= '';
$self->logger->info("CLI: Set key $key with $pairs{$key}");
push @list, [ $key, $oldValue, $pairs{$key} ]; push @list, [ $key, $oldValue, $pairs{$key} ];
} }
unless ( $self->yes ) { unless ( $self->yes ) {
@ -96,6 +95,7 @@ sub addKey {
unless ( $root =~ /$simpleHashKeys$/o or $root =~ /$sep/o ) { unless ( $root =~ /$simpleHashKeys$/o or $root =~ /$sep/o ) {
die "$root is not a simple hash. Aborting"; die "$root is not a simple hash. Aborting";
} }
$self->logger->info("CLI: Append key $root/$newKey $value");
push @list, [ $root, $newKey, $value ]; push @list, [ $root, $newKey, $value ];
} }
require Clone; require Clone;
@ -136,6 +136,7 @@ sub delKey {
unless ( $root =~ /$simpleHashKeys$/o or $root =~ /$sep/o ) { unless ( $root =~ /$simpleHashKeys$/o or $root =~ /$sep/o ) {
die "$root is not a simple hash. Aborting"; die "$root is not a simple hash. Aborting";
} }
$self->logger->info("CLI: Remove key $root/$key");
push @list, [ $root, $key ]; push @list, [ $root, $key ];
} }
require Clone; require Clone;
@ -191,6 +192,7 @@ sub delKey {
sub lastCfg { sub lastCfg {
my ($self) = @_; my ($self) = @_;
$self->logger->info("CLI: Retrieve last conf.");
return $self->jsonResponse('/confs/latest')->{cfgNum}; return $self->jsonResponse('/confs/latest')->{cfgNum};
} }
@ -218,6 +220,7 @@ sub restore {
close $f; close $f;
die "Empty or malformed file $file" unless ( $conf =~ /\w/s ); die "Empty or malformed file $file" unless ( $conf =~ /\w/s );
} }
$self->logger->info("CLI: Restore conf.");
my $res = $self->_post( '/confs/raw', '', IO::String->new($conf), my $res = $self->_post( '/confs/raw', '', IO::String->new($conf),
'application/json', length($conf) ); 'application/json', length($conf) );
use Data::Dumper; use Data::Dumper;
@ -273,29 +276,34 @@ sub _save {
} }
); );
unless ( $parser->testNewConf() ) { unless ( $parser->testNewConf() ) {
$self->logger->error("CLI: Configuration rejected with message: $parser->{message}");
printf STDERR "Modifications rejected: %s:\n", $parser->{message}; printf STDERR "Modifications rejected: %s:\n", $parser->{message};
} }
my $saveParams = { force => $self->force }; my $saveParams = { force => $self->force };
if ( $self->force and $self->cfgNum ) { if ( $self->force and $self->cfgNum ) {
$self->logger->debug("CLI: cfgNum forced with $self->cfgNum()");
$saveParams->{cfgNum} = $self->cfgNum; $saveParams->{cfgNum} = $self->cfgNum;
$saveParams->{cfgNumFixed} = 1; $saveParams->{cfgNumFixed} = 1;
} }
$new->{cfgAuthor} = scalar( getpwuid $< ) . '(command-line)'; $new->{cfgAuthor} = scalar( getpwuid $< ) . '(command-line-interface)';
chomp $new->{cfgAuthor}; chomp $new->{cfgAuthor};
$new->{cfgAuthorIP} = '127.0.0.1'; $new->{cfgAuthorIP} = '127.0.0.1';
$new->{cfgDate} = time; $new->{cfgDate} = time;
$new->{cfgVersion} = $Lemonldap::NG::Manager::VERSION; $new->{cfgVersion} = $Lemonldap::NG::Manager::VERSION;
$new->{cfgLog} = $self->log // 'Modified using LLNG cli'; $new->{cfgLog} = $self->log // 'Modified with LL::NG CLI';
$new->{key} ||= join( '', $new->{key} ||= join( '',
map { chr( int( ord( Crypt::URandom::urandom(1) ) * 94 / 256 ) + 33 ) } map { chr( int( ord( Crypt::URandom::urandom(1) ) * 94 / 256 ) + 33 ) }
( 1 .. 16 ) ); ( 1 .. 16 ) );
my $s = $self->mgr->confAcc->saveConf( $new, %$saveParams ); my $s = $self->mgr->confAcc->saveConf( $new, %$saveParams );
if ( $s > 0 ) { if ( $s > 0 ) {
$self->logger->debug("CLI: Configuration $s has been saved by $new->{cfgAuthor}");
$self->logger->info("CLI: Configuration $s saved");
print STDERR "Saved under number $s\n"; print STDERR "Saved under number $s\n";
$parser->{status} = [ $self->mgr->applyConf($new) ]; $parser->{status} = [ $self->mgr->applyConf($new) ];
} }
else { else {
$self->logger->error("CLI: Configuration not saved!");
printf STDERR "Modifications rejected: %s:\n", $parser->{message}; printf STDERR "Modifications rejected: %s:\n", $parser->{message};
print STDERR Dumper($parser); print STDERR Dumper($parser);
} }
@ -336,8 +344,9 @@ sub run {
my $action = shift; my $action = shift;
unless ( $action =~ /^(?:get|set|addKey|delKey|save|restore)$/ ) { unless ( $action =~ /^(?:get|set|addKey|delKey|save|restore)$/ ) {
die die
"unknown action $action. Only get, set, addKey or delKey are accepted"; "Unknown action $action. Only get, set, addKey or delKey allowed";
} }
$self->$action(@_); $self->$action(@_);
} }
@ -346,7 +355,6 @@ package Lemonldap::NG::Manager::Cli::Request;
use Mouse; use Mouse;
has cfgNum => ( is => 'rw' ); has cfgNum => ( is => 'rw' );
has error => ( is => 'rw' ); has error => ( is => 'rw' );
sub params { sub params {

@ -1,10 +1,9 @@
my $tests; use Test::More;
BEGIN { $tests = 5 }
use Test::More tests => $tests;
use JSON; use JSON;
use strict; use strict;
require 't/test-lib.pm';
my $tests = 9;
use_ok('Lemonldap::NG::Common::Cli'); use_ok('Lemonldap::NG::Common::Cli');
use_ok('Lemonldap::NG::Manager::Cli'); use_ok('Lemonldap::NG::Manager::Cli');
@ -15,20 +14,60 @@ SKIP: {
if ($@) { if ($@) {
skip 'Test::Output is missing, skipping', $tests - 2; skip 'Test::Output is missing, skipping', $tests - 2;
} }
my @cmd;
@cmd = ('save');
my $client = my $client =
Lemonldap::NG::Manager::Cli->new( iniFile => 't/lemonldap-ng.ini' ); Lemonldap::NG::Manager::Cli->new( iniFile => 't/lemonldap-ng.ini' );
my $res = Capture::Tiny::capture_stdout( sub { $client->run(@cmd) } ); my @cmd;
my $res;
# Test 'set' command
@cmd = qw(-yes 1 set notification 1);
$res = 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
@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 'save' command
@cmd = ('save');
$res = Capture::Tiny::capture_stdout( sub { $client->run(@cmd) } );
ok( $res =~ /^\s*(\{.*\})\s*$/s, '"save" result looks like JSON' ); ok( $res =~ /^\s*(\{.*\})\s*$/s, '"save" result looks like JSON' );
eval { JSON::from_json($res) }; eval { JSON::from_json($res) };
ok( not($@), ' result is JSON' ) or diag "error: $@"; ok( not($@), ' result is JSON' ) or diag "error: $@";
# Test 'restore' command
close STDIN; close STDIN;
open STDIN, '<', \$res; open STDIN, '<', \$res;
@cmd = ( 'restore', '-' ); @cmd = ( 'restore', '-' );
Test::Output::combined_like( sub { $client->run(@cmd) }, Test::Output::combined_like( sub { $client->run(@cmd) },
qr/"cfgNum"\s*:\s*"2"/s, 'New config: 2' ); qr/"cfgNum"\s*:\s*"3"/s, 'New config: 3' );
} }
count($tests);
done_testing( count() );
&cleanConfFiles; &cleanConfFiles;
sub cleanConfFiles { sub cleanConfFiles {

@ -15,7 +15,7 @@ our $VERSION = '2.0.8';
# PRIVATE METHOD # PRIVATE METHOD
sub modifCertificate { sub modifCertificate {
my ( $self, $newcertif, $usercertif, $req ) = @_; my ( $self, $req, $newcertif, $usercertif ) = @_;
my $ceaAttribute = $self->conf->{certificateResetByMailCeaAttribute} my $ceaAttribute = $self->conf->{certificateResetByMailCeaAttribute}
|| "description"; || "description";
my $certificateAttribute = my $certificateAttribute =
@ -58,7 +58,6 @@ sub modifCertificate {
$self->logger->debug("$ceaAttribute set to $newcertif"); $self->logger->debug("$ceaAttribute set to $newcertif");
return PE_OK; return PE_OK;
} }
1; 1;

@ -64,7 +64,7 @@ sub handler {
and $req->{env}->{HTTP_COOKIE} and $req->{env}->{HTTP_COOKIE}
and $req->{env}->{HTTP_COOKIE} =~ /$url64/ ) and $req->{env}->{HTTP_COOKIE} =~ /$url64/ )
{ {
$self->logger->debug("Force cleaning pdata"); $self->logger->info("Force cleaning pdata");
$self->logger->warn("pdata cookie domain must be set") $self->logger->warn("pdata cookie domain must be set")
unless ( $self->conf->{pdataDomain} ); unless ( $self->conf->{pdataDomain} );
$req->pdata( {} ); $req->pdata( {} );

@ -394,7 +394,8 @@ sub _certificateReset {
# Send mail # Send mail
unless ( unless (
$self->send_mail( $self->send_mail(
$req->data->{mailAddress}, $subject, $body, $html $req->data->{mailAddress},
$subject, $body, $html
) )
) )
{ {
@ -525,8 +526,8 @@ sub modifyCertificate {
# Modify ldap certificate attribute # Modify ldap certificate attribute
$req->user( $req->{sessionInfo}->{_user} ); $req->user( $req->{sessionInfo}->{_user} );
my $result = my $result =
$self->registerModule->modifCertificate( $certificatExactAssertion, $self->registerModule->modifCertificate( $req, $certificatExactAssertion,
$cert, $req ); $cert );
$self->{user} = undef; $self->{user} = undef;
# Mail token can be used only one time, delete the session if all is ok # Mail token can be used only one time, delete the session if all is ok

@ -32,4 +32,5 @@ sub applyLoginRule {
# For now, get first letter of firstname and lastname # For now, get first letter of firstname and lastname
return substr( $firstname, 0, 1 ) . $lastname; return substr( $firstname, 0, 1 ) . $lastname;
} }
1; 1;

@ -36,7 +36,6 @@
<span trspan="connect">Connect</span> <span trspan="connect">Connect</span>
</button> </button>
</div> </div>
</TMPL_IF>
<div class="actions"> <div class="actions">
<TMPL_IF NAME="DISPLAY_RESETPASSWORD"> <TMPL_IF NAME="DISPLAY_RESETPASSWORD">
@ -60,3 +59,4 @@
</a> </a>
</TMPL_IF> </TMPL_IF>
</div> </div>
</TMPL_IF>

@ -48,7 +48,7 @@ ok( defined $register_answer->{client_id},
"Client ID found in answer: " . $register_answer->{client_id} ); "Client ID found in answer: " . $register_answer->{client_id} );
# New configuration registered # New configuration registered
my $confFile = "t/lmConf-2.json"; my $confFile = "$main::tmpDir/lmConf-2.json";
my $conf = JSON::from_json(`cat $confFile`); my $conf = JSON::from_json(`cat $confFile`);
# Check saved data # Check saved data
@ -74,8 +74,7 @@ clean_sessions();
done_testing(); done_testing();
sub op { sub op {
return LLNG::Manager::Test->new( return LLNG::Manager::Test->new( {
{
ini => { ini => {
logLevel => $debug, logLevel => $debug,
domain => 'idp.com', domain => 'idp.com',

@ -1,6 +1,8 @@
use Test::More; use Test::More;
use strict; use strict;
use IO::String; use IO::String;
use JSON;
use Lemonldap::NG::Portal::Main::Constants 'PE_NOTOKEN';
require 't/test-lib.pm'; require 't/test-lib.pm';
@ -42,6 +44,13 @@ ok(
count(1); count(1);
expectReject($res); expectReject($res);
my $json;
ok( $json = eval { from_json( $res->[2]->[0] ) }, 'Response is JSON' )
or print STDERR "$@\n" . Dumper($res);
ok( $json->{error} == PE_NOTOKEN, 'Response is PE_NOTOKEN' )
or explain( $json, "error => 81" );
count(2);
# Try to auth with token # Try to auth with token
$query .= '&user=dwho&password=dwho'; $query .= '&user=dwho&password=dwho';
ok( ok(

@ -76,11 +76,13 @@ $Data::Dumper::Useperl = 1;
my $ini; my $ini;
use File::Temp 'tempfile', 'tempdir'; use File::Temp 'tempfile', 'tempdir';
use File::Copy 'copy';
our $tmpDir = $LLNG::TMPDIR our $tmpDir = $LLNG::TMPDIR
|| tempdir( 'tmpSessionXXXXX', DIR => 't/sessions', CLEANUP => 1 ); || tempdir( 'tmpSessionXXXXX', DIR => 't/sessions', CLEANUP => 1 );
mkdir "$tmpDir/lock"; mkdir "$tmpDir/lock";
mkdir "$tmpDir/saml"; mkdir "$tmpDir/saml";
mkdir "$tmpDir/saml/lock"; mkdir "$tmpDir/saml/lock";
copy( "t/lmConf-1.json", "$tmpDir/lmConf-1.json" );
=head4 count($inc) =head4 count($inc)
@ -141,8 +143,7 @@ sub count_sessions {
sub getCache { sub getCache {
require Cache::FileCache; require Cache::FileCache;
return Cache::FileCache->new( return Cache::FileCache->new( {
{
namespace => 'lemonldap-ng-session', namespace => 'lemonldap-ng-session',
cache_root => $tmpDir, cache_root => $tmpDir,
cache_depth => 0, cache_depth => 0,
@ -515,7 +516,7 @@ extends 'Lemonldap::NG::Common::PSGI::Cli::Lib';
our $defaultIni = { our $defaultIni = {
configStorage => { configStorage => {
type => 'File', type => 'File',
dirName => 't', dirName => "$tmpDir",
}, },
localSessionStorage => 'Cache::FileCache', localSessionStorage => 'Cache::FileCache',
localSessionStorageOptions => { localSessionStorageOptions => {
@ -705,8 +706,7 @@ to test content I<(to launch a C<expectForm()> for example)>.
sub _get { sub _get {
my ( $self, $path, %args ) = @_; my ( $self, $path, %args ) = @_;
my $res = $self->app->( my $res = $self->app->( {
{
'HTTP_ACCEPT' => $args{accept} 'HTTP_ACCEPT' => $args{accept}
|| 'application/json, text/plain, */*', || 'application/json, text/plain, */*',
'HTTP_ACCEPT_LANGUAGE' => 'fr,fr-FR;q=0.8,en-US;q=0.5,en;q=0.3', 'HTTP_ACCEPT_LANGUAGE' => 'fr,fr-FR;q=0.8,en-US;q=0.5,en;q=0.3',
@ -758,8 +758,7 @@ sub _post {
my ( $self, $path, $body, %args ) = @_; my ( $self, $path, $body, %args ) = @_;
die "$body must be a IO::Handle" die "$body must be a IO::Handle"
unless ( ref($body) and $body->can('read') ); unless ( ref($body) and $body->can('read') );
my $res = $self->app->( my $res = $self->app->( {
{
'HTTP_ACCEPT' => $args{accept} 'HTTP_ACCEPT' => $args{accept}
|| 'application/json, text/plain, */*', || 'application/json, text/plain, */*',
'HTTP_ACCEPT_LANGUAGE' => 'fr,fr-FR;q=0.8,en-US;q=0.5,en;q=0.3', 'HTTP_ACCEPT_LANGUAGE' => 'fr,fr-FR;q=0.8,en-US;q=0.5,en;q=0.3',

Loading…
Cancel
Save