mirror of
https://github.com/xcat2/xcat-core.git
synced 2026-09-10 19:46:24 +00:00
259 lines
9.3 KiB
Perl
259 lines
9.3 KiB
Perl
#!/usr/bin/env perl
|
|
## no critic (TestingAndDebugging::ProhibitNoStrict)
|
|
use strict;
|
|
use warnings;
|
|
|
|
use File::Spec;
|
|
use FindBin;
|
|
use Test::More;
|
|
|
|
BEGIN {
|
|
package xCAT::Table;
|
|
our %rows;
|
|
our @opened;
|
|
sub import { }
|
|
sub new {
|
|
my ($class, $table, @options) = @_;
|
|
push @opened, [ $table, @options ];
|
|
return bless { table => $table }, $class;
|
|
}
|
|
sub getAttribs {
|
|
my ($self, $key, @columns) = @_;
|
|
foreach my $row ( @{ $rows{ $self->{table} } || [] } ) {
|
|
next if grep { !defined($row->{$_}) or $row->{$_} ne $key->{$_} } keys %$key;
|
|
return { map { $_ => $row->{$_} } @columns };
|
|
}
|
|
return;
|
|
}
|
|
sub getNodeAttribs {
|
|
my ($self, $node, $columns) = @_;
|
|
return $self->getAttribs({ node => $node }, @$columns);
|
|
}
|
|
sub close { }
|
|
$INC{'xCAT/Table.pm'} = 1;
|
|
|
|
package xCAT::NodeRange;
|
|
sub import {
|
|
no strict 'refs';
|
|
*{ caller() . '::noderange' } = \&noderange;
|
|
}
|
|
sub noderange { return ( $_[0] ); }
|
|
$INC{'xCAT/NodeRange.pm'} = 1;
|
|
|
|
package xCAT::Zone;
|
|
sub import { }
|
|
$INC{'xCAT/Zone.pm'} = 1;
|
|
|
|
package xCAT::Utils;
|
|
sub import { }
|
|
sub isAIX { return 0; }
|
|
sub isServiceNode { return 0; }
|
|
$INC{'xCAT/Utils.pm'} = 1;
|
|
|
|
package xCAT::NetworkUtils;
|
|
sub import { }
|
|
sub getipaddr { return (); }
|
|
$INC{'xCAT/NetworkUtils.pm'} = 1;
|
|
|
|
package xCAT::PasswordUtils;
|
|
our @calls;
|
|
sub import { }
|
|
sub crypt_system_password {
|
|
my ( $table, $key, $fields ) = @_;
|
|
$key ||= { key => 'system', username => 'root' };
|
|
push @calls, [ $table, {%$key}, $fields ? [@$fields] : undef ];
|
|
return '$6$salt$' . $key->{username};
|
|
}
|
|
$INC{'xCAT/PasswordUtils.pm'} = 1;
|
|
|
|
package xCAT::TableUtils;
|
|
sub import { }
|
|
sub get_site_attribute { return ('192.0.2.10'); }
|
|
$INC{'xCAT/TableUtils.pm'} = 1;
|
|
|
|
package xCAT::MsgUtils;
|
|
our @traces;
|
|
sub import { }
|
|
sub trace { push @traces, [@_]; }
|
|
sub message {
|
|
my ( $class, $type, $rsp, $callback ) = @_;
|
|
$callback->($rsp) if ref($callback) eq 'CODE';
|
|
}
|
|
$INC{'xCAT/MsgUtils.pm'} = 1;
|
|
|
|
package LWP;
|
|
sub import { }
|
|
$INC{'LWP.pm'} = 1;
|
|
|
|
package LWP::UserAgent;
|
|
sub new { return bless {}, shift; }
|
|
|
|
package HTTP::Request::Common;
|
|
sub import {
|
|
no strict 'refs';
|
|
*{ caller() . '::GET' } = sub { return $_[0]; };
|
|
}
|
|
$INC{'HTTP/Request/Common.pm'} = 1;
|
|
}
|
|
|
|
my $repo_root = File::Spec->catdir( $FindBin::Bin, '..', '..' );
|
|
my $plugin = File::Spec->catfile(
|
|
$repo_root, qw(xCAT-server lib xcat plugins credentials.pm)
|
|
);
|
|
require $plugin;
|
|
|
|
# The node callback on port 300 is the only part that needs a live node.
|
|
{
|
|
no warnings qw(redefine once);
|
|
*xCAT_plugin::credentials::ok_with_node = sub { return 1; };
|
|
}
|
|
|
|
sub request_hash {
|
|
my ( $node, $parameter ) = @_;
|
|
my @responses;
|
|
@xCAT::PasswordUtils::calls = ();
|
|
@xCAT::Table::opened = ();
|
|
@xCAT::MsgUtils::traces = ();
|
|
xCAT_plugin::credentials::process_request(
|
|
{
|
|
command => ['getcredentials'],
|
|
arg => [$parameter],
|
|
_xcat_clienthost => [$node],
|
|
callback_port => [300],
|
|
},
|
|
sub { push @responses, @_; }
|
|
);
|
|
return \@responses;
|
|
}
|
|
|
|
sub served_hash {
|
|
my ($responses) = @_;
|
|
my ($data) = grep { $_->{data} } @$responses;
|
|
return unless $data;
|
|
return ( $data->{data}->[0]->{content}->[0], $data->{data}->[0]->{desc}->[0] );
|
|
}
|
|
|
|
sub served_error {
|
|
my ($responses) = @_;
|
|
my ($error) = grep { $_->{error} } @$responses;
|
|
return unless $error;
|
|
return $error->{error}->[0];
|
|
}
|
|
|
|
%xCAT::Table::rows = (
|
|
passwd => [
|
|
{ key => 'system', username => 'root', password => 'rootpw', cryptmethod => undef },
|
|
{ key => 'system', username => 'xcat', password => 'sudoerpw', cryptmethod => 'sha512' },
|
|
{ key => 'system', username => 'ops', password => 'opspw', cryptmethod => undef },
|
|
{ key => 'system', username => 'img', password => 'imgpw', cryptmethod => undef },
|
|
{ key => 'system', username => 'nopw', password => undef, cryptmethod => undef },
|
|
],
|
|
postscripts => [
|
|
{ node => 'xcatdefaults', postscripts => 'syslog,remoteshell', postbootscripts => 'otherpkgs' },
|
|
{ node => 'compute-01', postscripts => 'sudoer,confignetwork', postbootscripts => undef },
|
|
{ node => 'compute-02', postscripts => undef, postbootscripts => 'sudoer -u ops' },
|
|
{ node => 'compute-03', postscripts => 'confignetwork', postbootscripts => undef },
|
|
{ node => 'compute-04', postscripts => undef, postbootscripts => undef },
|
|
{ node => 'compute-05', postscripts => 'sudoer -unopw', postbootscripts => undef },
|
|
],
|
|
nodetype => [
|
|
{ node => 'compute-01', provmethod => 'install' },
|
|
{ node => 'compute-04', provmethod => 'alma9-x86_64-install-compute' },
|
|
],
|
|
osimage => [
|
|
{ imagename => 'alma9-x86_64-install-compute', postscripts => 'sudoer -u img', postbootscripts => undef },
|
|
],
|
|
);
|
|
|
|
{
|
|
my ( $hash, $desc ) = served_hash( request_hash( 'compute-03', 'xcat_secure_pw:root' ) );
|
|
is( $hash, '$6$salt$root', 'root hash is served without a sudoer entry' );
|
|
is( $desc, 'xcat_secure_pw:root', 'the reply names the requested credential' );
|
|
is_deeply(
|
|
$xCAT::PasswordUtils::calls[0],
|
|
[ 'passwd', { key => 'system', username => 'root' }, [ 'password', 'cryptmethod' ] ],
|
|
'root is hashed from the passwd row key=system,username=root'
|
|
);
|
|
}
|
|
|
|
{
|
|
my ( $hash, $desc ) = served_hash( request_hash( 'compute-01', 'xcat_secure_pw:xcat' ) );
|
|
is( $hash, '$6$salt$xcat', 'the default sudoer of a node with the sudoer postscript gets its hash' );
|
|
is( $desc, 'xcat_secure_pw:xcat', 'the reply names the sudoer credential' );
|
|
is_deeply(
|
|
$xCAT::PasswordUtils::calls[0],
|
|
[ 'passwd', { key => 'system', username => 'xcat' }, [ 'password', 'cryptmethod' ] ],
|
|
'the sudoer is hashed from the passwd row key=system,username=xcat'
|
|
);
|
|
}
|
|
|
|
{
|
|
my $responses = request_hash( 'compute-01', 'xcat_secure_pw:ops' );
|
|
ok( !defined( ( served_hash($responses) )[0] ), 'a user that is not the configured sudoer gets no hash' );
|
|
like( served_error($responses), qr/ops is not a configured sudoer of compute-01/,
|
|
'the reply says the user is not configured for the node' );
|
|
is( scalar @xCAT::PasswordUtils::calls, 0, 'nothing is hashed for an unconfigured user' );
|
|
}
|
|
|
|
{
|
|
my ( $hash ) = served_hash( request_hash( 'compute-02', 'xcat_secure_pw:ops' ) );
|
|
is( $hash, '$6$salt$ops', 'a sudoer named with -u in postbootscripts gets its hash' );
|
|
my $responses = request_hash( 'compute-02', 'xcat_secure_pw:xcat' );
|
|
like( served_error($responses), qr/xcat is not a configured sudoer/,
|
|
'the default name is not served when the node names another sudoer' );
|
|
}
|
|
|
|
{
|
|
my $responses = request_hash( 'compute-03', 'xcat_secure_pw:xcat' );
|
|
ok( !defined( ( served_hash($responses) )[0] ), 'a node without the sudoer postscript gets no hash' );
|
|
like( served_error($responses), qr/not a configured sudoer of compute-03/,
|
|
'the reply names the node without the postscript' );
|
|
}
|
|
|
|
{
|
|
my ( $hash ) = served_hash( request_hash( 'compute-04', 'xcat_secure_pw:img' ) );
|
|
is( $hash, '$6$salt$img', 'a sudoer configured on the osimage of provmethod gets its hash' );
|
|
}
|
|
|
|
{
|
|
local $xCAT::Table::rows{postscripts}->[0]->{postbootscripts} = 'otherpkgs,sudoer -u ops';
|
|
my ( $hash ) = served_hash( request_hash( 'compute-03', 'xcat_secure_pw:ops' ) );
|
|
is( $hash, '$6$salt$ops', 'a sudoer configured in xcatdefaults applies to every node' );
|
|
}
|
|
|
|
{
|
|
my ( $field, $desc ) = served_hash( request_hash( 'compute-05', 'xcat_secure_pw:nopw' ) );
|
|
is( $field, '!', 'a sudoer row without a password gets the locked field' );
|
|
is( $desc, 'xcat_secure_pw:nopw', 'the locked reply names the sudoer credential' );
|
|
is( scalar @xCAT::PasswordUtils::calls, 0, 'nothing is hashed for an empty password' );
|
|
}
|
|
|
|
{
|
|
local $xCAT::Table::rows{passwd} = [ grep { $_->{username} ne 'xcat' } @{ $xCAT::Table::rows{passwd} } ];
|
|
my ( $field ) = served_hash( request_hash( 'compute-01', 'xcat_secure_pw:xcat' ) );
|
|
is( $field, '!', 'a sudoer without a passwd row gets the locked field' );
|
|
}
|
|
|
|
{
|
|
local $xCAT::Table::rows{passwd} = [ grep { $_->{username} ne 'root' } @{ $xCAT::Table::rows{passwd} } ];
|
|
my $responses = request_hash( 'compute-01', 'xcat_secure_pw:root' );
|
|
ok( !defined( ( served_hash($responses) )[0] ), 'root without a passwd row is not locked' );
|
|
like( served_error($responses), qr/no password in the passwd table for root/,
|
|
'root without a passwd row answers with an error' );
|
|
}
|
|
|
|
{
|
|
my $responses = request_hash( 'compute-01', 'xcat_secure_pw:../root' );
|
|
ok( !defined( ( served_hash($responses) )[0] ), 'an invalid user name gets no hash' );
|
|
like( served_error($responses), qr/invalid user name/, 'the reply carries an error for an invalid name' );
|
|
is( scalar @xCAT::Table::opened, 0, 'an invalid user name never reaches a table' );
|
|
}
|
|
|
|
{
|
|
my $responses = request_hash( 'compute-01', 'xcat_secure_pw:' );
|
|
ok( !defined( ( served_hash($responses) )[0] ), 'a missing user name gets no hash' );
|
|
is( scalar @xCAT::Table::opened, 0, 'a missing user name never reaches a table' );
|
|
}
|
|
|
|
done_testing();
|