Обсуждение: LDAP lookup of connection parameters

Поиск
Список
Период
Сортировка

LDAP lookup of connection parameters

От
"Albe Laurenz"
Дата:
This patch for libpq allows you to enter an LDAP URL in pg_service.conf.
The URL will be queried and the resulting string(s) parsed for
keyword = value connection options.

The idea is to have connection information stored centrally on an LDAP
server rather than on the client machine.

On Windows the native library wldap32.dll is used, else OpenLDAP.
If --enable_thread_safety has been given, -lldap_r is appended to
PTHREAD_LIBS so that libpq will be linked against the tread safe
library.

There should probably also be a documentation patch for the --with-ldap
option of ./configure, but I didn't write it because it also belongs to
the
"LDAP Auth" patch.

I have added German translations for the new messages - how can I get
translations into other languages?

Yours,
Laurenz Albe

Вложения

Re: LDAP lookup of connection parameters

От
"Albe Laurenz"
Дата:
> This patch for libpq allows you to enter an LDAP URL in
pg_service.conf.
> The URL will be queried and the resulting string(s) parsed for
> keyword = value connection options.
>
> The idea is to have connection information stored centrally on an LDAP
> server rather than on the client machine.

I forgot to mention that there was a discussion on Hackers about this:
http://archives.postgresql.org/pgsql-hackers/2006-02/msg01182.php

I have implemented a solution following the idea of
http://archives.postgresql.org/pgsql-hackers/2006-02/msg01198.php
because I thought that it was better to have something existing to
talk about.

Yours,
Laurenz Albe

Re: LDAP lookup of connection parameters

От
"Albe Laurenz"
Дата:
> This patch for libpq allows you to enter an LDAP URL in
pg_service.conf.
> The URL will be queried and the resulting string(s) parsed for
> keyword = value connection options.
>
> The idea is to have connection information stored centrally on an LDAP
> server rather than on the client machine.

I forgot to mention that there was a brief discussion about this on
Hackers:
http://archives.postgresql.org/pgsql-hackers/2006-02/msg00828.php

My implementation follows the idea from
http://archives.postgresql.org/pgsql-hackers/2006-02/msg01198.php

I thought it would be good to have some real code as a basis for
further discussion whether this is a desirable feature or not.

I felt somewhat encouraged because PostgreSQL already has a
dependency on OpenLDAP since
http://archives.postgresql.org/pgsql-patches/2005-12/msg00375.php
and I do not have to introduce a new dependency.

Yours,
Laurenz Albe

Re: LDAP lookup of connection parameters

От
Bruce Momjian
Дата:
Albe Laurenz wrote:
> > This patch for libpq allows you to enter an LDAP URL in
> pg_service.conf.
> > The URL will be queried and the resulting string(s) parsed for
> > keyword = value connection options.
> >
> > The idea is to have connection information stored centrally on an LDAP
> > server rather than on the client machine.
>
> I forgot to mention that there was a brief discussion about this on
> Hackers:
> http://archives.postgresql.org/pgsql-hackers/2006-02/msg00828.php
>
> My implementation follows the idea from
> http://archives.postgresql.org/pgsql-hackers/2006-02/msg01198.php
>
> I thought it would be good to have some real code as a basis for
> further discussion whether this is a desirable feature or not.
>
> I felt somewhat encouraged because PostgreSQL already has a
> dependency on OpenLDAP since
> http://archives.postgresql.org/pgsql-patches/2005-12/msg00375.php
> and I do not have to introduce a new dependency.

Where are we on this?  It allows pg_service.conf to query LDAP for
connection strings.  Is it a feature people want?

--
  Bruce Momjian   http://candle.pha.pa.us
  EnterpriseDB    http://www.enterprisedb.com

  + If your life is a hard drive, Christ can be your backup. +

Re: LDAP lookup of connection parameters

От
Bruce Momjian
Дата:
I am confused why this patch requires libldap_r.  Is there a need for
threading?  Should this be contingent on whether the threading flag was
passed to configure?

---------------------------------------------------------------------------

Albe Laurenz wrote:
> This patch for libpq allows you to enter an LDAP URL in pg_service.conf.
> The URL will be queried and the resulting string(s) parsed for
> keyword = value connection options.
>
> The idea is to have connection information stored centrally on an LDAP
> server rather than on the client machine.
>
> On Windows the native library wldap32.dll is used, else OpenLDAP.
> If --enable_thread_safety has been given, -lldap_r is appended to
> PTHREAD_LIBS so that libpq will be linked against the tread safe
> library.
>
> There should probably also be a documentation patch for the --with-ldap
> option of ./configure, but I didn't write it because it also belongs to
> the
> "LDAP Auth" patch.
>
> I have added German translations for the new messages - how can I get
> translations into other languages?
>
> Yours,
> Laurenz Albe

Content-Description: ldap_service.patch

[ Attachment, skipping... ]

Content-Description: ldap_service_doc.patch

[ Attachment, skipping... ]

>
> ---------------------------(end of broadcast)---------------------------
> TIP 6: explain analyze is your friend

--
  Bruce Momjian   bruce@momjian.us
  EnterpriseDB    http://www.enterprisedb.com

  + If your life is a hard drive, Christ can be your backup. +

Re: LDAP lookup of connection parameters

От
Bruce Momjian
Дата:
Albe Laurenz wrote:
> This patch for libpq allows you to enter an LDAP URL in pg_service.conf.
> The URL will be queried and the resulting string(s) parsed for
> keyword = value connection options.
>
> The idea is to have connection information stored centrally on an LDAP
> server rather than on the client machine.
>
> On Windows the native library wldap32.dll is used, else OpenLDAP.
> If --enable_thread_safety has been given, -lldap_r is appended to
> PTHREAD_LIBS so that libpq will be linked against the tread safe
> library.
>
> There should probably also be a documentation patch for the --with-ldap
> option of ./configure, but I didn't write it because it also belongs to
> the "LDAP Auth" patch.
>
> I have added German translations for the new messages - how can I get
> translations into other languages?

Translations are done later in the release process.

I have heavily modified your patch to be clearer.  Please review the
attached version and test it to make sure it still works properly.
Thanks.

--
  Bruce Momjian   bruce@momjian.us
  EnterpriseDB    http://www.enterprisedb.com

  + If your life is a hard drive, Christ can be your backup. +
