diff --git a/src/test/authentication/t/003_peer.pl b/src/test/authentication/t/003_peer.pl new file mode 100644 index 0000000000..006c4405a5 --- /dev/null +++ b/src/test/authentication/t/003_peer.pl @@ -0,0 +1,140 @@ + +# Copyright (c) 2022, PostgreSQL Global Development Group + +# Tests for peer authentication and user name map. +# The test is skipped if the platform does not support peer authentication. + +use strict; +use warnings; +use PostgreSQL::Test::Cluster; +use PostgreSQL::Test::Utils; +use Test::More; + +# Delete pg_hba.conf from the given node, add a new entry to it +# and then execute a reload to refresh it. +sub reset_pg_hba +{ + my $node = shift; + my $hba_method = shift; + + unlink($node->data_dir . '/pg_hba.conf'); + # just for testing purposes, use a continuation line. + $node->append_conf('pg_hba.conf', "local all all\\\n $hba_method"); + $node->reload; + return; +} + +# Test access for a single role, useful to wrap all tests into one. +sub test_role +{ + local $Test::Builder::Level = $Test::Builder::Level + 1; + + my ($node, $role, $method, $expected_res) = @_; + my $status_string = 'failed'; + $status_string = 'success' if ($expected_res eq 0); + + my $connstr = "user=$role"; + my $testname = + "authentication $status_string for method $method, role $role"; + + if ($expected_res eq 0) + { + $node->connect_ok($connstr, $testname); + } + else + { + # No checks of the error message, only the status code. + $node->connect_fails($connstr, $testname); + } +} + +# return the size of logfile of $node in bytes. +sub get_log_size +{ + my ($node) = @_; + + return (stat $node->logfile)[7]; +} + +# find $pat in logfile of $node after $off-th byte. +sub find_in_log +{ + my ($node, $pat, $off) = @_; + + $off = 0 unless defined $off; + my $log = PostgreSQL::Test::Utils::slurp_file($node->logfile); + return 0 if (length($log) <= $off); + + $log = substr($log, $off); + + return $log =~ m/$pat/; +} + +# Initialize primary node. +my $node = PostgreSQL::Test::Cluster->new('primary'); +$node->init; +$node->append_conf('postgresql.conf', "log_connections = on\n"); +$node->start; + +# Get the session_user to define the user name map test. +my $session_user = + $node->safe_psql('postgres', 'select session_user'); + +# Create a new user for the user name map test. +$node->safe_psql('postgres', + qq{CREATE USER testmap$session_user}); + +# Set pg_hba.conf with the peer authentication. +reset_pg_hba($node, 'peer'); + +# Check that peer authentication is supported on this platform. +my $logstart = get_log_size($node); + +$node->psql('postgres', undef, connstr => "user=$session_user"); + +# If not supported, then skip the rest of the test. +if (find_in_log($node, qr/peer authentication is not supported on this platform/, $logstart)) +{ + plan skip_all => 'peer authentication is not supported on this platform'; +} + +# It's supported so let's test peer authentication. +# This connection should succeed. +$logstart = get_log_size($node); +test_role($node, $session_user, 'peer', 0); + +ok( find_in_log( + $node, + qr/connection authenticated: identity="$session_user" method=peer/, $logstart), + "logfile: connection authenticated for method peer and identity $session_user"); + +# This connection should failed. +$logstart = get_log_size($node); +test_role($node, qq{testmap$session_user}, 'peer', 2); + +ok( find_in_log( + $node, + qr/Peer authentication failed for user "testmap$session_user"/, $logstart), + "logfile: Peer authentication failed for user testmap$session_user"); + +# Define a user name map. +$node->append_conf('pg_ident.conf', qq{mypeermap $session_user testmap$session_user}); + +# Set pg_hba.conf with the peer authentication and the user name map. +reset_pg_hba($node, 'peer map=mypeermap'); + +# This connection should now succeed. +$logstart = get_log_size($node); +test_role($node, qq{testmap$session_user}, 'peer', 0); + +ok( find_in_log( + $node, + qr/connection authenticated: identity="$session_user" method=peer/, $logstart), + "logfile: connection authenticated for method peer and identity $session_user"); + +ok( find_in_log( + $node, + qr/connection authorized: user=testmap$session_user/, $logstart), + "logfile: connection authorized for user testmap$session_user"); + +done_testing(); \ No newline at end of file