Skip to content

LDAP-FDW: originating DN + write-support + multiple server support #155

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 64 additions & 11 deletions python/multicorn/ldapfdw.py
Original file line number Diff line number Diff line change
Expand Up @@ -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".
Expand Down Expand Up @@ -56,6 +56,7 @@
);

CREATE FOREIGN TABLE ldapexample (
dn character varying,
mail character varying,
cn character varying,
description character varying
Expand Down Expand Up @@ -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"]
Expand All @@ -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]
Expand All @@ -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
Expand All @@ -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)