Index: configure.in
===================================================================
RCS file: /cvsroot/pgsql/configure.in,v
retrieving revision 1.469
diff -c -c -r1.469 configure.in
*** configure.in    24 Jul 2006 16:32:44 -0000    1.469
--- configure.in    25 Jul 2006 21:44:20 -0000
***************
*** 1106,1111 ****
--- 1106,1119 ----
  PGAC_FUNC_GETPWUID_R_5ARG
  PGAC_FUNC_STRERROR_R_INT

+ # this will link libpq against libldap_r
+ if test "$with_ldap" = yes ; then
+   if test "$PORTNAME" != "win32"; then
+     AC_CHECK_LIB(ldap_r,    ldap_simple_bind, [], [AC_MSG_ERROR([library 'ldap_r' is required for LDAP])])
+     PTHREAD_LIBS="$PTHREAD_LIBS -lldap_r"
+   fi
+ fi
+
  CFLAGS="$_CFLAGS"
  LIBS="$_LIBS"

Index: doc/src/sgml/libpq.sgml
===================================================================
RCS file: /cvsroot/pgsql/doc/src/sgml/libpq.sgml,v
retrieving revision 1.213
diff -c -c -r1.213 libpq.sgml
*** doc/src/sgml/libpq.sgml    4 Jul 2006 13:22:15 -0000    1.213
--- doc/src/sgml/libpq.sgml    25 Jul 2006 21:44:23 -0000
***************
*** 4126,4131 ****
--- 4126,4197 ----
  </sect1>


+ <sect1 id="libpq-ldap">
+  <title>LDAP Lookup of Connection Parameters</title>
+
+ <indexterm zone="libpq-ldap">
+  <primary>LDAP connection parameter lookup</primary>
+ </indexterm>
+
+ <para>
+ If <application>libpq</application> has been compiled with LDAP support (option
+ <literal><option>--with-ldap</option></literal> for <command>configure</command>)
+ it is possible to retrieve connection options like <literal>host</literal>
+ or <literal>dbname</literal> via LDAP from a central server.
+ The advantage is that if the connection parameters for a database change,
+ the connection information doesn't have to be updated on all client machines.
+ </para>
+
+ <para>
+ LDAP connection parameter lookup uses the connection service file
+ <filename>pg_service.conf</filename> (see <xref linkend="libpq-pgservice">).
+ A line in a <filename>pg_service.conf</filename> stanza that starts with
+ <literal>ldap://</literal> will be recognized as an LDAP URL and an LDAP
+ query will be performed. The result must be a list of <literal>keyword =
+ value</literal> pairs which will be used to set connection options.
+ The URL must conform to RFC 1959 and be of the form
+ <synopsis>
+
ldap://[<replaceable>hostname</replaceable>[:<replaceable>port</replaceable>]]/<replaceable>search_base</replaceable>?<replaceable>attribute</replaceable>?<replaceable>search_scope</replaceable>?<replaceable>filter</replaceable>
+ </synopsis>
+ where <replaceable>hostname</replaceable>
+ defaults to <literal>localhost</literal> and
+ <replaceable>port</replaceable> defaults to 389.
+ </para>
+
+ <para>
+ Processing of <filename>pg_service.conf</filename> is terminated after
+ a successful LDAP lookup, but is continued if the LDAP server cannot be
+ contacted.  This is to provide a fallback with
+ further LDAP URL lines that point to different LDAP
+ servers, classical <literal>keyword = value</literal> pairs, or
+ default connection options.
+ If you would rather get an error message in this case, add a
+ syntactically incorrect line after the LDAP URL.
+ </para>
+
+ <para>
+ A sample LDAP entry that has been created with the LDIF file
+ <synopsis>
+ version:1
+ dn:cn=mydatabase,dc=mycompany,dc=com
+ changetype:add
+ objectclass:top
+ objectclass:groupOfUniqueNames
+ cn:mydatabase
+ uniqueMember:host=dbserver.mycompany.com
+ uniqueMember:port=5439
+ uniqueMember:dbname=mydb
+ uniqueMember:user=mydb_user
+ uniqueMember:sslmode=require
+ </synopsis>
+ might be queried with the following LDAP URL:
+ <synopsis>
+ ldap://ldap.mycompany.com/dc=mycompany,dc=com?uniqueMember?one?(cn=mydatabase)
+ </synopsis>
+ </para>
+ </sect1>
+
+
  <sect1 id="libpq-ssl">
  <title>SSL Support</title>

Index: src/interfaces/libpq/Makefile
===================================================================
RCS file: /cvsroot/pgsql/src/interfaces/libpq/Makefile,v
retrieving revision 1.146
diff -c -c -r1.146 Makefile
*** src/interfaces/libpq/Makefile    18 Jul 2006 22:18:08 -0000    1.146
--- src/interfaces/libpq/Makefile    25 Jul 2006 21:44:27 -0000
***************
*** 62,68 ****
  SHLIB_LINK += $(filter -lcrypt -ldes -lcom_err -lcrypto -lk5crypto -lkrb5 -lssl -lsocket -lnsl -lresolv -lintl
$(PTHREAD_LIBS),$(LIBS)) 
  endif
  ifeq ($(PORTNAME), win32)
! SHLIB_LINK += -lshfolder -lwsock32 -lws2_32 $(filter -leay32 -lssleay32 -lcomerr32 -lkrb5_32, $(LIBS))
  endif


--- 62,68 ----
  SHLIB_LINK += $(filter -lcrypt -ldes -lcom_err -lcrypto -lk5crypto -lkrb5 -lssl -lsocket -lnsl -lresolv -lintl
$(PTHREAD_LIBS),$(LIBS)) 
  endif
  ifeq ($(PORTNAME), win32)
! SHLIB_LINK += -lshfolder -lwsock32 -lws2_32 $(filter -leay32 -lssleay32 -lcomerr32 -lkrb5_32 -lwldap32, $(LIBS))
  endif


Index: src/interfaces/libpq/fe-connect.c
===================================================================
RCS file: /cvsroot/pgsql/src/interfaces/libpq/fe-connect.c,v
retrieving revision 1.333
diff -c -c -r1.333 fe-connect.c
*** src/interfaces/libpq/fe-connect.c    7 Jun 2006 22:24:46 -0000    1.333
--- src/interfaces/libpq/fe-connect.c    25 Jul 2006 21:44:29 -0000
***************
*** 60,65 ****
--- 60,78 ----
  #endif
  #endif

+ #ifdef USE_LDAP
+ #ifdef WIN32
+ #include <winldap.h>
+ #else
+ /* OpenLDAP deprecates RFC 1823, but we want standard conformance */
+ #define LDAP_DEPRECATED 1
+ #include <ldap.h>
+ typedef struct timeval LDAP_TIMEVAL;
+ #endif
+ static int ldapServiceLookup(const char *purl, PQconninfoOption *options,
+                              PQExpBuffer errorMessage);
+ #endif
+
  #include "libpq/ip.h"
  #include "mb/pg_wchar.h"

