Arthur de Jong

Open Source / Free Software developer

summaryrefslogtreecommitdiffstats
path: root/pynslcd/common.py
diff options
context:
space:
mode:
authorArthur de Jong <arthur@arthurdejong.org>2011-12-12 22:53:10 +0100
committerArthur de Jong <arthur@arthurdejong.org>2011-12-12 22:53:10 +0100
commit330a28040095fff67e9cb105019f3b4cb7cb5f0e (patch)
tree802d2a763f9210a3a1f581a9f758bdde301b96df /pynslcd/common.py
parentf4b3ad848987eb6ac2cf50d7ea99d1a7b579c70c (diff)
move check of required attributes and other common tests to the Request.handle_entry() method
git-svn-id: http://arthurdejong.org/svn/nss-pam-ldapd/nss-pam-ldapd@1570 ef36b2f9-881f-0410-afb5-c4e39611909c
Diffstat (limited to 'pynslcd/common.py')
-rw-r--r--pynslcd/common.py82
1 files changed, 68 insertions, 14 deletions
diff --git a/pynslcd/common.py b/pynslcd/common.py
index 1f50584..359b220 100644
--- a/pynslcd/common.py
+++ b/pynslcd/common.py
@@ -30,6 +30,7 @@ from attmap import Attributes
_validname_re = re.compile(r'^[a-z0-9._@$][a-z0-9._@$ \\~-]{0,98}[a-z0-9._@$~-]$', re.IGNORECASE)
+
def isvalidname(name):
"""Checks to see if the specified name seems to be a valid user or group
name.
@@ -45,6 +46,7 @@ def isvalidname(name):
character. As an extension this test allows some more characters."""
return bool(_validname_re.match(name))
+
def validate_name(name):
"""Checks to see if the specified name seems to be a valid user or group
name. See isvalidname()."""
@@ -57,12 +59,29 @@ class Request(object):
Request handler class. Subclasses are expected to handle actual requests
and should implement the following members:
- action: the NSLCD_ACTION_* action that should trigger this handler
- read_parameters: a function that reads the request parameters of the
- request stream
- filter: LDAP search filter
- mk_filter (optional): function that returns the LDAP search filter
- write: function that writes a single LDAP entry to the result stream
+ action - the NSLCD_ACTION_* action that should trigger this handler
+
+ case_sensitive - check that these attributes are present in the response
+ if they were in the request
+ case_insensitive - check that these attributes are present in the
+ response if they were in the request
+ limit_attributes - override response attributes with request attributes
+ required - attributes that are required
+ canonical_first - search the DN for these attributes and ensure that
+ they are listed first in the attribute values
+ read_parameters() - a function that reads the request parameters of the
+ request stream
+ mk_filter() (optional) - function that returns the LDAP search filter
+ write() - function that writes a single LDAP entry to the result stream
+
+ The module that contains the Request class can also contain the following
+ definitions:
+
+ attmap - an attribute mapping definition (using he Attributes class)
+ filter - an LDAP search filter
+ bases - search bases to be used, falls back to cfg.bases
+ scope - search scope, falls back to cfg.scope
+
"""
def __init__(self, fp, conn, calleruid):
@@ -81,19 +100,52 @@ class Request(object):
store them in self."""
pass
- def attributes(self):
- """Return the attributes that should be used in the LDAP search."""
- return self.attmap.attributes()
-
def mk_filter(self, parameters):
"""Return the active search filter (based on the read parameters)."""
if parameters:
- return '(&%s(%s))' % ( self.filter,
+ return '(&%s(%s))' % (self.filter,
')('.join('%s=%s' % (self.attmap[attribute],
ldap.filter.escape_filter_chars(str(value)))
- for attribute, value in parameters.items()) )
+ for attribute, value in parameters.items()))
return self.filter
+ def handle_entry(self, dn, attributes, parameters):
+ """Handle an entry with the specified attributes, filtering it with
+ the request parameters where needed."""
+ # translate the attributes using the attribute mapping
+ attributes = self.attmap.translate(attributes)
+ # make sure value from DN is first value
+ for attr in getattr(self, 'canonical_first', []):
+ primary_value = get_rdn_value(dn, self.attmap[attr])
+ if primary_value:
+ values = attributes[attr]
+ if primary_value in values:
+ values.remove(primary_value)
+ attributes[attr] = [primary_value] + values
+ # check that these attributes have at least one value
+ for attr in getattr(self, 'required', []):
+ if not attributes.get(attr, None):
+ print '%s: attribute %s not found' % (dn, self.attmap[attr])
+ return
+ # check that requested attribute is present (case sensitive)
+ for attr in getattr(self, 'case_sensitive', []):
+ value = parameters.get(attr, None)
+ if value and str(value) not in attributes[attr]:
+ print '%s: attribute %s does not contain %r value' % (dn, self.attmap[attr], value)
+ return # not found, skip entry
+ # check that requested attribute is present (case insensitive)
+ for attr in getattr(self, 'case_insensitive', []):
+ value = parameters.get(attr, None)
+ if value and str(value).lower() not in (x.lower() for x in attributes[attr]):
+ print '%s: attribute %s does not contain %r value' % (dn, self.attmap[attr], value)
+ return # not found, skip entry
+ # limit attribute values to requested value
+ for attr in getattr(self, 'limit_attributes', []):
+ if attr in parameters:
+ attributes[attr] = [parameters[attr]]
+ # write the result entry
+ self.write(dn, attributes, parameters)
+
def handle_request(self, parameters):
"""This method handles the request based on the parameters read
with read_parameters()."""
@@ -101,10 +153,11 @@ class Request(object):
for base in self.bases:
# do the LDAP search
try:
- res = self.conn.search_s(base, self.scope, self.mk_filter(parameters), self.attributes())
+ res = self.conn.search_s(base, self.scope, self.mk_filter(parameters),
+ self.attmap.attributes())
for entry in res:
if entry[0]:
- self.write(entry[0], self.attmap.mapped(entry[1]), parameters)
+ self.handle_entry(entry[0], entry[1], parameters)
except ldap.NO_SUCH_OBJECT:
# FIXME: log message
pass
@@ -130,5 +183,6 @@ def get_handlers(module):
res[cls.action] = cls
return res
+
def get_rdn_value(dn, attribute):
return dict((x, y) for x, y, z in ldap.dn.str2dn(dn)[0])[attribute]