
# Copyright (c) 2022-2025, PostgreSQL Global Development Group

use strict;
use warnings FATAL => 'all';
use File::Copy;
use FindBin;
use PostgreSQL::Test::Utils;
use PostgreSQL::Test::Cluster;
use Test::More;

use lib "$FindBin::RealBin/../../../ldap";
use LdapServer;

if ($ENV{with_ldap} ne 'yes')
{
	plan skip_all => 'LDAP not supported by this build';
}
elsif (!$ENV{PG_TEST_EXTRA} || $ENV{PG_TEST_EXTRA} !~ /\bldap\b/)
{
	plan skip_all =>
	  'Potentially unsafe test LDAP not enabled in PG_TEST_EXTRA';
}
elsif (!$LdapServer::setup)
{
	plan skip_all =>
	  "ldap tests not supported on $^O or dependencies not installed";
}

my $clear_ldap_rootpw = "FooBaR1";
my $rot13_ldap_rootpw = "SbbOnE1";

my $ldap = LdapServer->new($clear_ldap_rootpw, 'users');   # no anonymous auth
$ldap->ldapadd_file("$FindBin::RealBin/../../../ldap/authdata.ldif");
$ldap->ldapsetpw('uid=test1,dc=example,dc=net', 'secret1');

my ($ldap_server, $ldap_port, $ldap_basedn, $ldap_rootdn) =
  $ldap->prop(qw(server port basedn rootdn));


note "setting up PostgreSQL instance";

my $node = PostgreSQL::Test::Cluster->new('node');
$node->init;
$node->append_conf('postgresql.conf',
	"log_connections = 'receipt,authentication,authorization'\n");
$node->append_conf('postgresql.conf',
	"shared_preload_libraries = 'ldap_password_func'");
$node->start;

$node->safe_psql('postgres', 'CREATE USER test1;');

note "running tests";

sub test_access
{
	local $Test::Builder::Level = $Test::Builder::Level + 1;

	my ($node, $role, $expected_res, $test_name, %params) = @_;
	my $connstr = "user=$role";

	if ($expected_res eq 0)
	{
		$node->connect_ok($connstr, $test_name, %params);
	}
	else
	{
		# No checks of the error message, only the status code.
		$node->connect_fails($connstr, $test_name, %params);
	}
}

note "use ldapbindpasswd";

$ENV{"PGPASSWORD"} = 'secret1';

unlink($node->data_dir . '/pg_hba.conf');
$node->append_conf('pg_hba.conf',
	qq{local all all ldap ldapserver=$ldap_server ldapport=$ldap_port ldapbasedn="$ldap_basedn" ldapbinddn="$ldap_rootdn" ldapbindpasswd=wrong}
);
$node->restart;

test_access($node, 'test1', 2,
	'search+bind authentication fails with wrong ldapbindpasswd');

unlink($node->data_dir . '/pg_hba.conf');
$node->append_conf('pg_hba.conf',
	qq{local all all ldap ldapserver=$ldap_server ldapport=$ldap_port ldapbasedn="$ldap_basedn" ldapbinddn="$ldap_rootdn" ldapbindpasswd="$clear_ldap_rootpw"}
);
$node->restart;

test_access($node, 'test1', 2,
	'search+bind authentication fails with clear password');

unlink($node->data_dir . '/pg_hba.conf');
$node->append_conf('pg_hba.conf',
	qq{local all all ldap ldapserver=$ldap_server ldapport=$ldap_port ldapbasedn="$ldap_basedn" ldapbinddn="$ldap_rootdn" ldapbindpasswd="$rot13_ldap_rootpw"}
);
$node->restart;

test_access($node, 'test1', 0,
	'search+bind authentication succeeds with rot13ed password');

done_testing();