***************
*** 2343,2349 ****
--- 2356,2762 ----
      return STATUS_OK;
  }

+ #ifdef USE_LDAP
+
+ #define LDAP_URL    "ldap://"
+ #define LDAP_DEF_PORT    389
+ #define PGLDAP_TIMEOUT 2
+
+ #define ld_is_sp_tab(x) ((x) == ' ' || (x) == '\t')
+ #define ld_is_nl_cr(x) ((x) == '\r' || (x) == '\n')
+
+
+ /*
+  *        ldapServiceLookup
+  *
+  * Search the LDAP URL passed as first argument, treat the result as a
+  * string of connection options that are parsed and added to the array of
+  * options passed as second argument.
+  *
+  * LDAP URLs must conform to RFC 1959 without escape sequences.
+  *    ldap://host:port/dn?attributes?scope?filter?extensions
+  *
+  * Returns
+  *    0 if the lookup was successful,
+  *    1 if the connection to the LDAP server could be established but
+  *      the search was unsuccessful,
+  *    2 if a connection could not be established, and
+  *    3 if a fatal error occurred.
+  *
+  * An error message is returned in the third argument for return codes 1 and 3.
+  */
+ static int
+ ldapServiceLookup(const char *purl, PQconninfoOption *options,
+                   PQExpBuffer errorMessage)
+ {
+     int            port = LDAP_DEF_PORT, scope, rc, msgid, size, state, oldstate, i;
+     bool        found_keyword;
+     char       *url, *hostname, *portstr, *endptr, *dn, *scopestr, *filter,
+                *result, *p, *p1 = NULL, *optname = NULL, *optval = NULL;
+     char       *attrs[2] = {NULL, NULL};
+     LDAP       *ld = NULL;
+     LDAPMessage *res, *entry;
+     struct berval **values;
+     LDAP_TIMEVAL time = {PGLDAP_TIMEOUT, 0};
+
+     if ((url = strdup(purl)) == NULL)
+     {
+         printfPQExpBuffer(errorMessage, libpq_gettext("out of memory\n"));
+         return 3;
+     }
+
+     /*
+      *    Parse URL components, check for correctness.  Basically, url has
+      *    '\0' placed at componient boundaries and variables are pointed
+      *    at each component.
+      */
+
+     if (strncasecmp(url, LDAP_URL, strlen(LDAP_URL)) != 0)
+     {
+         printfPQExpBuffer(errorMessage,
+         libpq_gettext("bad LDAP URL \"%s\": scheme must be ldap://\n"), url);
+         free(url);
+         return 3;
+     }
+
+     hostname = url + strlen(LDAP_URL);
+     if (*hostname == '/')    /* no hostname? */
+         hostname = "localhost";    /* the default */
+
+     /* dn ("distinguished name") */
+     p = strchr(url +  strlen(LDAP_URL), '/');
+     if (p == NULL || *(p + 1) == '\0' || *(p + 1) == '?')
+     {
+         printfPQExpBuffer(errorMessage, libpq_gettext(
+                         "bad LDAP URL \"%s\": missing distinguished name\n"), url);
+         free(url);
+         return 3;
+     }
+     *p = '\0';    /* terminate hostname */
+     dn = p + 1;
+
+     /* port number? */
+     if (p1 = strchr(dn, ':')) != NULL)
+     {
+         long        lport;
+
+         *p1 = '\0';
+         portstr = p1 + 1;
+         errno = 0;
+         lport = strtol(portstr, &endptr, 10);
+         if (*portstr == '\0' || *endptr != '\0' || errno || lport < 0 || lport > 65535)
+         {
+             printfPQExpBuffer(errorMessage, libpq_gettext(
+                             "bad LDAP URL \"%s\": invalid port number\n"), url);
+             free(url);
+             return 3;
+         }
+         port = (int) lport;
+     }

