diff --git a/python/multicorn/ldapfdw.py b/python/multicorn/ldapfdw.py index 46f81c1c3..b47d263cb 100755 --- a/python/multicorn/ldapfdw.py +++ b/python/multicorn/ldapfdw.py @@ -23,7 +23,7 @@ ---------------- ``uri`` (string) -The URI for the server, for example "ldap://localhost". +The URI(s) for the server(s), for example "ldap://localhost,ldap://fallback". ``path`` (string) The base in which the search is performed, for example "dc=example,dc=com". @@ -56,6 +56,7 @@ ); CREATE FOREIGN TABLE ldapexample ( + dn character varying, mail character varying, cn character varying, description character varying @@ -115,15 +116,25 @@ class LdapFdw(ForeignDataWrapper): def __init__(self, fdw_options, fdw_columns): super(LdapFdw, self).__init__(fdw_options, fdw_columns) + self._row_id_column = "dn" + + self.uris = [] if "address" in fdw_options: - self.ldapuri = "ldap://" + fdw_options["address"] + for addr in fdw_options["address"].split(","): + self.uris.append("ldap://" + addr) else: - self.ldapuri = fdw_options["uri"] - self.ldap = ldap3.Connection( - ldap3.Server(self.ldapuri), - user=fdw_options.get("binddn", None), - password=fdw_options.get("bindpwd", None), - client_strategy=ldap3.STRATEGY_SYNC_RESTARTABLE) + for uri in fdw_options["uri"].split(","): + self.uris.append(uri) + + for uri in self.uris: + self.ldap = ldap3.Connection( + ldap3.Server(uri), + user=fdw_options.get("binddn", None), + password=fdw_options.get("bindpwd", None), + client_strategy=ldap3.STRATEGY_SYNC_RESTARTABLE) + if self.ldap != None: + break + self.path = fdw_options["path"] self.scope = self.parse_scope(fdw_options.get("scope", None)) self.object_class = fdw_options["objectclass"] @@ -136,6 +147,7 @@ def __init__(self, fdw_options, fdw_columns): def execute(self, quals, columns): request = unicode_("(objectClass=%s)") % self.object_class + path = self.path for qual in quals: if isinstance(qual.operator, tuple): operator = qual.operator[0] @@ -148,14 +160,25 @@ def execute(self, quals, columns): if operator == "~~" else baseval) else: val = qual.value - request = unicode_("(&%s(%s=%s))") % ( - request, qual.field_name, val) + + if qual.field_name != "dn": + request = unicode_("(&%s(%s=%s))") % ( + request, qual.field_name, val) + else: + if path == self.path and val.endswith(self.path): + path = val + else: + log_to_postgres( + "Only one instance of a DN can be used as filter, " + "and it must end with the user defined base path.", ERROR) + self.ldap.search( - self.path, request, self.scope, + path, request, self.scope, attributes=list(self.field_definitions)) for entry in self.ldap.response: # Case insensitive lookup for the attributes litem = dict() + litem["dn"] = entry["dn"] for key, value in entry["attributes"].items(): if key.lower() in self.field_definitions: pgcolname = self.field_definitions[key.lower()].column_name @@ -175,3 +198,33 @@ def parse_scope(self, scope=None): return ldap3.SEARCH_SCOPE_BASE_OBJECT else: log_to_postgres("Invalid scope specified: %s" % scope, ERROR) + + @property + def rowid_column(self): + return self._row_id_column + + def insert(self, values): + self.ldap.add(values.pop("dn"), attributes=values) + if self.ldap.result["result"]: + log_to_postgres( + self.ldap.server.host + ": The ADD operation failed.\n " + self.ldap.result["message"], + ERROR) + + def update(self, dn, newvalues): + changes = {} + newvalues.pop("dn", None) + for k, v in newvalues.iteritems(): + changes[k] = [(ldap3.MODIFY_REPLACE, v)] + + self.ldap.modify(dn, changes) + if self.ldap.result["result"]: + log_to_postgres( + self.ldap.server.host + ": The MODIFY operation failed.\n " + self.ldap.result["message"], + ERROR) + + def delete(self, dn): + self.ldap.delete(dn) + if self.ldap.result["result"]: + log_to_postgres( + self.ldap.server.host + ": The DELETE operation failed.\n " + self.ldap.result["message"], + ERROR)