public inbox for [email protected]help / color / mirror / Atom feed
[PATCH v32 05/11] Add Incremental View Maintenance support to psql 6+ messages / 3 participants [nested] [flat]
* [PATCH v32 05/11] Add Incremental View Maintenance support to psql @ 2019-12-20 01:21 Yugo Nagata <[email protected]> 0 siblings, 0 replies; 6+ messages in thread From: Yugo Nagata @ 2019-12-20 01:21 UTC (permalink / raw) Add tab completion and meta-command output for IVM. --- src/bin/psql/describe.c | 32 +++++++++++++++++++++++++++++++- src/bin/psql/tab-complete.c | 14 +++++++++----- 2 files changed, 40 insertions(+), 6 deletions(-) diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 6433497bcd..df559dce42 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -1574,6 +1574,7 @@ describeOneTableDetails(const char *schemaname, char relpersistence; char relreplident; char *relam; + bool isivm; } tableinfo; bool show_column_details = false; @@ -1586,7 +1587,26 @@ describeOneTableDetails(const char *schemaname, initPQExpBuffer(&tmpbuf); /* Get general table info */ - if (pset.sversion >= 120000) + if (pset.sversion >= 170000) + { + printfPQExpBuffer(&buf, + "SELECT c.relchecks, c.relkind, c.relhasindex, c.relhasrules, " + "c.relhastriggers, c.relrowsecurity, c.relforcerowsecurity, " + "false AS relhasoids, c.relispartition, %s, c.reltablespace, " + "CASE WHEN c.reloftype = 0 THEN '' ELSE c.reloftype::pg_catalog.regtype::pg_catalog.text END, " + "c.relpersistence, c.relreplident, am.amname, " + "c.relisivm\n" + "FROM pg_catalog.pg_class c\n " + "LEFT JOIN pg_catalog.pg_class tc ON (c.reltoastrelid = tc.oid)\n" + "LEFT JOIN pg_catalog.pg_am am ON (c.relam = am.oid)\n" + "WHERE c.oid = '%s';", + (verbose ? + "pg_catalog.array_to_string(c.reloptions || " + "array(select 'toast.' || x from pg_catalog.unnest(tc.reloptions) x), ', ')\n" + : "''"), + oid); + } + else if (pset.sversion >= 120000) { printfPQExpBuffer(&buf, "SELECT c.relchecks, c.relkind, c.relhasindex, c.relhasrules, " @@ -1706,6 +1726,10 @@ describeOneTableDetails(const char *schemaname, (char *) NULL : pg_strdup(PQgetvalue(res, 0, 14)); else tableinfo.relam = NULL; + if (pset.sversion >= 170000) + tableinfo.isivm = strcmp(PQgetvalue(res, 0, 15), "t") == 0; + else + tableinfo.isivm = false; PQclear(res); res = NULL; @@ -3560,6 +3584,12 @@ describeOneTableDetails(const char *schemaname, printfPQExpBuffer(&buf, _("Access method: %s"), tableinfo.relam); printTableAddFooter(&cont, buf.data); } + + /* Incremental view maintance info */ + if (verbose && tableinfo.relkind == RELKIND_MATVIEW && tableinfo.isivm) + { + printTableAddFooter(&cont, _("Incremental view maintenance: yes")); + } } /* reloptions, if verbose */ diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index fc6865fc70..456d042f09 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -1245,6 +1245,7 @@ static const pgsql_thing_t words_after_create[] = { {"FOREIGN TABLE", NULL, NULL, NULL}, {"FUNCTION", NULL, NULL, Query_for_list_of_functions}, {"GROUP", Query_for_list_of_roles}, + {"INCREMENTAL MATERIALIZED VIEW", NULL, NULL, &Query_for_list_of_matviews, NULL, THING_NO_DROP | THING_NO_ALTER}, {"INDEX", NULL, NULL, &Query_for_list_of_indexes}, {"LANGUAGE", Query_for_list_of_languages}, {"LARGE OBJECT", NULL, NULL, NULL, NULL, THING_NO_CREATE | THING_NO_DROP}, @@ -3256,7 +3257,7 @@ psql_completion(const char *text, int start, int end) if (HeadMatches("CREATE", "SCHEMA")) COMPLETE_WITH("TABLE", "SEQUENCE"); else - COMPLETE_WITH("TABLE", "SEQUENCE", "MATERIALIZED VIEW"); + COMPLETE_WITH("TABLE", "SEQUENCE", "MATERIALIZED VIEW", "INCREMENTAL MATERIALIZED VIEW"); } /* Complete PARTITION BY with RANGE ( or LIST ( or ... */ else if (TailMatches("PARTITION", "BY")) @@ -3601,13 +3602,16 @@ psql_completion(const char *text, int start, int end) COMPLETE_WITH("SELECT"); /* CREATE MATERIALIZED VIEW */ - else if (Matches("CREATE", "MATERIALIZED")) + else if (Matches("CREATE", "MATERIALIZED") || + Matches("CREATE", "INCREMENTAL", "MATERIALIZED")) COMPLETE_WITH("VIEW"); - /* Complete CREATE MATERIALIZED VIEW <name> with AS */ - else if (Matches("CREATE", "MATERIALIZED", "VIEW", MatchAny)) + /* Complete CREATE MATERIALIZED VIEW <name> with AS */ + else if (Matches("CREATE", "MATERIALIZED", "VIEW", MatchAny) || + Matches("CREATE", "INCREMENTAL", "MATERIALIZED", "VIEW", MatchAny)) COMPLETE_WITH("AS"); /* Complete "CREATE MATERIALIZED VIEW <sth> AS with "SELECT" */ - else if (Matches("CREATE", "MATERIALIZED", "VIEW", MatchAny, "AS")) + else if (Matches("CREATE", "MATERIALIZED", "VIEW", MatchAny, "AS") || + Matches("CREATE", "INCREMENTAL", "MATERIALIZED", "VIEW", MatchAny, "AS")) COMPLETE_WITH("SELECT"); /* CREATE EVENT TRIGGER */ -- 2.25.1 --Multipart=_Sun__31_Mar_2024_22_59_31_+0900_msknEviJj08_wgqO Content-Type: text/x-diff; name="v32-0006-Add-Incremental-View-Maintenance-support.patch" Content-Disposition: attachment; filename="v32-0006-Add-Incremental-View-Maintenance-support.patch" Content-Transfer-Encoding: 7bit ^ permalink raw reply [nested|flat] 6+ messages in thread
* Re: Add a test to ldapbindpasswd @ 2023-01-02 14:45 Andrew Dunstan <[email protected]> 0 siblings, 2 replies; 6+ messages in thread From: Andrew Dunstan @ 2023-01-02 14:45 UTC (permalink / raw) To: Thomas Munro <[email protected]>; +Cc: PostgreSQL Hackers <[email protected]> On 2023-01-01 Su 18:31, Andrew Dunstan wrote: > On 2023-01-01 Su 14:02, Thomas Munro wrote: >> On Mon, Jan 2, 2023 at 3:04 AM Andrew Dunstan <[email protected]> wrote: >>> On 2022-12-19 Mo 11:16, Andrew Dunstan wrote: >>>> There is currently no test for the use of ldapbindpasswd in the >>>> pg_hba.conf file. This patch, mostly the work of John Naylor, remedies that. >>>> >>>> >>> This currently has failures on the cfbot for meson builds on FBSD13 and >>> Debian Bullseye, but it's not at all clear why. In both cases it fails >>> where the ldap server is started. >> I think it's failing when using meson. I guess it fails to fail on >> macOS only because you need to add a new path for Homebrew/ARM like >> commit 14d63dd2, so it's skipping (it'd be nice if we didn't need >> another copy of all that logic). Trying locally... it looks like >> slapd is failing silently, and with some tracing I can see it's >> sending an error message to my syslog daemon, which logged: >> >> 2023-01-02T07:50:20.853019+13:00 x1 slapd[153599]: main: TLS init def >> ctx failed: -1 >> >> Ah, it looks like this test is relying on "slapd-certs", which doesn't exist: >> >> tmunro@x1:~/projects/postgresql/build$ ls testrun/ldap/001_auth/data/ >> ldap.conf ldappassword openldap-data portlock slapd-certs slapd.conf >> tmunro@x1:~/projects/postgresql/build$ ls testrun/ldap/002_bindpasswd/data/ >> portlock slapd.conf >> >> I didn't look closely, but apparently there is something wrong in the >> part that copies certs from the ssl test? Not sure why it works for >> autoconf... > > > Let's see how we fare with this patch. > > Not so well :-(. This version tries to make the tests totally independent, as they should be. That's an attempt to get the cfbot to go green, but I am intending to refactor this code substantially so the common bits are in a module each test file will load. cheers andrew -- Andrew Dunstan EDB: https://www.enterprisedb.com Attachments: [text/x-patch] 0001-Add-a-test-for-ldapbindpasswd-v3.patch (7.5K, ../../[email protected]/2-0001-Add-a-test-for-ldapbindpasswd-v3.patch) download | inline diff: From c2bedfd8a5b326ffb563da49b7b4b4006ddac361 Mon Sep 17 00:00:00 2001 From: Andrew Dunstan <[email protected]> Date: Mon, 2 Jan 2023 09:41:06 -0500 Subject: [PATCH] Add a test for ldapbindpasswd The existing LDAP tests don't cover the use of ldapbindpasswd in pg_hba.conf, so remedy that. Authors: John Naylor and Andrew Dunstan --- src/test/ldap/meson.build | 1 + src/test/ldap/t/002_bindpasswd.pl | 223 ++++++++++++++++++++++++++++++ 2 files changed, 224 insertions(+) create mode 100644 src/test/ldap/t/002_bindpasswd.pl diff --git a/src/test/ldap/meson.build b/src/test/ldap/meson.build index 90d88138e7..7628a9c7c6 100644 --- a/src/test/ldap/meson.build +++ b/src/test/ldap/meson.build @@ -7,6 +7,7 @@ tests += { 'tap': { 'tests': [ 't/001_auth.pl', + 't/002_bindpasswd.pl', ], 'env': { 'with_ldap': ldap.found() ? 'yes' : 'no', diff --git a/src/test/ldap/t/002_bindpasswd.pl b/src/test/ldap/t/002_bindpasswd.pl new file mode 100644 index 0000000000..330a2b7dad --- /dev/null +++ b/src/test/ldap/t/002_bindpasswd.pl @@ -0,0 +1,223 @@ + +# Copyright (c) 2022, PostgreSQL Global Development Group + +use strict; +use warnings; +use File::Copy; +use File::Basename; +use PostgreSQL::Test::Utils; +use PostgreSQL::Test::Cluster; +use Test::More; + + +my ($slapd, $ldap_bin_dir, $ldap_schema_dir); + +$ldap_bin_dir = undef; # usually in PATH + +if ($ENV{with_ldap} ne 'yes') +{ + plan skip_all => 'LDAP not supported by this build'; +} +elsif ($ENV{PG_TEST_EXTRA} !~ /\bldap\b/) +{ + plan skip_all => 'Potentially unsafe test LDAP not enabled in PG_TEST_EXTRA'; +} +elsif ($^O eq 'darwin' && -d '/opt/homebrew/opt/openldap') +{ + # typical paths for Homebrew on ARM + $slapd = '/opt/homebrew/opt/openldap/libexec/slapd'; + $ldap_schema_dir = '/opt/homebrew/etc/openldap/schema'; +} +elsif ($^O eq 'darwin' && -d '/usr/local/opt/openldap') +{ + # typical paths for Homebrew on Intel + $slapd = '/usr/local/opt/openldap/libexec/slapd'; + $ldap_schema_dir = '/usr/local/etc/openldap/schema'; +} +elsif ($^O eq 'darwin' && -d '/opt/local/etc/openldap') +{ + # typical paths for MacPorts + $slapd = '/opt/local/libexec/slapd'; + $ldap_schema_dir = '/opt/local/etc/openldap/schema'; +} +elsif ($^O eq 'linux') +{ + $slapd = '/usr/sbin/slapd'; + $ldap_schema_dir = '/etc/ldap/schema' if -d '/etc/ldap/schema'; + $ldap_schema_dir = '/etc/openldap/schema' if -d '/etc/openldap/schema'; +} +elsif ($^O eq 'freebsd') +{ + $slapd = '/usr/local/libexec/slapd'; + $ldap_schema_dir = '/usr/local/etc/openldap/schema'; +} +elsif ($^O eq 'openbsd') +{ + $slapd = '/usr/local/libexec/slapd'; + $ldap_schema_dir = '/usr/local/share/examples/openldap/schema'; +} +else +{ + plan skip_all => + "ldap tests not supported on $^O or dependencies not installed"; +} + +# make your own edits here +#$slapd = ''; +#$ldap_bin_dir = ''; +#$ldap_schema_dir = ''; + +$ENV{PATH} = "$ldap_bin_dir:$ENV{PATH}" if $ldap_bin_dir; + +my $tst_name = basename(__FILE__,'.pl'); +my $test_temp = PostgreSQL::Test::Utils::tempdir("ldap-$tst_name"); + +my $ldap_datadir = "$test_temp/openldap-data"; +my $slapd_certs = "$test_temp/slapd-certs"; +my $slapd_conf = "$test_temp/slapd.conf"; +my $slapd_pidfile = "$test_temp/slapd.pid"; +my $slapd_logfile = "${PostgreSQL::Test::Utils::log_path}/slapd-2.log"; +my $ldap_conf = "$test_temp/ldap.conf"; +my $ldap_server = 'localhost'; +my $ldap_port = PostgreSQL::Test::Cluster::get_free_port(); +my $ldaps_port = PostgreSQL::Test::Cluster::get_free_port(); +my $ldap_url = "ldap://$ldap_server:$ldap_port"; +my $ldaps_url = "ldaps://$ldap_server:$ldaps_port"; +my $ldap_basedn = 'dc=example,dc=net'; +my $ldap_rootdn = 'cn=Manager,dc=example,dc=net'; +my $ldap_rootpw = 'secret'; +my $ldap_pwfile = "$test_temp/ldappassword"; + +note "setting up slapd"; + +# need to create new config without anonymous auth +unlink $slapd_conf; + +append_to_file( + $slapd_conf, + qq{include $ldap_schema_dir/core.schema +include $ldap_schema_dir/cosine.schema +include $ldap_schema_dir/nis.schema +include $ldap_schema_dir/inetorgperson.schema + +pidfile $slapd_pidfile +logfile $slapd_logfile + +access to * + by * read + by users auth + +database ldif +directory $ldap_datadir + +TLSCACertificateFile $slapd_certs/ca.crt +TLSCertificateFile $slapd_certs/server.crt +TLSCertificateKeyFile $slapd_certs/server.key + +suffix "dc=example,dc=net" +rootdn "$ldap_rootdn" +rootpw $ldap_rootpw}); + +mkdir $ldap_datadir or die "making $ldap_datadir: $!"; +mkdir $slapd_certs or die "making $slapd_certs: $!"; + +copy "../ssl/ssl/server_ca.crt", "$slapd_certs/ca.crt" + || die "copying ca.crt: $!"; +copy "../ssl/ssl/server-cn-only.crt", "$slapd_certs/server.crt" + || die "copying server.crt: $!";; +copy "../ssl/ssl/server-cn-only.key", "$slapd_certs/server.key" + || die "copying server.key: $!";; + +system_or_bail $slapd, '-f', $slapd_conf, '-h', "$ldap_url $ldaps_url"; + +END +{ + kill 'INT', `cat $slapd_pidfile` if -f $slapd_pidfile; +} + +append_to_file($ldap_pwfile, $ldap_rootpw); +chmod 0600, $ldap_pwfile or die; + +# wait until slapd accepts requests +my $retries = 0; +while (1) +{ + last + if ( + system_log( + "ldapsearch", "-sbase", + "-H", $ldap_url, + "-b", $ldap_basedn, + "-D", $ldap_rootdn, + "-y", $ldap_pwfile, + "-n", "'objectclass=*'") == 0); + die "cannot connect to slapd" if ++$retries >= 300; + note "waiting for slapd to accept requests..."; + Time::HiRes::usleep(1000000); +} + +$ENV{'LDAPURI'} = $ldap_url; +$ENV{'LDAPBINDDN'} = $ldap_rootdn; +$ENV{'LDAPCONF'} = $ldap_conf; + +# use same data as 001 test + +note "loading LDAP data"; + +system_or_bail 'ldapadd', '-x', '-y', $ldap_pwfile, '-f', 'authdata.ldif'; +system_or_bail 'ldappasswd', '-x', '-y', $ldap_pwfile, '-s', 'secret1', + 'uid=test1,dc=example,dc=net'; +system_or_bail 'ldappasswd', '-x', '-y', $ldap_pwfile, '-s', 'secret2', + 'uid=test2,dc=example,dc=net'; + +note "setting up PostgreSQL instance"; + +my $node = PostgreSQL::Test::Cluster->new('node'); +$node->init; +$node->append_conf('postgresql.conf', "log_connections = on\n"); +$node->start; + +$node->safe_psql('postgres', 'CREATE USER test0;'); +$node->safe_psql('postgres', 'CREATE USER test1;'); +$node->safe_psql('postgres', 'CREATE USER "[email protected]";'); + +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"; + +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; + +$ENV{"PGPASSWORD"} = 'secret1'; +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="$ldap_rootpw"} +); +$node->restart; + +test_access($node, 'test1', 0, 'search+bind authentication succeeds with ldapbindpasswd'); + +done_testing(); -- 2.34.1 ^ permalink raw reply [nested|flat] 6+ messages in thread
* Re: Add a test to ldapbindpasswd @ 2023-01-03 05:48 Julien Rouhaud <[email protected]> parent: Andrew Dunstan <[email protected]> 1 sibling, 0 replies; 6+ messages in thread From: Julien Rouhaud @ 2023-01-03 05:48 UTC (permalink / raw) To: Andrew Dunstan <[email protected]>; +Cc: Thomas Munro <[email protected]>; PostgreSQL Hackers <[email protected]> Hi, On Mon, Jan 02, 2023 at 09:45:27AM -0500, Andrew Dunstan wrote: > > On 2023-01-01 Su 18:31, Andrew Dunstan wrote: > > Let's see how we fare with this patch. > > > > > > Not so well :-(. This version tries to make the tests totally > independent, as they should be. That's an attempt to get the cfbot to go > green, but I am intending to refactor this code substantially so the > common bits are in a module each test file will load. FTR you can run the same set of CI tests using your own GH account rather than sedning patches, see src/tools/ci/README/ ^ permalink raw reply [nested|flat] 6+ messages in thread
* Re: Add a test to ldapbindpasswd @ 2023-01-04 21:26 Andrew Dunstan <[email protected]> parent: Andrew Dunstan <[email protected]> 1 sibling, 1 reply; 6+ messages in thread From: Andrew Dunstan @ 2023-01-04 21:26 UTC (permalink / raw) To: Thomas Munro <[email protected]>; +Cc: PostgreSQL Hackers <[email protected]> On 2023-01-02 Mo 09:45, Andrew Dunstan wrote: > On 2023-01-01 Su 18:31, Andrew Dunstan wrote: >> On 2023-01-01 Su 14:02, Thomas Munro wrote: >>> On Mon, Jan 2, 2023 at 3:04 AM Andrew Dunstan <[email protected]> wrote: >>>> On 2022-12-19 Mo 11:16, Andrew Dunstan wrote: >>>>> There is currently no test for the use of ldapbindpasswd in the >>>>> pg_hba.conf file. This patch, mostly the work of John Naylor, remedies that. >>>>> >>>>> >>>> This currently has failures on the cfbot for meson builds on FBSD13 and >>>> Debian Bullseye, but it's not at all clear why. In both cases it fails >>>> where the ldap server is started. >>> I think it's failing when using meson. I guess it fails to fail on >>> macOS only because you need to add a new path for Homebrew/ARM like >>> commit 14d63dd2, so it's skipping (it'd be nice if we didn't need >>> another copy of all that logic). Trying locally... it looks like >>> slapd is failing silently, and with some tracing I can see it's >>> sending an error message to my syslog daemon, which logged: >>> >>> 2023-01-02T07:50:20.853019+13:00 x1 slapd[153599]: main: TLS init def >>> ctx failed: -1 >>> >>> Ah, it looks like this test is relying on "slapd-certs", which doesn't exist: >>> >>> tmunro@x1:~/projects/postgresql/build$ ls testrun/ldap/001_auth/data/ >>> ldap.conf ldappassword openldap-data portlock slapd-certs slapd.conf >>> tmunro@x1:~/projects/postgresql/build$ ls testrun/ldap/002_bindpasswd/data/ >>> portlock slapd.conf >>> >>> I didn't look closely, but apparently there is something wrong in the >>> part that copies certs from the ssl test? Not sure why it works for >>> autoconf... >> >> Let's see how we fare with this patch. >> >> > Not so well :-(. This version tries to make the tests totally > independent, as they should be. That's an attempt to get the cfbot to go > green, but I am intending to refactor this code substantially so the > common bits are in a module each test file will load. > > This version factors out the creation of the LDAP server into a separate perl Module. That makes both the existing test script and the new test script a lot shorter, and will be useful for the nearby patch for a hook for the ldapbindpassword. cheers andrew -- Andrew Dunstan EDB: https://www.enterprisedb.com Attachments: [text/x-patch] Add-a-test-for-ldapbindpasswd-v4.patch (16.1K, ../../[email protected]/2-Add-a-test-for-ldapbindpasswd-v4.patch) download | inline diff: diff --git a/src/test/ldap/LdapServer.pm b/src/test/ldap/LdapServer.pm new file mode 100644 index 0000000000..a7198e06e5 --- /dev/null +++ b/src/test/ldap/LdapServer.pm @@ -0,0 +1,318 @@ + +############################################################################ +# +# LdapServer.pm +# +# Module to set up an LDAP server for testing pg_hba.conf ldap authentication +# +# Copyright (c) 2023, PostgreSQL Global Development Group +# +############################################################################ + +=pod + +=head1 NAME + +LdapServer - class for an LDAP server for testing pg_hba.conf authentication + +=head1 SYNOPSIS + + use LdapServer; + + # have we found openldap binaies suitable for setting up a server? + my $ldap_binaries_found = $LdapServer::setup; + + # create a server with the given root password and auth type + # (users or anonymous) + my $server = LdapServer->new($root_password, $auth_type); + + # Add the contents of an LDIF file to the server + $server->ldapadd_file ($path_to_ldif_data); + + # set the Ldap password for a user + $server->ldapsetpw($user, $password); + + # get details of some settings for the server + my @properties = $server->prop($propname1, $propname2, ...); + +=head1 DESCRIPTION + + LdapServer tests in its INIT phase for the presence of suitable openldap + binaries. Its constructor method sets up and runs an LDAP server, and any + servers that are set up are terminated during its END phase. + +=cut + +package LdapServer; + +use strict; +use warnings; + +use PostgreSQL::Test::Utils; +use Test::More; + +use File::Copy; +use File::Basename; + +# private variables +my ($slapd, $ldap_schema_dir, @servers); + +# visible variable +our ($setup); + +INIT +{ + $setup = 1; + if ($^O eq 'darwin' && -d '/opt/homebrew/opt/openldap') + { + # typical paths for Homebrew on ARM + $slapd = '/opt/homebrew/opt/openldap/libexec/slapd'; + $ldap_schema_dir = '/opt/homebrew/etc/openldap/schema'; + } + elsif ($^O eq 'darwin' && -d '/usr/local/opt/openldap') + { + # typical paths for Homebrew on Intel + $slapd = '/usr/local/opt/openldap/libexec/slapd'; + $ldap_schema_dir = '/usr/local/etc/openldap/schema'; + } + elsif ($^O eq 'darwin' && -d '/opt/local/etc/openldap') + { + # typical paths for MacPorts + $slapd = '/opt/local/libexec/slapd'; + $ldap_schema_dir = '/opt/local/etc/openldap/schema'; + } + elsif ($^O eq 'linux') + { + $slapd = '/usr/sbin/slapd'; + $ldap_schema_dir = '/etc/ldap/schema' if -d '/etc/ldap/schema'; + $ldap_schema_dir = '/etc/openldap/schema' if -d '/etc/openldap/schema'; + } + elsif ($^O eq 'freebsd') + { + $slapd = '/usr/local/libexec/slapd'; + $ldap_schema_dir = '/usr/local/etc/openldap/schema'; + } + elsif ($^O eq 'openbsd') + { + $slapd = '/usr/local/libexec/slapd'; + $ldap_schema_dir = '/usr/local/share/examples/openldap/schema'; + } + else + { + $setup = 0; + } +} + +END +{ + foreach my $server (@servers) + { + next unless -f $server->{pidfile}; + my $pid = slurp_file($server->{pidfile}); + chomp $pid; + kill 'INT', $pid; + } +} + +=pod + +=head1 METHODS + +=over + +=item LdapServer->new($rootpw, $auth_type) + +Create a new LDAP server. + +The rootpw can be used when authenticating with the ldapbindpasswd option. + +The auth_type is either 'users' or 'anonymous'. + +=back + +=cut + +sub new +{ + die "no suitable binaries found" unless $setup; + + my $class = shift; + my $rootpw = shift; + my $authtype = shift; # 'users' or 'anonymous' + my $testname = basename((caller)[1],'.pl'); + my $self = {}; + + my $test_temp = PostgreSQL::Test::Utils::tempdir("ldap-$testname"); + + my $ldap_datadir = "$test_temp/openldap-data"; + my $slapd_certs = "$test_temp/slapd-certs"; + my $slapd_pidfile = "$test_temp/slapd.pid"; + my $slapd_logfile = + "${PostgreSQL::Test::Utils::log_path}/slapd-$testname.log"; + my $ldap_server = 'localhost'; + my $ldap_port = PostgreSQL::Test::Cluster::get_free_port(); + my $ldaps_port = PostgreSQL::Test::Cluster::get_free_port(); + my $ldap_url = "ldap://$ldap_server:$ldap_port"; + my $ldaps_url = "ldaps://$ldap_server:$ldaps_port"; + my $ldap_basedn = 'dc=example,dc=net'; + my $ldap_rootdn = 'cn=Manager,dc=example,dc=net'; + my $ldap_rootpw = $rootpw; + my $ldap_pwfile = "$test_temp/ldappassword"; + + (my $conf = <<"EOC") =~ s/^\t\t//gm; + include $ldap_schema_dir/core.schema + include $ldap_schema_dir/cosine.schema + include $ldap_schema_dir/nis.schema + include $ldap_schema_dir/inetorgperson.schema + + pidfile $slapd_pidfile + logfile $slapd_logfile + + access to * + by * read + by $authtype auth + + database ldif + directory $ldap_datadir + + TLSCACertificateFile $slapd_certs/ca.crt + TLSCertificateFile $slapd_certs/server.crt + TLSCertificateKeyFile $slapd_certs/server.key + + suffix "dc=example,dc=net" + rootdn "$ldap_rootdn" + rootpw "$ldap_rootpw" +EOC + append_to_file($slapd_conf, $conf); + + mkdir $ldap_datadir or die "making $ldap_datadir: $!"; + mkdir $slapd_certs or die "making $slapd_certs: $!"; + + my $certdir = dirname(__FILE__) . "/../ssl/ssl"; + + copy "$certdir/server_ca.crt", "$slapd_certs/ca.crt" + || die "copying ca.crt: $!"; + # check we actually have the file, as copy() sometimes gives a false success + -f "$slapd_certs/ca.crt" || die "copying ca.crt (error unknown)"; + copy "$certdir/server-cn-only.crt", "$slapd_certs/server.crt" + || die "copying server.crt: $!"; + copy "$certdir/server-cn-only.key", "$slapd_certs/server.key" + || die "copying server.key: $!"; + + append_to_file($ldap_pwfile, $ldap_rootpw); + chmod 0600, $ldap_pwfile or die "chmod on $ldap_pwfile"; + + system_or_bail $slapd, '-f', $slapd_conf, '-h', "$ldap_url $ldaps_url"; + + # wait until slapd accepts requests + my $retries = 0; + while (1) + { + last + if ( + system_log( + "ldapsearch", "-sbase", + "-H", $ldap_url, + "-b", $ldap_basedn, + "-D", $ldap_rootdn, + "-y", $ldap_pwfile, + "-n", "'objectclass=*'") == 0); + die "cannot connect to slapd" if ++$retries >= 300; + note "waiting for slapd to accept requests..."; + Time::HiRes::usleep(1000000); + } + + $self->{pidfile} = $slapd_pidfile; + $self->{pwfile} = $ldap_pwfile; + $self->{url} = $ldap_url; + $self->{s_url} = $ldaps_url; + $self->{server} = $ldap_server; + $self->{port} = $ldap_port; + $self->{s_port} = $ldaps_port; + $self->{basedn} = $ldap_basedn; + $self->{rootdn} = $ldap_rootdn; + + bless $self, $class; + push @servers, $self; + return $self; +} + +# private routine to set up the environment for methods below +sub _ldapenv +{ + my $self = shift; + my %env = %ENV; + $env{'LDAPURI'} = $self->{url}; + $env{'LDAPBINDDN'} = $self->{rootdn}; + return %env; +} + +=pod + +=over + +=item ldap_add(filename) + +filename is the path to a file containing LDIF data which is added to the LDAP +server. + +=back + +=cut + +sub ldapadd_file +{ + my $self = shift; + my $file = shift; + + local %ENV = $self->_ldapenv; + + system_or_bail 'ldapadd', '-x', '-y', $self->{pwfile}, '-f', $file; +} + +=pod + +=over + +=item ldapsetpw(user, password) + +Set the user's password in the LDAP server + +=back + +=cut + +sub ldapsetpw +{ + my $self = shift; + my $user = shift; + my $password = shift; + + local %ENV = $self->_ldapenv; + + system_or_bail 'ldappasswd', '-x', '-y', $self->{pwfile}, '-s', $password, + $user; +} + +=pod + +=over + +=item prop(name1, ...) + +Returns the list of values for the specified properties of the instance, such +as 'url', 'port', 'basedn'. + +=back + +=cut + +sub prop +{ + my $self = shift; + my @settings; + push @settings, $self->{$_} foreach (@_); + return @settings; +} + +1; diff --git a/src/test/ldap/t/001_auth.pl b/src/test/ldap/t/001_auth.pl index 39736e5116..4a6c4c9339 100644 --- a/src/test/ldap/t/001_auth.pl +++ b/src/test/ldap/t/001_auth.pl @@ -3,170 +3,45 @@ use strict; use warnings; + +use FindBin; use lib "$FindBin::RealBin/.."; + use File::Copy; +use File::Basename; +use LdapServer; use PostgreSQL::Test::Utils; use PostgreSQL::Test::Cluster; use Test::More; - -my ($slapd, $ldap_bin_dir, $ldap_schema_dir); - -$ldap_bin_dir = undef; # usually in PATH - if ($ENV{with_ldap} ne 'yes') { plan skip_all => 'LDAP not supported by this build'; } elsif ($ENV{PG_TEST_EXTRA} !~ /\bldap\b/) { - plan skip_all => 'Potentially unsafe test LDAP not enabled in PG_TEST_EXTRA'; + plan skip_all => + 'Potentially unsafe test LDAP not enabled in PG_TEST_EXTRA'; } -elsif ($^O eq 'darwin' && -d '/opt/homebrew/opt/openldap') -{ - # typical paths for Homebrew on ARM - $slapd = '/opt/homebrew/opt/openldap/libexec/slapd'; - $ldap_schema_dir = '/opt/homebrew/etc/openldap/schema'; -} -elsif ($^O eq 'darwin' && -d '/usr/local/opt/openldap') -{ - # typical paths for Homebrew on Intel - $slapd = '/usr/local/opt/openldap/libexec/slapd'; - $ldap_schema_dir = '/usr/local/etc/openldap/schema'; -} -elsif ($^O eq 'darwin' && -d '/opt/local/etc/openldap') -{ - # typical paths for MacPorts - $slapd = '/opt/local/libexec/slapd'; - $ldap_schema_dir = '/opt/local/etc/openldap/schema'; -} -elsif ($^O eq 'linux') -{ - $slapd = '/usr/sbin/slapd'; - $ldap_schema_dir = '/etc/ldap/schema' if -d '/etc/ldap/schema'; - $ldap_schema_dir = '/etc/openldap/schema' if -d '/etc/openldap/schema'; -} -elsif ($^O eq 'freebsd') -{ - $slapd = '/usr/local/libexec/slapd'; - $ldap_schema_dir = '/usr/local/etc/openldap/schema'; -} -elsif ($^O eq 'openbsd') -{ - $slapd = '/usr/local/libexec/slapd'; - $ldap_schema_dir = '/usr/local/share/examples/openldap/schema'; -} -else +elsif (! $LdapServer::setup) { plan skip_all => "ldap tests not supported on $^O or dependencies not installed"; } -# make your own edits here -#$slapd = ''; -#$ldap_bin_dir = ''; -#$ldap_schema_dir = ''; +note "setting up LDAP server"; -$ENV{PATH} = "$ldap_bin_dir:$ENV{PATH}" if $ldap_bin_dir; +my $ldap_rootpw = 'secret'; +my $ldap = LdapServer->new($ldap_rootpw,'anonymous'); # use anonymous auth +$ldap->ldapadd_file('authdata.ldif'); +$ldap->ldapsetpw('uid=test1,dc=example,dc=net','secret1'); +$ldap->ldapsetpw('uid=test2,dc=example,dc=net','secret2'); -my $ldap_datadir = "${PostgreSQL::Test::Utils::tmp_check}/openldap-data"; -my $slapd_certs = "${PostgreSQL::Test::Utils::tmp_check}/slapd-certs"; -my $slapd_conf = "${PostgreSQL::Test::Utils::tmp_check}/slapd.conf"; -my $slapd_pidfile = "${PostgreSQL::Test::Utils::tmp_check}/slapd.pid"; -my $slapd_logfile = "${PostgreSQL::Test::Utils::log_path}/slapd.log"; -my $ldap_conf = "${PostgreSQL::Test::Utils::tmp_check}/ldap.conf"; -my $ldap_server = 'localhost'; -my $ldap_port = PostgreSQL::Test::Cluster::get_free_port(); -my $ldaps_port = PostgreSQL::Test::Cluster::get_free_port(); -my $ldap_url = "ldap://$ldap_server:$ldap_port"; -my $ldaps_url = "ldaps://$ldap_server:$ldaps_port"; -my $ldap_basedn = 'dc=example,dc=net'; -my $ldap_rootdn = 'cn=Manager,dc=example,dc=net'; -my $ldap_rootpw = 'secret'; -my $ldap_pwfile = "${PostgreSQL::Test::Utils::tmp_check}/ldappassword"; - -note "setting up slapd"; - -append_to_file( - $slapd_conf, - qq{include $ldap_schema_dir/core.schema -include $ldap_schema_dir/cosine.schema -include $ldap_schema_dir/nis.schema -include $ldap_schema_dir/inetorgperson.schema - -pidfile $slapd_pidfile -logfile $slapd_logfile - -access to * - by * read - by anonymous auth - -database ldif -directory $ldap_datadir - -TLSCACertificateFile $slapd_certs/ca.crt -TLSCertificateFile $slapd_certs/server.crt -TLSCertificateKeyFile $slapd_certs/server.key - -suffix "dc=example,dc=net" -rootdn "$ldap_rootdn" -rootpw $ldap_rootpw}); +my ($ldap_server, $ldap_port, $ldaps_port, $ldap_url, $ldaps_url, $ldap_basedn, + $ldap_rootdn) = + $ldap->prop(qw(server port s_port url s_url basedn rootdn)); # don't bother to check the server's cert (though perhaps we should) -append_to_file( - $ldap_conf, - qq{TLS_REQCERT never -}); - -mkdir $ldap_datadir or die; -mkdir $slapd_certs or die; - -# use existing certs from nearby SSL test suite -copy "../ssl/ssl/server_ca.crt", "$slapd_certs/ca.crt" - || die "copying ca.crt: $!"; -copy "../ssl/ssl/server-cn-only.crt", "$slapd_certs/server.crt" - || die "copying server.crt: $!";; -copy "../ssl/ssl/server-cn-only.key", "$slapd_certs/server.key" - || die "copying server.key: $!";; - -system_or_bail $slapd, '-f', $slapd_conf, '-h', "$ldap_url $ldaps_url"; - -END -{ - kill 'INT', `cat $slapd_pidfile` if -f $slapd_pidfile; -} - -append_to_file($ldap_pwfile, $ldap_rootpw); -chmod 0600, $ldap_pwfile or die; - -# wait until slapd accepts requests -my $retries = 0; -while (1) -{ - last - if ( - system_log( - "ldapsearch", "-sbase", - "-H", $ldap_url, - "-b", $ldap_basedn, - "-D", $ldap_rootdn, - "-y", $ldap_pwfile, - "-n", "'objectclass=*'") == 0); - die "cannot connect to slapd" if ++$retries >= 300; - note "waiting for slapd to accept requests..."; - Time::HiRes::usleep(1000000); -} - -$ENV{'LDAPURI'} = $ldap_url; -$ENV{'LDAPBINDDN'} = $ldap_rootdn; -$ENV{'LDAPCONF'} = $ldap_conf; - -note "loading LDAP data"; - -system_or_bail 'ldapadd', '-x', '-y', $ldap_pwfile, '-f', 'authdata.ldif'; -system_or_bail 'ldappasswd', '-x', '-y', $ldap_pwfile, '-s', 'secret1', - 'uid=test1,dc=example,dc=net'; -system_or_bail 'ldappasswd', '-x', '-y', $ldap_pwfile, '-s', 'secret2', - 'uid=test2,dc=example,dc=net'; +$ENV{'LDAPTLS_REQCERT'} = "never"; note "setting up PostgreSQL instance"; diff --git a/src/test/ldap/t/002_bindpasswd.pl b/src/test/ldap/t/002_bindpasswd.pl new file mode 100644 index 0000000000..112149344a --- /dev/null +++ b/src/test/ldap/t/002_bindpasswd.pl @@ -0,0 +1,92 @@ + +# Copyright (c) 2023, PostgreSQL Global Development Group + +use strict; +use warnings; + +use FindBin; use lib "$FindBin::RealBin/.."; + +use File::Copy; +use File::Basename; +use LdapServer; +use PostgreSQL::Test::Utils; +use PostgreSQL::Test::Cluster; +use Test::More; + +if ($ENV{with_ldap} ne 'yes') +{ + plan skip_all => 'LDAP not supported by this build'; +} +elsif ($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"; +} + +note "setting up LDAP server"; + +my $ldap_rootpw = 'secret'; +my $ldap = LdapServer->new($ldap_rootpw,'users'); # no anonymous auth +$ldap->ldapadd_file('authdata.ldif'); +$ldap->ldapsetpw('uid=test1,dc=example,dc=net','secret1'); +$ldap->ldapsetpw('uid=test2,dc=example,dc=net','secret2'); + +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 = on\n"); +$node->start; + +$node->safe_psql('postgres', 'CREATE USER test0;'); +$node->safe_psql('postgres', 'CREATE USER test1;'); +$node->safe_psql('postgres', 'CREATE USER "[email protected]";'); + +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"; + +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; + +$ENV{"PGPASSWORD"} = 'secret1'; +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="$ldap_rootpw"} +); +$node->restart; + +test_access($node, 'test1', 0, 'search+bind authentication succeeds with ldapbindpasswd'); + +done_testing(); ^ permalink raw reply [nested|flat] 6+ messages in thread
* Re: Add a test to ldapbindpasswd @ 2023-01-04 22:33 Andrew Dunstan <[email protected]> parent: Andrew Dunstan <[email protected]> 0 siblings, 1 reply; 6+ messages in thread From: Andrew Dunstan @ 2023-01-04 22:33 UTC (permalink / raw) To: Thomas Munro <[email protected]>; +Cc: PostgreSQL Hackers <[email protected]> On 2023-01-04 We 16:26, Andrew Dunstan wrote: > On 2023-01-02 Mo 09:45, Andrew Dunstan wrote: >> On 2023-01-01 Su 18:31, Andrew Dunstan wrote: >>> On 2023-01-01 Su 14:02, Thomas Munro wrote: >>>> On Mon, Jan 2, 2023 at 3:04 AM Andrew Dunstan <[email protected]> wrote: >>>>> On 2022-12-19 Mo 11:16, Andrew Dunstan wrote: >>>>>> There is currently no test for the use of ldapbindpasswd in the >>>>>> pg_hba.conf file. This patch, mostly the work of John Naylor, remedies that. >>>>>> >>>>>> >>>>> This currently has failures on the cfbot for meson builds on FBSD13 and >>>>> Debian Bullseye, but it's not at all clear why. In both cases it fails >>>>> where the ldap server is started. >>>> I think it's failing when using meson. I guess it fails to fail on >>>> macOS only because you need to add a new path for Homebrew/ARM like >>>> commit 14d63dd2, so it's skipping (it'd be nice if we didn't need >>>> another copy of all that logic). Trying locally... it looks like >>>> slapd is failing silently, and with some tracing I can see it's >>>> sending an error message to my syslog daemon, which logged: >>>> >>>> 2023-01-02T07:50:20.853019+13:00 x1 slapd[153599]: main: TLS init def >>>> ctx failed: -1 >>>> >>>> Ah, it looks like this test is relying on "slapd-certs", which doesn't exist: >>>> >>>> tmunro@x1:~/projects/postgresql/build$ ls testrun/ldap/001_auth/data/ >>>> ldap.conf ldappassword openldap-data portlock slapd-certs slapd.conf >>>> tmunro@x1:~/projects/postgresql/build$ ls testrun/ldap/002_bindpasswd/data/ >>>> portlock slapd.conf >>>> >>>> I didn't look closely, but apparently there is something wrong in the >>>> part that copies certs from the ssl test? Not sure why it works for >>>> autoconf... >>> Let's see how we fare with this patch. >>> >>> >> Not so well :-(. This version tries to make the tests totally >> independent, as they should be. That's an attempt to get the cfbot to go >> green, but I am intending to refactor this code substantially so the >> common bits are in a module each test file will load. >> >> > This version factors out the creation of the LDAP server into a separate > perl Module. That makes both the existing test script and the new test > script a lot shorter, and will be useful for the nearby patch for a hook > for the ldapbindpassword. > > Looks like I fat fingered this. Here's a version that works. cheers andrew -- Andrew Dunstan EDB: https://www.enterprisedb.com Attachments: [text/x-patch] Add-a-test-for-ldapbindpasswd-v5.patch (16.1K, ../../[email protected]/2-Add-a-test-for-ldapbindpasswd-v5.patch) download | inline diff: diff --git a/src/test/ldap/LdapServer.pm b/src/test/ldap/LdapServer.pm new file mode 100644 index 0000000000..0b538defbf --- /dev/null +++ b/src/test/ldap/LdapServer.pm @@ -0,0 +1,319 @@ + +############################################################################ +# +# LdapServer.pm +# +# Module to set up an LDAP server for testing pg_hba.conf ldap authentication +# +# Copyright (c) 2023, PostgreSQL Global Development Group +# +############################################################################ + +=pod + +=head1 NAME + +LdapServer - class for an LDAP server for testing pg_hba.conf authentication + +=head1 SYNOPSIS + + use LdapServer; + + # have we found openldap binaies suitable for setting up a server? + my $ldap_binaries_found = $LdapServer::setup; + + # create a server with the given root password and auth type + # (users or anonymous) + my $server = LdapServer->new($root_password, $auth_type); + + # Add the contents of an LDIF file to the server + $server->ldapadd_file ($path_to_ldif_data); + + # set the Ldap password for a user + $server->ldapsetpw($user, $password); + + # get details of some settings for the server + my @properties = $server->prop($propname1, $propname2, ...); + +=head1 DESCRIPTION + + LdapServer tests in its INIT phase for the presence of suitable openldap + binaries. Its constructor method sets up and runs an LDAP server, and any + servers that are set up are terminated during its END phase. + +=cut + +package LdapServer; + +use strict; +use warnings; + +use PostgreSQL::Test::Utils; +use Test::More; + +use File::Copy; +use File::Basename; + +# private variables +my ($slapd, $ldap_schema_dir, @servers); + +# visible variable +our ($setup); + +INIT +{ + $setup = 1; + if ($^O eq 'darwin' && -d '/opt/homebrew/opt/openldap') + { + # typical paths for Homebrew on ARM + $slapd = '/opt/homebrew/opt/openldap/libexec/slapd'; + $ldap_schema_dir = '/opt/homebrew/etc/openldap/schema'; + } + elsif ($^O eq 'darwin' && -d '/usr/local/opt/openldap') + { + # typical paths for Homebrew on Intel + $slapd = '/usr/local/opt/openldap/libexec/slapd'; + $ldap_schema_dir = '/usr/local/etc/openldap/schema'; + } + elsif ($^O eq 'darwin' && -d '/opt/local/etc/openldap') + { + # typical paths for MacPorts + $slapd = '/opt/local/libexec/slapd'; + $ldap_schema_dir = '/opt/local/etc/openldap/schema'; + } + elsif ($^O eq 'linux') + { + $slapd = '/usr/sbin/slapd'; + $ldap_schema_dir = '/etc/ldap/schema' if -d '/etc/ldap/schema'; + $ldap_schema_dir = '/etc/openldap/schema' if -d '/etc/openldap/schema'; + } + elsif ($^O eq 'freebsd') + { + $slapd = '/usr/local/libexec/slapd'; + $ldap_schema_dir = '/usr/local/etc/openldap/schema'; + } + elsif ($^O eq 'openbsd') + { + $slapd = '/usr/local/libexec/slapd'; + $ldap_schema_dir = '/usr/local/share/examples/openldap/schema'; + } + else + { + $setup = 0; + } +} + +END +{ + foreach my $server (@servers) + { + next unless -f $server->{pidfile}; + my $pid = slurp_file($server->{pidfile}); + chomp $pid; + kill 'INT', $pid; + } +} + +=pod + +=head1 METHODS + +=over + +=item LdapServer->new($rootpw, $auth_type) + +Create a new LDAP server. + +The rootpw can be used when authenticating with the ldapbindpasswd option. + +The auth_type is either 'users' or 'anonymous'. + +=back + +=cut + +sub new +{ + die "no suitable binaries found" unless $setup; + + my $class = shift; + my $rootpw = shift; + my $authtype = shift; # 'users' or 'anonymous' + my $testname = basename((caller)[1],'.pl'); + my $self = {}; + + my $test_temp = PostgreSQL::Test::Utils::tempdir("ldap-$testname"); + + my $ldap_datadir = "$test_temp/openldap-data"; + my $slapd_certs = "$test_temp/slapd-certs"; + my $slapd_pidfile = "$test_temp/slapd.pid"; + my $slapd_conf = "$test_temp/slapd.conf"; + my $slapd_logfile = + "${PostgreSQL::Test::Utils::log_path}/slapd-$testname.log"; + my $ldap_server = 'localhost'; + my $ldap_port = PostgreSQL::Test::Cluster::get_free_port(); + my $ldaps_port = PostgreSQL::Test::Cluster::get_free_port(); + my $ldap_url = "ldap://$ldap_server:$ldap_port"; + my $ldaps_url = "ldaps://$ldap_server:$ldaps_port"; + my $ldap_basedn = 'dc=example,dc=net'; + my $ldap_rootdn = 'cn=Manager,dc=example,dc=net'; + my $ldap_rootpw = $rootpw; + my $ldap_pwfile = "$test_temp/ldappassword"; + + (my $conf = <<"EOC") =~ s/^\t\t//gm; + include $ldap_schema_dir/core.schema + include $ldap_schema_dir/cosine.schema + include $ldap_schema_dir/nis.schema + include $ldap_schema_dir/inetorgperson.schema + + pidfile $slapd_pidfile + logfile $slapd_logfile + + access to * + by * read + by $authtype auth + + database ldif + directory $ldap_datadir + + TLSCACertificateFile $slapd_certs/ca.crt + TLSCertificateFile $slapd_certs/server.crt + TLSCertificateKeyFile $slapd_certs/server.key + + suffix "dc=example,dc=net" + rootdn "$ldap_rootdn" + rootpw "$ldap_rootpw" +EOC + append_to_file($slapd_conf, $conf); + + mkdir $ldap_datadir or die "making $ldap_datadir: $!"; + mkdir $slapd_certs or die "making $slapd_certs: $!"; + + my $certdir = dirname(__FILE__) . "/../ssl/ssl"; + + copy "$certdir/server_ca.crt", "$slapd_certs/ca.crt" + || die "copying ca.crt: $!"; + # check we actually have the file, as copy() sometimes gives a false success + -f "$slapd_certs/ca.crt" || die "copying ca.crt (error unknown)"; + copy "$certdir/server-cn-only.crt", "$slapd_certs/server.crt" + || die "copying server.crt: $!"; + copy "$certdir/server-cn-only.key", "$slapd_certs/server.key" + || die "copying server.key: $!"; + + append_to_file($ldap_pwfile, $ldap_rootpw); + chmod 0600, $ldap_pwfile or die "chmod on $ldap_pwfile"; + + system_or_bail $slapd, '-f', $slapd_conf, '-h', "$ldap_url $ldaps_url"; + + # wait until slapd accepts requests + my $retries = 0; + while (1) + { + last + if ( + system_log( + "ldapsearch", "-sbase", + "-H", $ldap_url, + "-b", $ldap_basedn, + "-D", $ldap_rootdn, + "-y", $ldap_pwfile, + "-n", "'objectclass=*'") == 0); + die "cannot connect to slapd" if ++$retries >= 300; + note "waiting for slapd to accept requests..."; + Time::HiRes::usleep(1000000); + } + + $self->{pidfile} = $slapd_pidfile; + $self->{pwfile} = $ldap_pwfile; + $self->{url} = $ldap_url; + $self->{s_url} = $ldaps_url; + $self->{server} = $ldap_server; + $self->{port} = $ldap_port; + $self->{s_port} = $ldaps_port; + $self->{basedn} = $ldap_basedn; + $self->{rootdn} = $ldap_rootdn; + + bless $self, $class; + push @servers, $self; + return $self; +} + +# private routine to set up the environment for methods below +sub _ldapenv +{ + my $self = shift; + my %env = %ENV; + $env{'LDAPURI'} = $self->{url}; + $env{'LDAPBINDDN'} = $self->{rootdn}; + return %env; +} + +=pod + +=over + +=item ldap_add(filename) + +filename is the path to a file containing LDIF data which is added to the LDAP +server. + +=back + +=cut + +sub ldapadd_file +{ + my $self = shift; + my $file = shift; + + local %ENV = $self->_ldapenv; + + system_or_bail 'ldapadd', '-x', '-y', $self->{pwfile}, '-f', $file; +} + +=pod + +=over + +=item ldapsetpw(user, password) + +Set the user's password in the LDAP server + +=back + +=cut + +sub ldapsetpw +{ + my $self = shift; + my $user = shift; + my $password = shift; + + local %ENV = $self->_ldapenv; + + system_or_bail 'ldappasswd', '-x', '-y', $self->{pwfile}, '-s', $password, + $user; +} + +=pod + +=over + +=item prop(name1, ...) + +Returns the list of values for the specified properties of the instance, such +as 'url', 'port', 'basedn'. + +=back + +=cut + +sub prop +{ + my $self = shift; + my @settings; + push @settings, $self->{$_} foreach (@_); + return @settings; +} + +1; diff --git a/src/test/ldap/t/001_auth.pl b/src/test/ldap/t/001_auth.pl index d38f01125d..2ecbe828a1 100644 --- a/src/test/ldap/t/001_auth.pl +++ b/src/test/ldap/t/001_auth.pl @@ -3,170 +3,45 @@ use strict; use warnings; + +use FindBin; use lib "$FindBin::RealBin/.."; + use File::Copy; +use File::Basename; +use LdapServer; use PostgreSQL::Test::Utils; use PostgreSQL::Test::Cluster; use Test::More; - -my ($slapd, $ldap_bin_dir, $ldap_schema_dir); - -$ldap_bin_dir = undef; # usually in PATH - if ($ENV{with_ldap} ne 'yes') { plan skip_all => 'LDAP not supported by this build'; } elsif ($ENV{PG_TEST_EXTRA} !~ /\bldap\b/) { - plan skip_all => 'Potentially unsafe test LDAP not enabled in PG_TEST_EXTRA'; + plan skip_all => + 'Potentially unsafe test LDAP not enabled in PG_TEST_EXTRA'; } -elsif ($^O eq 'darwin' && -d '/opt/homebrew/opt/openldap') -{ - # typical paths for Homebrew on ARM - $slapd = '/opt/homebrew/opt/openldap/libexec/slapd'; - $ldap_schema_dir = '/opt/homebrew/etc/openldap/schema'; -} -elsif ($^O eq 'darwin' && -d '/usr/local/opt/openldap') -{ - # typical paths for Homebrew on Intel - $slapd = '/usr/local/opt/openldap/libexec/slapd'; - $ldap_schema_dir = '/usr/local/etc/openldap/schema'; -} -elsif ($^O eq 'darwin' && -d '/opt/local/etc/openldap') -{ - # typical paths for MacPorts - $slapd = '/opt/local/libexec/slapd'; - $ldap_schema_dir = '/opt/local/etc/openldap/schema'; -} -elsif ($^O eq 'linux') -{ - $slapd = '/usr/sbin/slapd'; - $ldap_schema_dir = '/etc/ldap/schema' if -d '/etc/ldap/schema'; - $ldap_schema_dir = '/etc/openldap/schema' if -d '/etc/openldap/schema'; -} -elsif ($^O eq 'freebsd') -{ - $slapd = '/usr/local/libexec/slapd'; - $ldap_schema_dir = '/usr/local/etc/openldap/schema'; -} -elsif ($^O eq 'openbsd') -{ - $slapd = '/usr/local/libexec/slapd'; - $ldap_schema_dir = '/usr/local/share/examples/openldap/schema'; -} -else +elsif (! $LdapServer::setup) { plan skip_all => "ldap tests not supported on $^O or dependencies not installed"; } -# make your own edits here -#$slapd = ''; -#$ldap_bin_dir = ''; -#$ldap_schema_dir = ''; +note "setting up LDAP server"; -$ENV{PATH} = "$ldap_bin_dir:$ENV{PATH}" if $ldap_bin_dir; +my $ldap_rootpw = 'secret'; +my $ldap = LdapServer->new($ldap_rootpw,'anonymous'); # use anonymous auth +$ldap->ldapadd_file('authdata.ldif'); +$ldap->ldapsetpw('uid=test1,dc=example,dc=net','secret1'); +$ldap->ldapsetpw('uid=test2,dc=example,dc=net','secret2'); -my $ldap_datadir = "${PostgreSQL::Test::Utils::tmp_check}/openldap-data"; -my $slapd_certs = "${PostgreSQL::Test::Utils::tmp_check}/slapd-certs"; -my $slapd_conf = "${PostgreSQL::Test::Utils::tmp_check}/slapd.conf"; -my $slapd_pidfile = "${PostgreSQL::Test::Utils::tmp_check}/slapd.pid"; -my $slapd_logfile = "${PostgreSQL::Test::Utils::log_path}/slapd.log"; -my $ldap_conf = "${PostgreSQL::Test::Utils::tmp_check}/ldap.conf"; -my $ldap_server = 'localhost'; -my $ldap_port = PostgreSQL::Test::Cluster::get_free_port(); -my $ldaps_port = PostgreSQL::Test::Cluster::get_free_port(); -my $ldap_url = "ldap://$ldap_server:$ldap_port"; -my $ldaps_url = "ldaps://$ldap_server:$ldaps_port"; -my $ldap_basedn = 'dc=example,dc=net'; -my $ldap_rootdn = 'cn=Manager,dc=example,dc=net'; -my $ldap_rootpw = 'secret'; -my $ldap_pwfile = "${PostgreSQL::Test::Utils::tmp_check}/ldappassword"; - -note "setting up slapd"; - -append_to_file( - $slapd_conf, - qq{include $ldap_schema_dir/core.schema -include $ldap_schema_dir/cosine.schema -include $ldap_schema_dir/nis.schema -include $ldap_schema_dir/inetorgperson.schema - -pidfile $slapd_pidfile -logfile $slapd_logfile - -access to * - by * read - by anonymous auth - -database ldif -directory $ldap_datadir - -TLSCACertificateFile $slapd_certs/ca.crt -TLSCertificateFile $slapd_certs/server.crt -TLSCertificateKeyFile $slapd_certs/server.key - -suffix "dc=example,dc=net" -rootdn "$ldap_rootdn" -rootpw $ldap_rootpw}); +my ($ldap_server, $ldap_port, $ldaps_port, $ldap_url, $ldaps_url, $ldap_basedn, + $ldap_rootdn) = + $ldap->prop(qw(server port s_port url s_url basedn rootdn)); # don't bother to check the server's cert (though perhaps we should) -append_to_file( - $ldap_conf, - qq{TLS_REQCERT never -}); - -mkdir $ldap_datadir or die; -mkdir $slapd_certs or die; - -# use existing certs from nearby SSL test suite -copy "../ssl/ssl/server_ca.crt", "$slapd_certs/ca.crt" - || die "copying ca.crt: $!"; -copy "../ssl/ssl/server-cn-only.crt", "$slapd_certs/server.crt" - || die "copying server.crt: $!";; -copy "../ssl/ssl/server-cn-only.key", "$slapd_certs/server.key" - || die "copying server.key: $!";; - -system_or_bail $slapd, '-f', $slapd_conf, '-h', "$ldap_url $ldaps_url"; - -END -{ - kill 'INT', `cat $slapd_pidfile` if -f $slapd_pidfile; -} - -append_to_file($ldap_pwfile, $ldap_rootpw); -chmod 0600, $ldap_pwfile or die; - -# wait until slapd accepts requests -my $retries = 0; -while (1) -{ - last - if ( - system_log( - "ldapsearch", "-sbase", - "-H", $ldap_url, - "-b", $ldap_basedn, - "-D", $ldap_rootdn, - "-y", $ldap_pwfile, - "-n", "'objectclass=*'") == 0); - die "cannot connect to slapd" if ++$retries >= 300; - note "waiting for slapd to accept requests..."; - Time::HiRes::usleep(1000000); -} - -$ENV{'LDAPURI'} = $ldap_url; -$ENV{'LDAPBINDDN'} = $ldap_rootdn; -$ENV{'LDAPCONF'} = $ldap_conf; - -note "loading LDAP data"; - -system_or_bail 'ldapadd', '-x', '-y', $ldap_pwfile, '-f', 'authdata.ldif'; -system_or_bail 'ldappasswd', '-x', '-y', $ldap_pwfile, '-s', 'secret1', - 'uid=test1,dc=example,dc=net'; -system_or_bail 'ldappasswd', '-x', '-y', $ldap_pwfile, '-s', 'secret2', - 'uid=test2,dc=example,dc=net'; +$ENV{'LDAPTLS_REQCERT'} = "never"; note "setting up PostgreSQL instance"; diff --git a/src/test/ldap/t/002_bindpasswd.pl b/src/test/ldap/t/002_bindpasswd.pl new file mode 100644 index 0000000000..112149344a --- /dev/null +++ b/src/test/ldap/t/002_bindpasswd.pl @@ -0,0 +1,92 @@ + +# Copyright (c) 2023, PostgreSQL Global Development Group + +use strict; +use warnings; + +use FindBin; use lib "$FindBin::RealBin/.."; + +use File::Copy; +use File::Basename; +use LdapServer; +use PostgreSQL::Test::Utils; +use PostgreSQL::Test::Cluster; +use Test::More; + +if ($ENV{with_ldap} ne 'yes') +{ + plan skip_all => 'LDAP not supported by this build'; +} +elsif ($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"; +} + +note "setting up LDAP server"; + +my $ldap_rootpw = 'secret'; +my $ldap = LdapServer->new($ldap_rootpw,'users'); # no anonymous auth +$ldap->ldapadd_file('authdata.ldif'); +$ldap->ldapsetpw('uid=test1,dc=example,dc=net','secret1'); +$ldap->ldapsetpw('uid=test2,dc=example,dc=net','secret2'); + +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 = on\n"); +$node->start; + +$node->safe_psql('postgres', 'CREATE USER test0;'); +$node->safe_psql('postgres', 'CREATE USER test1;'); +$node->safe_psql('postgres', 'CREATE USER "[email protected]";'); + +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"; + +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; + +$ENV{"PGPASSWORD"} = 'secret1'; +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="$ldap_rootpw"} +); +$node->restart; + +test_access($node, 'test1', 0, 'search+bind authentication succeeds with ldapbindpasswd'); + +done_testing(); ^ permalink raw reply [nested|flat] 6+ messages in thread
* Re: Add a test to ldapbindpasswd @ 2023-01-23 13:45 Andrew Dunstan <[email protected]> parent: Andrew Dunstan <[email protected]> 0 siblings, 0 replies; 6+ messages in thread From: Andrew Dunstan @ 2023-01-23 13:45 UTC (permalink / raw) To: Thomas Munro <[email protected]>; +Cc: PostgreSQL Hackers <[email protected]> On 2023-01-04 We 17:33, Andrew Dunstan wrote: > >> This version factors out the creation of the LDAP server into a separate >> perl Module. That makes both the existing test script and the new test >> script a lot shorter, and will be useful for the nearby patch for a hook >> for the ldapbindpassword. >> >> > Looks like I fat fingered this. Here's a version that works. > > pushed. cheers andrew -- Andrew Dunstan EDB: https://www.enterprisedb.com ^ permalink raw reply [nested|flat] 6+ messages in thread
end of thread, other threads:[~2023-01-23 13:45 UTC | newest] Thread overview: 6+ messages (download: mbox mbox.gz follow: Atom feed) -- links below jump to the message on this page -- 2019-12-20 01:21 [PATCH v32 05/11] Add Incremental View Maintenance support to psql Yugo Nagata <[email protected]> 2023-01-02 14:45 Re: Add a test to ldapbindpasswd Andrew Dunstan <[email protected]> 2023-01-03 05:48 ` Re: Add a test to ldapbindpasswd Julien Rouhaud <[email protected]> 2023-01-04 21:26 ` Re: Add a test to ldapbindpasswd Andrew Dunstan <[email protected]> 2023-01-04 22:33 ` Re: Add a test to ldapbindpasswd Andrew Dunstan <[email protected]> 2023-01-23 13:45 ` Re: Add a test to ldapbindpasswd Andrew Dunstan <[email protected]>
This inbox is served by agora; see mirroring instructions for how to clone and mirror all data and code used for this inbox