+     /* Is there an attribute? */
+     if ((p = strchr(dn, '?')) == NULL || *(p + 1) == '\0' || *(p + 1) == '?')
+     {
+         printfPQExpBuffer(errorMessage, libpq_gettext(
+                             "bad LDAP URL \"%s\": must have exactly one attribute\n"), url);
+         free(url);
+         return 3;
+     }
+     *p = '\0';
+     attrs[0] = p + 1;
+
+     /* Allow only one attribute */
+     if (strchr(attrs[0], ',') != NULL)
+     {
+         printfPQExpBuffer(errorMessage, libpq_gettext(
+                             "bad LDAP URL \"%s\": must have exactly one attribute\n"), url);
+         free(url);
+         return 3;
+     }
+
+     /* scope */
+     if ((p = strchr(attrs[0], '?')) == NULL || *(p + 1) == '\0' || *(p + 1) == '?')
+     {
+         printfPQExpBuffer(errorMessage, libpq_gettext(
+                             "bad LDAP URL \"%s\": must have search scope (base/one/sub)\n"), url);
+         free(url);
+         return 3;
+     }
+     *p = '\0';
+     scopestr = p + 1;
+     if (strcasecmp(scopestr, "base") == 0)
+         scope = LDAP_SCOPE_BASE;
+     else if (strcasecmp(scopestr, "one") == 0)
+         scope = LDAP_SCOPE_ONELEVEL;
+     else if (strcasecmp(scopestr, "sub") == 0)
+         scope = LDAP_SCOPE_SUBTREE;
+     else
+     {
+         printfPQExpBuffer(errorMessage, libpq_gettext(
+                     "bad LDAP URL \"%s\": must have search scope (base/one/sub)\n"), url);
+         free(url);
+         return 3;
+     }
+
+     /* filter */
+     if ((p = strchr(scopestr, '?')) == NULL || *(p + 1) == '\0' || *(p + 1) == '?')
+     {
+         printfPQExpBuffer(errorMessage,
+                     libpq_gettext("bad LDAP URL \"%s\": no filter\n"), url);
+         free(url);
+         return 3;
+     }
+     *p = '\0';
+     filter = p + 1;
+     if ((p = strchr(filter, '?')) != NULL)
+         *p = '\0';
+
+     /* initialize LDAP structure */
+     if ((ld = ldap_init(hostname, port)) == NULL)
+     {
+         printfPQExpBuffer(errorMessage,
+                           libpq_gettext("error creating LDAP structure\n"));
+         free(url);
+         return 3;
+     }
+
+     /*
+      *    Initialize connection to the server.  We do an explicit bind because
+      *    we want to return 2 if the bind fails.
+      */
+     if ((msgid = ldap_simple_bind(ld, NULL, NULL)) == -1)
+     {
+         /* error in ldap_simple_bind() */
+         free(url);
+         ldap_unbind(ld);
+         return 2;
+     }
+
+     /* wait some time for the connection to succeed */
+     res = NULL;
+     if (rc = ldap_result(ld, msgid, LDAP_MSG_ALL, &time, &res)) == -1 ||
+         res == NULL)
+     {
+         if (res != NULL)
+         {
+             /* timeout */
+             ldap_msgfree(res);
+         }
+         /* error in ldap_result() */
+         free(url);
+         ldap_unbind(ld);
+         return 2;
+     }
+     ldap_msgfree(res);
+
+     /* search */
+     res = NULL;
+     if ((rc = ldap_search_st(ld, dn, scope, filter, attrs, 0, &time, &res))
+         != LDAP_SUCCESS)
+     {
+         if (res != NULL)
+             ldap_msgfree(res);
+         printfPQExpBuffer(errorMessage,
+                           libpq_gettext("lookup on LDAP server failed: %s\n"),
+                           ldap_err2string(rc));
+         ldap_unbind(ld);
+         free(url);
+         return 1;
+     }
+
+     /* complain if there was not exactly one result */
+     if ((rc = ldap_count_entries(ld, res)) != 1)
+     {
+         printfPQExpBuffer(errorMessage,
+              rc ? libpq_gettext("more than one entry found on LDAP lookup\n")
+                           : libpq_gettext("no entry found on LDAP lookup\n"));
+         ldap_msgfree(res);
+         ldap_unbind(ld);
+         free(url);
+         return 1;
+     }
+
+     /* get entry */
+     if ((entry = ldap_first_entry(ld, res)) == NULL)
+     {
+         /* should never happen */
+         printfPQExpBuffer(errorMessage,
+                           libpq_gettext("no entry found on LDAP lookup\n"));
+         ldap_msgfree(res);
+         ldap_unbind(ld);
+         free(url);
+         return 1;
+     }
+
+     /* get values */
+     if ((values = ldap_get_values_len(ld, entry, attrs[0])) == NULL)
+     {
+         printfPQExpBuffer(errorMessage,
+                   libpq_gettext("attribute has no values on LDAP lookup\n"));
+         ldap_msgfree(res);
+         ldap_unbind(ld);
+         free(url);
+         return 1;
+     }
+
+     ldap_msgfree(res);
+     free(url);
+
+     if (values[0] == NULL)
+     {
+         printfPQExpBuffer(errorMessage,
+                   libpq_gettext("attribute has no values on LDAP lookup\n"));
+         ldap_value_free_len(values);
+         ldap_unbind(ld);
+         return 1;
+     }
+
+     /* concatenate values to a single string */
+     for (size = 0, i = 0; values[i] != NULL; ++i)
+         size += values[i]->bv_len + 1;
+     if ((result = malloc(size + 1)) == NULL)
+     {
+         printfPQExpBuffer(errorMessage,
+                           libpq_gettext("out of memory\n"));
+         ldap_value_free_len(values);
+         ldap_unbind(ld);
+         return 3;
+     }
+     for (p = result, i = 0; NULL != values[i]; ++i)
+     {
+         strncpy(p, values[i]->bv_val, values[i]->bv_len);
+         p += values[i]->bv_len;
+         *(p++) = '\n';
+         if (values[i + 1] == NULL)
+             *(p + 1) = '\0';
+     }
+
+     ldap_value_free_len(values);
+     ldap_unbind(ld);
+
+     /* parse result string */
+     oldstate = state = 0;
+     for (p = result; *p != '\0'; ++p)
+     {
+         switch (state)
+         {
+             case 0:                /* between entries */
+                 if (!ld_is_sp_tab(*p) && !ld_is_nl_cr(*p))
+                 {
+                     optname = p;
+                     state = 1;
+                 }
+                 break;
+             case 1:                /* in option name */
+                 if (ld_is_sp_tab(*p))
+                 {
+                     *p = '\0';
+                     state = 2;
+                 }
+                 else if (ld_is_nl_cr(*p))
+                 {
+                     printfPQExpBuffer(errorMessage, libpq_gettext(
+                                 "missing \"=\" after \"%s\" in connection info string\n"),
+                                   optname);
+                     return 3;
+                 }
+                 else if (*p == '=')
+                 {
+                     *p = '\0';
+                     state = 3;
+                 }
+                 break;
+             case 2:                /* after option name */
+                 if (*p == '=')
+                 {
+                     state = 3;
+                 }
+                 else if (!ld_is_sp_tab(*p))
+                 {
+                     printfPQExpBuffer(errorMessage, libpq_gettext(
+                                 "missing \"=\" after \"%s\" in connection info string\n"),
+                                   optname);
+                     return 3;
+                 }
+                 break;
+             case 3:                /* before option value */
+                 if (*p == '\'')
+                 {
+                     optval = p + 1;
+                     p1 = p + 1;
+                     state = 5;
+                 }
+                 else if (ld_is_nl_cr(*p))
+                 {
+                     optval = optname + strlen(optname); /* empty */
+                     state = 0;
+                 }
+                 else if (!ld_is_sp_tab(*p))
+                 {
+                     optval = p;
+                     state = 4;
+                 }
+                 break;
+             case 4:                /* in unquoted option value */
+                 if (ld_is_sp_tab(*p) || ld_is_nl_cr(*p))
+                 {
+                     *p = '\0';
+                     state = 0;
+                 }
+                 break;
+             case 5:                /* in quoted option value */
+                 if (*p == '\'')
+                 {
+                     *p1 = '\0';
+                     state = 0;
+                 }
+                 else if (*p == '\\')
+                     state = 6;
+                 else
+                     *(p1++) = *p;
+                 break;
+             case 6:                /* in quoted option value after escape */
+                 *(p1++) = *p;
+                 state = 5;
+                 break;
+         }
+
+         if (state == 0 && oldstate != 0)
+         {
+             found_keyword = false;
+             for (i = 0; options[i].keyword; i++)
+             {
+                 if (strcmp(options[i].keyword, optname) == 0)
+                 {
+                     if (options[i].val == NULL)
+                         options[i].val = strdup(optval);
+                     found_keyword = true;
+                     break;
+                 }
+             }
+             if (!found_keyword)
+             {
+                 printfPQExpBuffer(errorMessage,
+                          libpq_gettext("invalid connection option \"%s\"\n"),
+                           optname);
+                 return 1;
+             }
+             optname = NULL;
+             optval = NULL;
+         }
+         oldstate = state;
+     }
+
+     if (state == 5 || state == 6)
+     {
+         printfPQExpBuffer(errorMessage, libpq_gettext(
+                         "unterminated quoted string in connection info string\n"));
+         return 3;
+     }
+
+     return 0;
+ }
+ #endif

  #define MAXBUFSIZE 256

