Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit ee4613d

Browse files
committed
Restructure Ldap TAP test
The code for detecting the Ldap installation and setting up a test server is broken out into a reusable module that can be used for additional tests to be added in later patches. Discussion: https://postgr.es/m/06005bfb-0fd7-9d08-e0e5-440f277b73b4@dunslane.net
1 parent b90f0b5 commit ee4613d

File tree

2 files changed

+340
-144
lines changed

2 files changed

+340
-144
lines changed

src/test/ldap/LdapServer.pm

+320
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,320 @@
1+
2+
############################################################################
3+
#
4+
# LdapServer.pm
5+
#
6+
# Module to set up an LDAP server for testing pg_hba.conf ldap authentication
7+
#
8+
# Copyright (c) 2023, PostgreSQL Global Development Group
9+
#
10+
############################################################################
11+
12+
=pod
13+
14+
=head1 NAME
15+
16+
LdapServer - class for an LDAP server for testing pg_hba.conf authentication
17+
18+
=head1 SYNOPSIS
19+
20+
use LdapServer;
21+
22+
# have we found openldap binaies suitable for setting up a server?
23+
my $ldap_binaries_found = $LdapServer::setup;
24+
25+
# create a server with the given root password and auth type
26+
# (users or anonymous)
27+
my $server = LdapServer->new($root_password, $auth_type);
28+
29+
# Add the contents of an LDIF file to the server
30+
$server->ldapadd_file ($path_to_ldif_data);
31+
32+
# set the Ldap password for a user
33+
$server->ldapsetpw($user, $password);
34+
35+
# get details of some settings for the server
36+
my @properties = $server->prop($propname1, $propname2, ...);
37+
38+
=head1 DESCRIPTION
39+
40+
LdapServer tests in its INIT phase for the presence of suitable openldap
41+
binaries. Its constructor method sets up and runs an LDAP server, and any
42+
servers that are set up are terminated during its END phase.
43+
44+
=cut
45+
46+
package LdapServer;
47+
48+
use strict;
49+
use warnings;
50+
51+
use PostgreSQL::Test::Utils;
52+
use Test::More;
53+
54+
use File::Copy;
55+
use File::Basename;
56+
57+
# private variables
58+
my ($slapd, $ldap_schema_dir, @servers);
59+
60+
# visible variable
61+
our ($setup);
62+
63+
INIT
64+
{
65+
$setup = 1;
66+
if ($^O eq 'darwin' && -d '/opt/homebrew/opt/openldap')
67+
{
68+
# typical paths for Homebrew on ARM
69+
$slapd = '/opt/homebrew/opt/openldap/libexec/slapd';
70+
$ldap_schema_dir = '/opt/homebrew/etc/openldap/schema';
71+
}
72+
elsif ($^O eq 'darwin' && -d '/usr/local/opt/openldap')
73+
{
74+
# typical paths for Homebrew on Intel
75+
$slapd = '/usr/local/opt/openldap/libexec/slapd';
76+
$ldap_schema_dir = '/usr/local/etc/openldap/schema';
77+
}
78+
elsif ($^O eq 'darwin' && -d '/opt/local/etc/openldap')
79+
{
80+
# typical paths for MacPorts
81+
$slapd = '/opt/local/libexec/slapd';
82+
$ldap_schema_dir = '/opt/local/etc/openldap/schema';
83+
}
84+
elsif ($^O eq 'linux')
85+
{
86+
$slapd = '/usr/sbin/slapd';
87+
$ldap_schema_dir = '/etc/ldap/schema' if -d '/etc/ldap/schema';
88+
$ldap_schema_dir = '/etc/openldap/schema'
89+
if -d '/etc/openldap/schema';
90+
}
91+
elsif ($^O eq 'freebsd')
92+
{
93+
$slapd = '/usr/local/libexec/slapd';
94+
$ldap_schema_dir = '/usr/local/etc/openldap/schema';
95+
}
96+
elsif ($^O eq 'openbsd')
97+
{
98+
$slapd = '/usr/local/libexec/slapd';
99+
$ldap_schema_dir = '/usr/local/share/examples/openldap/schema';
100+
}
101+
else
102+
{
103+
$setup = 0;
104+
}
105+
}
106+
107+
END
108+
{
109+
foreach my $server (@servers)
110+
{
111+
next unless -f $server->{pidfile};
112+
my $pid = slurp_file($server->{pidfile});
113+
chomp $pid;
114+
kill 'INT', $pid;
115+
}
116+
}
117+
118+
=pod
119+
120+
=head1 METHODS
121+
122+
=over
123+
124+
=item LdapServer->new($rootpw, $auth_type)
125+
126+
Create a new LDAP server.
127+
128+
The rootpw can be used when authenticating with the ldapbindpasswd option.
129+
130+
The auth_type is either 'users' or 'anonymous'.
131+
132+
=back
133+
134+
=cut
135+
136+
sub new
137+
{
138+
die "no suitable binaries found" unless $setup;
139+
140+
my $class = shift;
141+
my $rootpw = shift;
142+
my $authtype = shift; # 'users' or 'anonymous'
143+
my $testname = basename((caller)[1], '.pl');
144+
my $self = {};
145+
146+
my $test_temp = PostgreSQL::Test::Utils::tempdir("ldap-$testname");
147+
148+
my $ldap_datadir = "$test_temp/openldap-data";
149+
my $slapd_certs = "$test_temp/slapd-certs";
150+
my $slapd_pidfile = "$test_temp/slapd.pid";
151+
my $slapd_conf = "$test_temp/slapd.conf";
152+
my $slapd_logfile =
153+
"${PostgreSQL::Test::Utils::log_path}/slapd-$testname.log";
154+
my $ldap_server = 'localhost';
155+
my $ldap_port = PostgreSQL::Test::Cluster::get_free_port();
156+
my $ldaps_port = PostgreSQL::Test::Cluster::get_free_port();
157+
my $ldap_url = "ldap://$ldap_server:$ldap_port";
158+
my $ldaps_url = "ldaps://$ldap_server:$ldaps_port";
159+
my $ldap_basedn = 'dc=example,dc=net';
160+
my $ldap_rootdn = 'cn=Manager,dc=example,dc=net';
161+
my $ldap_rootpw = $rootpw;
162+
my $ldap_pwfile = "$test_temp/ldappassword";
163+
164+
(my $conf = <<"EOC") =~ s/^\t\t//gm;
165+
include $ldap_schema_dir/core.schema
166+
include $ldap_schema_dir/cosine.schema
167+
include $ldap_schema_dir/nis.schema
168+
include $ldap_schema_dir/inetorgperson.schema
169+
170+
pidfile $slapd_pidfile
171+
logfile $slapd_logfile
172+
173+
access to *
174+
by * read
175+
by $authtype auth
176+
177+
database ldif
178+
directory $ldap_datadir
179+
180+
TLSCACertificateFile $slapd_certs/ca.crt
181+
TLSCertificateFile $slapd_certs/server.crt
182+
TLSCertificateKeyFile $slapd_certs/server.key
183+
184+
suffix "dc=example,dc=net"
185+
rootdn "$ldap_rootdn"
186+
rootpw "$ldap_rootpw"
187+
EOC
188+
append_to_file($slapd_conf, $conf);
189+
190+
mkdir $ldap_datadir or die "making $ldap_datadir: $!";
191+
mkdir $slapd_certs or die "making $slapd_certs: $!";
192+
193+
my $certdir = dirname(__FILE__) . "/../ssl/ssl";
194+
195+
copy "$certdir/server_ca.crt", "$slapd_certs/ca.crt"
196+
|| die "copying ca.crt: $!";
197+
# check we actually have the file, as copy() sometimes gives a false success
198+
-f "$slapd_certs/ca.crt" || die "copying ca.crt (error unknown)";
199+
copy "$certdir/server-cn-only.crt", "$slapd_certs/server.crt"
200+
|| die "copying server.crt: $!";
201+
copy "$certdir/server-cn-only.key", "$slapd_certs/server.key"
202+
|| die "copying server.key: $!";
203+
204+
append_to_file($ldap_pwfile, $ldap_rootpw);
205+
chmod 0600, $ldap_pwfile or die "chmod on $ldap_pwfile";
206+
207+
system_or_bail $slapd, '-f', $slapd_conf, '-h', "$ldap_url $ldaps_url";
208+
209+
# wait until slapd accepts requests
210+
my $retries = 0;
211+
while (1)
212+
{
213+
last
214+
if (
215+
system_log(
216+
"ldapsearch", "-sbase",
217+
"-H", $ldap_url,
218+
"-b", $ldap_basedn,
219+
"-D", $ldap_rootdn,
220+
"-y", $ldap_pwfile,
221+
"-n", "'objectclass=*'") == 0);
222+
die "cannot connect to slapd" if ++$retries >= 300;
223+
note "waiting for slapd to accept requests...";
224+
Time::HiRes::usleep(1000000);
225+
}
226+
227+
$self->{pidfile} = $slapd_pidfile;
228+
$self->{pwfile} = $ldap_pwfile;
229+
$self->{url} = $ldap_url;
230+
$self->{s_url} = $ldaps_url;
231+
$self->{server} = $ldap_server;
232+
$self->{port} = $ldap_port;
233+
$self->{s_port} = $ldaps_port;
234+
$self->{basedn} = $ldap_basedn;
235+
$self->{rootdn} = $ldap_rootdn;
236+
237+
bless $self, $class;
238+
push @servers, $self;
239+
return $self;
240+
}
241+
242+
# private routine to set up the environment for methods below
243+
sub _ldapenv
244+
{
245+
my $self = shift;
246+
my %env = %ENV;
247+
$env{'LDAPURI'} = $self->{url};
248+
$env{'LDAPBINDDN'} = $self->{rootdn};
249+
return %env;
250+
}
251+
252+
=pod
253+
254+
=over
255+
256+
=item ldap_add(filename)
257+
258+
filename is the path to a file containing LDIF data which is added to the LDAP
259+
server.
260+
261+
=back
262+
263+
=cut
264+
265+
sub ldapadd_file
266+
{
267+
my $self = shift;
268+
my $file = shift;
269+
270+
local %ENV = $self->_ldapenv;
271+
272+
system_or_bail 'ldapadd', '-x', '-y', $self->{pwfile}, '-f', $file;
273+
}
274+
275+
=pod
276+
277+
=over
278+
279+
=item ldapsetpw(user, password)
280+
281+
Set the user's password in the LDAP server
282+
283+
=back
284+
285+
=cut
286+
287+
sub ldapsetpw
288+
{
289+
my $self = shift;
290+
my $user = shift;
291+
my $password = shift;
292+
293+
local %ENV = $self->_ldapenv;
294+
295+
system_or_bail 'ldappasswd', '-x', '-y', $self->{pwfile}, '-s', $password,
296+
$user;
297+
}
298+
299+
=pod
300+
301+
=over
302+
303+
=item prop(name1, ...)
304+
305+
Returns the list of values for the specified properties of the instance, such
306+
as 'url', 'port', 'basedn'.
307+
308+
=back
309+
310+
=cut
311+
312+
sub prop
313+
{
314+
my $self = shift;
315+
my @settings;
316+
push @settings, $self->{$_} foreach (@_);
317+
return @settings;
318+
}
319+
320+
1;

0 commit comments

Comments
 (0)