Skip to content

Commit

Permalink
WIP(test-vault): add many test and start mocking the http library
Browse files Browse the repository at this point in the history
Refs:MON-106121
  • Loading branch information
Evan-Adam committed Oct 9, 2024
1 parent e001f78 commit ee5de18
Show file tree
Hide file tree
Showing 3 changed files with 161 additions and 48 deletions.
40 changes: 20 additions & 20 deletions connectors/centreonPerlLibs/src/centreon/common/centreonvault.pm
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ sub new {
my $self = bless \%options, $class;
# mandatory options:
# - logger: logger object
# - config_file: path of a JSON vault config file
# - config_file: either path of a JSON vault config file or the configuration as a perl hash.

$self->{enabled} = 1;
$self->{crypted_credentials} = 1;
Expand All @@ -53,15 +53,19 @@ sub init {

$self->check_options() or return undef;

# check if the following information is available
$self->{logger}->writeLogDebug("Reading Vault configuration from file " . $self->{config_file} . ".");
$self->{vault_config} = parse_json_file( 'json_file' => $self->{config_file} );
if (defined($self->{vault_config}->{error_message})) {
$self->{logger}->writeLogError("Error while parsing " . $self->{config_file} . ": "
. $self->{vault_config}->{error_message});
return undef;
# for unit test purpose, if the config is given as an hash, we don't try to read the config file.
if (ref $self->{config_file} eq ref {}) {
$self->{vault_config} = $self->{config_file};
} else {
# check if the following information is available
$self->{logger}->writeLogDebug("Reading Vault configuration from file " . $self->{config_file} . ".");
$self->{vault_config} = parse_json_file('json_file' => $self->{config_file});
if (defined($self->{vault_config}->{error_message})) {
$self->{logger}->writeLogError("Error while parsing " . $self->{config_file} . ": "
. $self->{vault_config}->{error_message});
return undef;
}
}

$self->check_configuration() or return undef;

$self->{logger}->writeLogDebug("Vault configuration read. Name: " . $self->{vault_config}->{name}
Expand All @@ -84,7 +88,7 @@ sub check_options {
$self->{logger}->writeLogError("No config file given to the constructor. Centreonvault cannot be used.");
return undef;
}
if ( ! -f $self->{config_file} ) {
if ( ! -f $self->{config_file} and ref $self->{config_file} ne ref {}) {
$self->{logger}->writeLogError("The given configuration file " . $self->{config_file}
. " does not exist. Centreonvault cannot be used.");
return undef;
Expand Down Expand Up @@ -209,7 +213,7 @@ sub authenticate {
$self->{curl_easy}->setopt(CURLOPT_POST, 1);
$self->{curl_easy}->setopt(CURLOPT_POSTFIELDS, $post_data);
$self->{curl_easy}->setopt(CURLOPT_POSTFIELDSIZE, length($post_data));
$self->{curl_easy}->setopt(CURLOPT_WRITEDATA(), \$auth_result_json);
$self->{curl_easy}->setopt(CURLOPT_WRITEDATA(), \$self->{auth_result_json});

eval {
$self->{curl_easy}->perform();
Expand All @@ -221,9 +225,9 @@ sub authenticate {

$self->{logger}->writeLogInfo("Authentication to the vault passed." );

my $auth_result_obj = transform_json_to_object($auth_result_json);
my $auth_result_obj = transform_json_to_object($self->{auth_result_json});
if (defined($auth_result_obj->{error_message})) {
$self->{logger}->writeLogError("Error while decoding JSON '$auth_result_json'. Message: "
$self->{logger}->writeLogError("Error while decoding JSON '$self->{auth_result_json}'. Message: "
. $auth_result_obj->{error_message});
return undef;
}
Expand All @@ -240,7 +244,7 @@ sub authenticate {
'token' => $auth_result_obj->{auth}->{client_token},
'expiration_epoch' => $expiration_epoch
};

print("authent passed, token : $self->{auth}->{expiration_epoch}\n");
$self->{logger}->writeLogInfo("Authenticating worked. Token valid until "
. localtime($self->{auth}->{expiration_epoch}));

Expand Down Expand Up @@ -302,7 +306,7 @@ sub get_secret {
# request_id

# the result is a json string, convert it into an object
my $get_result_obj = centreon::vmware::common::transform_json_to_object($get_result_json);
my $get_result_obj = transform_json_to_object($get_result_json);
if (defined($get_result_obj->{error_message})) {
$self->{logger}->writeLogError("Error while decoding JSON '$get_result_json'. Message: "
. $get_result_obj->{error_message});
Expand Down Expand Up @@ -330,7 +334,7 @@ sub transform_json_to_object {
$json_as_object = decode_json($json_data);
};
if ($@) {
return ('error_message' => "Could not decode JSON from '$json_data'. Reason: " . $@);
return ({'error_message' => "Could not decode JSON from '$json_data'. Reason: " . $@});
};
return($json_as_object);
}
Expand All @@ -341,10 +345,6 @@ sub parse_json_file {
my $fh;
my $json_data = '';

if ( !defined($options{json_file}) ) {
return ('error_message' => "parse_json_file: json_file option is mandatory");
}

my $json_file = $options{json_file};

open($fh, '<', $json_file) or return ('error_message' => "parse_json_file: Cannot open " . $json_file);
Expand Down
162 changes: 134 additions & 28 deletions connectors/centreonPerlLibs/t/centreonvault.t
Original file line number Diff line number Diff line change
Expand Up @@ -3,41 +3,147 @@ use strict;
use warnings;
use Test2::V0;
use Test2::Plugin::NoWarnings echo => 1;
use Test2::Tools::Compare qw{is like match};
use Net::Curl::Easy qw(:constants);
use Data::Dumper;

use FindBin;
use lib qw($FindBin::RealBin/../src);
use lib "$FindBin::RealBin/../src";

use centreon::common::centreonvault;
use centreon::common::logger;
use JSON::XS;

my $vault;
my $global_logger = centreon::common::logger->new();
my @test_data = (
{'logger' => undef, 'config_file' => undef, 'test' => '$error_message =~ /FATAL: No logger given to the constructor/'},
{'logger' => $global_logger, 'config_file' => undef, 'test' => '$vault->{enabled} == 0'},
{'logger' => $global_logger, 'config_file' => 'does_not_exist.json', 'test' => '$vault->{enabled} == 0'}
);

for my $i (0..$#test_data) {
my $logger = $test_data[$i]->{logger};
my $config_file = $test_data[$i]->{config_file};
my $test = $test_data[$i]->{test};

use Data::Dumper;
#print("Test $i with logger " . Dumper($logger) ."\n");
eval {
$vault = centreon::script::centreonvault->new(
(
'logger' => $logger,
'config_file' => $config_file
)
);
};

my $error_message = defined($@) ? $@ : '';
print("Test $i with vault " . Dumper($vault) ."\n");

ok (eval($test), "TEST CASE $i FAILED: '$test' with error message: '" . $error_message . "'" );
#$global_logger->file_mode("/dev/null");
# this is an exemple of configuration for vault, the decrypted role_id/secret_id both are "String-to-encrypt"
my $default_app_secret = 'SGVsbG8gd29ybGQsIGRvZywgY2F0LCBwdXBwaWVzLgo=';
my $vault_config_hash = {
"name" => "default",
"url" => "localhost",
"port" => 443,
"root_path" => "path",
"role_id" => "4vOkzIaIJ7yxGWmysGVYY9sYHDyDM1nEv1++jSx9eAHpj83J6aIjE5SPvvpF6kBu3JeFga7o6DDS2yC7jVPAwXsWiur+KUOQncPq0JtjiFojr9YkrO8x1w1dmQFq/RqYV/S/kUare8z6r6+RnAxwsA==",
"secret_id" => "4vOkzIaIJ7yxGWmysGVYY9sYHDyDM1nEv1++jSx9eAHpj83J6aIjE5SPvvpF6kBu3JeFga7o6DDS2yC7jVPAwXsWiur+KUOQncPq0JtjiFojr9YkrO8x1w1dmQFq/RqYV/S/kUare8z6r6+RnAxwsA==",
"salt" => "U2FsdA==" }; # for now the salt is not used, it will be used to check the data where correctly decrypted.

sub test_new {
my @test_data = (
{ 'logger' => undef, 'config_file' => undef, 'test' => '$error_message =~ /FATAL: No logger given to the constructor/' },
{ 'logger' => $global_logger, 'config_file' => undef, 'test' => '$vault->{enabled} == 0' },
{ 'logger' => $global_logger, 'config_file' => 'does_not_exist.json', 'test' => '$vault->{enabled} == 0' }
);

for my $i (0 .. $#test_data) {
my $logger = $test_data[$i]->{logger};
my $config_file = $test_data[$i]->{config_file};
my $test = $test_data[$i]->{test};


#print("Test $i with logger " . Dumper($logger) ."\n");
eval {
$vault = centreon::common::centreonvault->new(
(
'logger' => $logger,
'config_file' => $config_file
)
);
};
my $error_message = defined($@) ? $@ : '';
ok(eval($test), "'$test' should be true");
}
}

sub test_decrypt {
my $old_app_secret = $ENV{'APP_SECRET'};
$ENV{'APP_SECRET'} = $default_app_secret;
$vault = centreon::common::centreonvault->new(
(
'logger' => $global_logger,
'config_file' => $vault_config_hash
)
);

is($vault->extract_and_decrypt(('data' => $vault_config_hash->{secret_id})), 'String-to-encrypt', 'extract_and_decrypt() worked');
# I encrypted the string "String-to-encrypt" from the C++ implementation, and set it to secret_id and role_id
# the key to decrypt is set as an environment variable.
# the salt can used to encrypt again the data, so the script can be sure the decryption worked correctly, but this function is not implemented yet.
$ENV{'APP_SECRET'} = $old_app_secret;
}

sub test_transform_json_to_object {
my $tests_cases = [
{
json => '{"int": 12, "string": "A String with space", "array" : ["array-key", "string"]}',
result => { "int" => 12, "string" => "A String with space", "array" => [ "array-key", "string" ] },
detail => "simple json can be decoded as a perl object"
},
{
json => '"int": 12, "string": "A String with space", "array" : ["array-key", "string"]}',
result => { "error_message" => match(qr/^Could not decode JSON from/) },
detail => "invalid json should generate an error"
},
{
json => '',
result => { "error_message" => match(qr/^Could not decode JSON from.*'. Reason:/) },
detail => "empty json"
},
{
json => 'abcdef',
result => { "error_message" => match(qr/^Could not decode JSON from/) },
detail => "simple string json"
},
{
json => '{}',
result => {},
detail => "empty json brace should make an empty object"
},
];

for my $test (@$tests_cases) {
is(centreon::common::centreonvault::transform_json_to_object($test->{json}), $test->{result}, $test->{detail});
}

}

done_testing();
sub test_authenticate {
no warnings 'prototype';
$vault = centreon::common::centreonvault->new(
(
'logger' => $global_logger,
'config_file' => $vault_config_hash
)
);
print "opt : " . CURLOPT_WRITEDATA . "\n";

my $mock = mock 'Net::Curl::Easy';
/* @TODO: we can find the link to the variable in the setopt when the opt number is the good one
we can either set the value (the effective mock action) in the setopt or in the perform, but the perform will force use to store that value somewhere temporaly.
*/
$mock->override('perform' => sub($) {
my $self = shift;
$vault->{auth_result_json} = '{"auth":{"lease_duration":"13455", "client_token":"ImAToken"}}';
},
'setopt' => sub($$$) {
if ($_[1] == 10001) {
$_[2] = '{"auth":{"lease_duration":"13455", "client_token":"ImAToken"}}';
}
print "opt : " . Dumper(@_) . "\n";
}
);

$vault->authenticate();
print $vault->{auth}->{token};
}

sub main {
#test_new();
#test_decrypt();
#test_transform_json_to_object();
test_authenticate();

done_testing();
}

&main;
7 changes: 7 additions & 0 deletions connectors/centreonPerlLibs/t/exemple-file.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"int": 12,
"string": "A String with space",
"array" : [
"array-key", "string"
]
}

0 comments on commit ee5de18

Please sign in to comment.