***************
*** 2439,2444 ****
--- 2852,2877 ----
                                 *val;
                      bool        found_keyword;

+ #ifdef USE_LDAP
+                     if (strncmp(line, "ldap", 4) == 0)
+                     {
+                         int rc = ldapServiceLookup(line, options, errorMessage);
+                         /* if rc = 2, go on reading for fallback */
+                         switch (rc)
+                         {
+                             case 0:
+                                 fclose(f);
+                                 return 0;
+                             case 1:
+                             case 3:
+                                 fclose(f);
+                                 return 3;
+                             case 2:
+                                 continue;
+                         }
+                     }
+ #endif
+
                      key = line;
                      val = strchr(line, '=');
                      if (val == NULL)

Re: LDAP lookup of connection parameters

От
"Albe Laurenz"
Дата:
Bruce Momjian wrote:
> Albe Laurenz wrote:
>> This patch for libpq allows you to enter an LDAP URL in
pg_service.conf.
>> The URL will be queried and the resulting string(s) parsed for
>> keyword = value connection options.
>
> I have heavily modified your patch to be clearer.  Please review the
> attached version and test it to make sure it still works properly.
> Thanks.

Most of your modifications are fine, but a quick look tells me that your
modifications in the parsing of the LDAP URL have been too invasive,
e.g.:

- you look for the port number in the 'dn' and not in the 'hostname'
- you check the validity of 'scopestr' and 'attrs[0]' before it is
'\0'-terminated

Would you prefer that I try to fix your fixes (and stick with your
coding style)
or do you want another go?

Yours,
Laurenz Albe

Re: LDAP lookup of connection parameters

От
Bruce Momjian
Дата:
Albe Laurenz wrote:
> Bruce Momjian wrote:
> > Albe Laurenz wrote:
> >> This patch for libpq allows you to enter an LDAP URL in
> pg_service.conf.
> >> The URL will be queried and the resulting string(s) parsed for
> >> keyword = value connection options.
> >
> > I have heavily modified your patch to be clearer.  Please review the
> > attached version and test it to make sure it still works properly.
> > Thanks.
>
> Most of your modifications are fine, but a quick look tells me that your
> modifications in the parsing of the LDAP URL have been too invasive,
> e.g.:
>
> - you look for the port number in the 'dn' and not in the 'hostname'
> - you check the validity of 'scopestr' and 'attrs[0]' before it is
> '\0'-terminated
>
> Would you prefer that I try to fix your fixes (and stick with your
> coding style)
> or do you want another go?

Thanks for the review.  Updated patch attached.  Is that OK?

--
  Bruce Momjian   bruce@momjian.us
  EnterpriseDB    http://www.enterprisedb.com

  + If your life is a hard drive, Christ can be your backup. +
Index: configure.in
===================================================================
RCS file: /cvsroot/pgsql/configure.in,v
retrieving revision 1.469
diff -c -c -r1.469 configure.in
*** configure.in    24 Jul 2006 16:32:44 -0000    1.469
--- configure.in    26 Jul 2006 16:38:39 -0000
***************
*** 1106,1111 ****
--- 1106,1119 ----
  PGAC_FUNC_GETPWUID_R_5ARG
  PGAC_FUNC_STRERROR_R_INT

+ # this will link libpq against libldap_r
+ if test "$with_ldap" = yes ; then
+   if test "$PORTNAME" != "win32"; then
+     AC_CHECK_LIB(ldap_r,    ldap_simple_bind, [], [AC_MSG_ERROR([library 'ldap_r' is required for LDAP])])
+     PTHREAD_LIBS="$PTHREAD_LIBS -lldap_r"
+   fi
+ fi
+
  CFLAGS="$_CFLAGS"
  LIBS="$_LIBS"

Index: doc/src/sgml/libpq.sgml
===================================================================
RCS file: /cvsroot/pgsql/doc/src/sgml/libpq.sgml,v
retrieving revision 1.213
diff -c -c -r1.213 libpq.sgml
*** doc/src/sgml/libpq.sgml    4 Jul 2006 13:22:15 -0000    1.213
--- doc/src/sgml/libpq.sgml    26 Jul 2006 16:38:41 -0000
***************
*** 4126,4131 ****
--- 4126,4197 ----
  </sect1>


+ <sect1 id="libpq-ldap">
+  <title>LDAP Lookup of Connection Parameters</title>
+
+ <indexterm zone="libpq-ldap">
+  <primary>LDAP connection parameter lookup</primary>
+ </indexterm>
+
+ <para>
+ If <application>libpq</application> has been compiled with LDAP support (option
+ <literal><option>--with-ldap</option></literal> for <command>configure</command>)
+ it is possible to retrieve connection options like <literal>host</literal>
+ or <literal>dbname</literal> via LDAP from a central server.
+ The advantage is that if the connection parameters for a database change,
+ the connection information doesn't have to be updated on all client machines.
+ </para>
+
+ <para>
+ LDAP connection parameter lookup uses the connection service file
+ <filename>pg_service.conf</filename> (see <xref linkend="libpq-pgservice">).
+ A line in a <filename>pg_service.conf</filename> stanza that starts with
+ <literal>ldap://</literal> will be recognized as an LDAP URL and an LDAP
+ query will be performed. The result must be a list of <literal>keyword =
+ value</literal> pairs which will be used to set connection options.
+ The URL must conform to RFC 1959 and be of the form
+ <synopsis>
+
ldap://[<replaceable>hostname</replaceable>[:<replaceable>port</replaceable>]]/<replaceable>search_base</replaceable>?<replaceable>attribute</replaceable>?<replaceable>search_scope</replaceable>?<replaceable>filter</replaceable>
+ </synopsis>
+ where <replaceable>hostname</replaceable>
+ defaults to <literal>localhost</literal> and
+ <replaceable>port</replaceable> defaults to 389.
+ </para>
+
+ <para>
+ Processing of <filename>pg_service.conf</filename> is terminated after
+ a successful LDAP lookup, but is continued if the LDAP server cannot be
+ contacted.  This is to provide a fallback with
+ further LDAP URL lines that point to different LDAP
+ servers, classical <literal>keyword = value</literal> pairs, or
+ default connection options.
+ If you would rather get an error message in this case, add a
+ syntactically incorrect line after the LDAP URL.
+ </para>
+
+ <para>
+ A sample LDAP entry that has been created with the LDIF file
+ <synopsis>
+ version:1
+ dn:cn=mydatabase,dc=mycompany,dc=com
+ changetype:add
+ objectclass:top
+ objectclass:groupOfUniqueNames
+ cn:mydatabase
+ uniqueMember:host=dbserver.mycompany.com
+ uniqueMember:port=5439
+ uniqueMember:dbname=mydb
+ uniqueMember:user=mydb_user
+ uniqueMember:sslmode=require
+ </synopsis>
+ might be queried with the following LDAP URL:
+ <synopsis>
+ ldap://ldap.mycompany.com/dc=mycompany,dc=com?uniqueMember?one?(cn=mydatabase)
+ </synopsis>
+ </para>
+ </sect1>
+
+
  <sect1 id="libpq-ssl">
  <title>SSL Support</title>

