diff options
author | Arthur de Jong <arthur@arthurdejong.org> | 2011-12-12 22:53:10 +0100 |
---|---|---|
committer | Arthur de Jong <arthur@arthurdejong.org> | 2011-12-12 22:53:10 +0100 |
commit | 330a28040095fff67e9cb105019f3b4cb7cb5f0e (patch) | |
tree | 802d2a763f9210a3a1f581a9f758bdde301b96df /pynslcd/common.py | |
parent | f4b3ad848987eb6ac2cf50d7ea99d1a7b579c70c (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.py | 82 |
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] |