Index: src/interfaces/libpq/Makefile
===================================================================
RCS file: /cvsroot/pgsql/src/interfaces/libpq/Makefile,v
retrieving revision 1.146
diff -c -c -r1.146 Makefile
*** src/interfaces/libpq/Makefile    18 Jul 2006 22:18:08 -0000    1.146
--- src/interfaces/libpq/Makefile    26 Jul 2006 16:38:49 -0000
***************
*** 62,68 ****
  SHLIB_LINK += $(filter -lcrypt -ldes -lcom_err -lcrypto -lk5crypto -lkrb5 -lssl -lsocket -lnsl -lresolv -lintl
$(PTHREAD_LIBS),$(LIBS)) 
  endif
  ifeq ($(PORTNAME), win32)
! SHLIB_LINK += -lshfolder -lwsock32 -lws2_32 $(filter -leay32 -lssleay32 -lcomerr32 -lkrb5_32, $(LIBS))
  endif


--- 62,68 ----
  SHLIB_LINK += $(filter -lcrypt -ldes -lcom_err -lcrypto -lk5crypto -lkrb5 -lssl -lsocket -lnsl -lresolv -lintl
$(PTHREAD_LIBS),$(LIBS)) 
  endif
  ifeq ($(PORTNAME), win32)
! SHLIB_LINK += -lshfolder -lwsock32 -lws2_32 $(filter -leay32 -lssleay32 -lcomerr32 -lkrb5_32 -lwldap32, $(LIBS))
  endif


Index: src/interfaces/libpq/fe-connect.c
===================================================================
RCS file: /cvsroot/pgsql/src/interfaces/libpq/fe-connect.c,v
retrieving revision 1.333
diff -c -c -r1.333 fe-connect.c
*** src/interfaces/libpq/fe-connect.c    7 Jun 2006 22:24:46 -0000    1.333
--- src/interfaces/libpq/fe-connect.c    26 Jul 2006 16:38:58 -0000
***************
*** 60,65 ****
--- 60,78 ----
  #endif
  #endif

+ #ifdef USE_LDAP
+ #ifdef WIN32
+ #include <winldap.h>
+ #else
+ /* OpenLDAP deprecates RFC 1823, but we want standard conformance */
+ #define LDAP_DEPRECATED 1
+ #include <ldap.h>
+ typedef struct timeval LDAP_TIMEVAL;
+ #endif
+ static int ldapServiceLookup(const char *purl, PQconninfoOption *options,
+                              PQExpBuffer errorMessage);
+ #endif
+
  #include "libpq/ip.h"
  #include "mb/pg_wchar.h"

***************
*** 2343,2349 ****
--- 2356,2765 ----
      return STATUS_OK;
  }

+ #ifdef USE_LDAP
+
+ #define LDAP_URL    "ldap://"
+ #define LDAP_DEF_PORT    389
+ #define PGLDAP_TIMEOUT 2
+
+ #define ld_is_sp_tab(x) ((x) == ' ' || (x) == '\t')
+ #define ld_is_nl_cr(x) ((x) == '\r' || (x) == '\n')
+
+
+ /*
+  *        ldapServiceLookup
+  *
+  * Search the LDAP URL passed as first argument, treat the result as a
+  * string of connection options that are parsed and added to the array of
+  * options passed as second argument.
+  *
+  * LDAP URLs must conform to RFC 1959 without escape sequences.
+  *    ldap://host:port/dn?attributes?scope?filter?extensions
+  *
+  * Returns
+  *    0 if the lookup was successful,
+  *    1 if the connection to the LDAP server could be established but
+  *      the search was unsuccessful,
+  *    2 if a connection could not be established, and
+  *    3 if a fatal error occurred.
+  *
+  * An error message is returned in the third argument for return codes 1 and 3.
+  */
+ static int
+ ldapServiceLookup(const char *purl, PQconninfoOption *options,
+                   PQExpBuffer errorMessage)
+ {
+     int            port = LDAP_DEF_PORT, scope, rc, msgid, size, state, oldstate, i;
+     bool        found_keyword;
+     char       *url, *hostname, *portstr, *endptr, *dn, *scopestr, *filter,
+                *result, *p, *p1 = NULL, *optname = NULL, *optval = NULL;
+     char       *attrs[2] = {NULL, NULL};
+     LDAP       *ld = NULL;
+     LDAPMessage *res, *entry;
+     struct berval **values;
+     LDAP_TIMEVAL time = {PGLDAP_TIMEOUT, 0};
+
+     if ((url = strdup(purl)) == NULL)
+     {
+         printfPQExpBuffer(errorMessage, libpq_gettext("out of memory\n"));
+         return 3;
+     }
+
+     /*
+      *    Parse URL components, check for correctness.  Basically, url has
+      *    '\0' placed at componient boundaries and variables are pointed
+      *    at each component.
+      */
+
+     if (strncasecmp(url, LDAP_URL, strlen(LDAP_URL)) != 0)
+     {
+         printfPQExpBuffer(errorMessage,
+         libpq_gettext("bad LDAP URL \"%s\": scheme must be ldap://\n"), url);
+         free(url);
+         return 3;
+     }
+
+     /* hostname */
+     hostname = url + strlen(LDAP_URL);
+     if (*hostname == '/')    /* no hostname? */
+         hostname = "localhost";    /* the default */
+
+     /* dn, "distinguished name" */
+     p = strchr(url +  strlen(LDAP_URL), '/');
+     if (p == NULL || *(p + 1) == '\0' || *(p + 1) == '?')
+     {
+         printfPQExpBuffer(errorMessage, libpq_gettext(
+                         "bad LDAP URL \"%s\": missing distinguished name\n"), url);
+         free(url);
+         return 3;
+     }
+     *p = '\0';    /* terminate hostname */
+     dn = p + 1;
+
+     /* attribute */
+     if ((p = strchr(dn, '?')) == NULL || *(p + 1) == '\0' || *(p + 1) == '?')
+     {
+         printfPQExpBuffer(errorMessage, libpq_gettext(
+                             "bad LDAP URL \"%s\": must have exactly one attribute\n"), url);
+         free(url);
+         return 3;
+     }
+     *p = '\0';
+     attrs[0] = p + 1;
+
+     /* scope */
+     if ((p = strchr(attrs[0], '?')) == NULL || *(p + 1) == '\0' || *(p + 1) == '?')
+     {
+         printfPQExpBuffer(errorMessage, libpq_gettext(
+                             "bad LDAP URL \"%s\": must have search scope (base/one/sub)\n"), url);
+         free(url);
+         return 3;
+     }
+     *p = '\0';
+     scopestr = p + 1;
+
+     /* filter */
+     if ((p = strchr(scopestr, '?')) == NULL || *(p + 1) == '\0' || *(p + 1) == '?')
+     {
+         printfPQExpBuffer(errorMessage,
+                     libpq_gettext("bad LDAP URL \"%s\": no filter\n"), url);
+         free(url);
+         return 3;
+     }
+     *p = '\0';
+     filter = p + 1;
+     if ((p = strchr(filter, '?')) != NULL)
+         *p = '\0';
+
+     /* port number? */
+     if (p1 = strchr(hostname, ':')) != NULL)
+     {
+         long        lport;
+
+         *p1 = '\0';
+         portstr = p1 + 1;
+         errno = 0;
+         lport = strtol(portstr, &endptr, 10);
+         if (*portstr == '\0' || *endptr != '\0' || errno || lport < 0 || lport > 65535)
+         {
+             printfPQExpBuffer(errorMessage, libpq_gettext(
+                             "bad LDAP URL \"%s\": invalid port number\n"), url);
+             free(url);
+             return 3;
+         }
+         port = (int) lport;
+     }
+
+     /* Allow only one attribute */
+     if (strchr(attrs[0], ',') != NULL)
+     {
+         printfPQExpBuffer(errorMessage, libpq_gettext(
+                             "bad LDAP URL \"%s\": must have exactly one attribute\n"), url);
+         free(url);
+         return 3;
+     }
+
+     /* set scope */
+     if (strcasecmp(scopestr, "base") == 0)
+         scope = LDAP_SCOPE_BASE;
+     else if (strcasecmp(scopestr, "one") == 0)
+         scope = LDAP_SCOPE_ONELEVEL;
+     else if (strcasecmp(scopestr, "sub") == 0)
+         scope = LDAP_SCOPE_SUBTREE;
+     else
+     {
+         printfPQExpBuffer(errorMessage, libpq_gettext(
+                     "bad LDAP URL \"%s\": must have search scope (base/one/sub)\n"), url);
+         free(url);
+         return 3;
+     }
+
+     /* initialize LDAP structure */
+     if ((ld = ldap_init(hostname, port)) == NULL)
+     {
+         printfPQExpBuffer(errorMessage,
+                           libpq_gettext("error creating LDAP structure\n"));
+         free(url);
+         return 3;
+     }

+     /*
+      *    Initialize connection to the server.  We do an explicit bind because
+      *    we want to return 2 if the bind fails.
+      */
+     if ((msgid = ldap_simple_bind(ld, NULL, NULL)) == -1)
+     {
+         /* error in ldap_simple_bind() */
+         free(url);
+         ldap_unbind(ld);
+         return 2;
+     }
+
+     /* wait some time for the connection to succeed */
+     res = NULL;
+     if (rc = ldap_result(ld, msgid, LDAP_MSG_ALL, &time, &res)) == -1 ||
+         res == NULL)
+     {
+         if (res != NULL)
+         {
+             /* timeout */
+             ldap_msgfree(res);
+         }
+         /* error in ldap_result() */
+         free(url);
+         ldap_unbind(ld);
+         return 2;
+     }
+     ldap_msgfree(res);
+
+     /* search */
+     res = NULL;
+     if ((rc = ldap_search_st(ld, dn, scope, filter, attrs, 0, &time, &res))
+         != LDAP_SUCCESS)
+     {
+         if (res != NULL)
+             ldap_msgfree(res);
+         printfPQExpBuffer(errorMessage,
+                           libpq_gettext("lookup on LDAP server failed: %s\n"),
+                           ldap_err2string(rc));
+         ldap_unbind(ld);
+         free(url);
+         return 1;
+     }
+
+     /* complain if there was not exactly one result */
+     if ((rc = ldap_count_entries(ld, res)) != 1)
+     {
+         printfPQExpBuffer(errorMessage,
+              rc ? libpq_gettext("more than one entry found on LDAP lookup\n")
+                           : libpq_gettext("no entry found on LDAP lookup\n"));
+         ldap_msgfree(res);
+         ldap_unbind(ld);
+         free(url);
+         return 1;
+     }
+
+     /* get entry */
+     if ((entry = ldap_first_entry(ld, res)) == NULL)
+     {
+         /* should never happen */
+         printfPQExpBuffer(errorMessage,
+                           libpq_gettext("no entry found on LDAP lookup\n"));
+         ldap_msgfree(res);
+         ldap_unbind(ld);
+         free(url);
+         return 1;
+     }
+
+     /* get values */
+     if ((values = ldap_get_values_len(ld, entry, attrs[0])) == NULL)
+     {
+         printfPQExpBuffer(errorMessage,
+                   libpq_gettext("attribute has no values on LDAP lookup\n"));
+         ldap_msgfree(res);
+         ldap_unbind(ld);
+         free(url);
+         return 1;
+     }
+
+     ldap_msgfree(res);
+     free(url);
+
+     if (values[0] == NULL)
+     {
+         printfPQExpBuffer(errorMessage,
+                   libpq_gettext("attribute has no values on LDAP lookup\n"));
+         ldap_value_free_len(values);
+         ldap_unbind(ld);
+         return 1;
+     }
+
+     /* concatenate values to a single string */
+     for (size = 0, i = 0; values[i] != NULL; ++i)
+         size += values[i]->bv_len + 1;
+     if ((result = malloc(size + 1)) == NULL)
+     {
+         printfPQExpBuffer(errorMessage,
+                           libpq_gettext("out of memory\n"));
+         ldap_value_free_len(values);
+         ldap_unbind(ld);
+         return 3;
+     }
+     for (p = result, i = 0; NULL != values[i]; ++i)
+     {
+         strncpy(p, values[i]->bv_val, values[i]->bv_len);
+         p += values[i]->bv_len;
+         *(p++) = '\n';
+         if (values[i + 1] == NULL)
+             *(p + 1) = '\0';
+     }
+
+     ldap_value_free_len(values);
+     ldap_unbind(ld);
+
+     /* parse result string */
+     oldstate = state = 0;
+     for (p = result; *p != '\0'; ++p)
+     {
+         switch (state)
+         {
+             case 0:                /* between entries */
+                 if (!ld_is_sp_tab(*p) && !ld_is_nl_cr(*p))
+                 {
+                     optname = p;
+                     state = 1;
+                 }
+                 break;
+             case 1:                /* in option name */
+                 if (ld_is_sp_tab(*p))
+                 {
+                     *p = '\0';
+                     state = 2;
+                 }
+                 else if (ld_is_nl_cr(*p))
+                 {
+                     printfPQExpBuffer(errorMessage, libpq_gettext(
+                                 "missing \"=\" after \"%s\" in connection info string\n"),
+                                   optname);
+                     return 3;
+                 }
+                 else if (*p == '=')
+                 {
+                     *p = '\0';
+                     state = 3;
+                 }
+                 break;
+             case 2:                /* after option name */
+                 if (*p == '=')
+                 {
+                     state = 3;
+                 }
+                 else if (!ld_is_sp_tab(*p))
+                 {
+                     printfPQExpBuffer(errorMessage, libpq_gettext(
+                                 "missing \"=\" after \"%s\" in connection info string\n"),
+                                   optname);
+                     return 3;
+                 }
+                 break;
+             case 3:                /* before option value */
+                 if (*p == '\'')
+                 {
+                     optval = p + 1;
+                     p1 = p + 1;
+                     state = 5;
+                 }
+                 else if (ld_is_nl_cr(*p))
+                 {
+                     optval = optname + strlen(optname); /* empty */
+                     state = 0;
+                 }
+                 else if (!ld_is_sp_tab(*p))
+                 {
+                     optval = p;
+                     state = 4;
+                 }
+                 break;
+             case 4:                /* in unquoted option value */
+                 if (ld_is_sp_tab(*p) || ld_is_nl_cr(*p))
+                 {
+                     *p = '\0';
+                     state = 0;
+                 }
+                 break;
+             case 5:                /* in quoted option value */
+                 if (*p == '\'')
+                 {
+                     *p1 = '\0';
+                     state = 0;
+                 }
+                 else if (*p == '\\')
+                     state = 6;
+                 else
+                     *(p1++) = *p;
+                 break;
+             case 6:                /* in quoted option value after escape */
+                 *(p1++) = *p;
+                 state = 5;
+                 break;
+         }
+
+         if (state == 0 && oldstate != 0)
+         {
+             found_keyword = false;
+             for (i = 0; options[i].keyword; i++)
+             {
+                 if (strcmp(options[i].keyword, optname) == 0)
+                 {
+                     if (options[i].val == NULL)
+                         options[i].val = strdup(optval);
+                     found_keyword = true;
+                     break;
+                 }
+             }
+             if (!found_keyword)
+             {
+                 printfPQExpBuffer(errorMessage,
+                          libpq_gettext("invalid connection option \"%s\"\n"),
+                           optname);
+                 return 1;
+             }
+             optname = NULL;
+             optval = NULL;
+         }
+         oldstate = state;
+     }
+
+     if (state == 5 || state == 6)
+     {
+         printfPQExpBuffer(errorMessage, libpq_gettext(
+                         "unterminated quoted string in connection info string\n"));
+         return 3;
+     }
+
+     return 0;
+ }
+ #endif

  #define MAXBUFSIZE 256

***************
*** 2439,2444 ****
--- 2855,2880 ----
                                 *val;
                      bool        found_keyword;

+ #ifdef USE_LDAP
+                     if (strncmp(line, "ldap", 4) == 0)
+                     {
+                         int rc = ldapServiceLookup(line, options, errorMessage);
+                         /* if rc = 2, go on reading for fallback */
+                         switch (rc)
+                         {
+                             case 0:
+                                 fclose(f);
+                                 return 0;
+                             case 1:
+                             case 3:
+                                 fclose(f);
+                                 return 3;
+                             case 2:
+                                 continue;
+                         }
+                     }
+ #endif
+
                      key = line;
                      val = strchr(line, '=');
                      if (val == NULL)

Re: LDAP lookup of connection parameters

От
"Albe Laurenz"
Дата:
Bruce Momjian wrote:
>>>> This patch for libpq allows you to enter an LDAP URL in
>>>> pg_service.conf.
>>>> The URL will be queried and the resulting string(s) parsed for
>>>> keyword = value connection options.
>>>
>>> I have heavily modified your patch to be clearer.  Please
>>> review the
>>> attached version and test it to make sure it still works
>>> properly.
>
> Thanks for the review.  Updated patch attached.  Is that OK?

Mostly yes, and I must admit that the code has become more
readable. There were two syntax errors and some minor odds
and ends (partly my fault).

I have slightly changed your patch and submit it again
(diff the patches to see).

Thanks for the effort,
Laurenz Albe


Вложения

Re: LDAP lookup of connection parameters

От
Bruce Momjian
Дата:
Patch applied.  Thanks.

---------------------------------------------------------------------------


Albe Laurenz wrote:
> Bruce Momjian wrote:
> >>>> This patch for libpq allows you to enter an LDAP URL in
> >>>> pg_service.conf.
> >>>> The URL will be queried and the resulting string(s) parsed for
> >>>> keyword = value connection options.
> >>>
> >>> I have heavily modified your patch to be clearer.  Please
> >>> review the
> >>> attached version and test it to make sure it still works
> >>> properly.
> >
> > Thanks for the review.  Updated patch attached.  Is that OK?
>
> Mostly yes, and I must admit that the code has become more
> readable. There were two syntax errors and some minor odds
> and ends (partly my fault).
>
> I have slightly changed your patch and submit it again
> (diff the patches to see).
>
> Thanks for the effort,
> Laurenz Albe
>

Content-Description: ldap_v2.patch

[ Attachment, skipping... ]

--
  Bruce Momjian   bruce@momjian.us
  EnterpriseDB    http://www.enterprisedb.com

  + If your life is a hard drive, Christ can be your backup. +