lists.arthurdejong.org
RSS feed

nss-pam-ldapd commit: r1454 - nss-pam-ldapd/pynslcd

[Date Prev][Date Next] [Thread Prev][Thread Next]

nss-pam-ldapd commit: r1454 - nss-pam-ldapd/pynslcd



Author: arthur
Date: Sun May  1 21:08:39 2011
New Revision: 1454
URL: http://arthurdejong.org/viewvc/nss-pam-ldapd?view=rev&revision=1454

Log:
implement attribute mapping functionality and do some refactoring

Added:
   nss-pam-ldapd/pynslcd/attmap.py
Modified:
   nss-pam-ldapd/pynslcd/alias.py
   nss-pam-ldapd/pynslcd/common.py
   nss-pam-ldapd/pynslcd/ether.py
   nss-pam-ldapd/pynslcd/group.py
   nss-pam-ldapd/pynslcd/host.py
   nss-pam-ldapd/pynslcd/netgroup.py
   nss-pam-ldapd/pynslcd/network.py
   nss-pam-ldapd/pynslcd/pam.py
   nss-pam-ldapd/pynslcd/passwd.py
   nss-pam-ldapd/pynslcd/protocol.py
   nss-pam-ldapd/pynslcd/rpc.py
   nss-pam-ldapd/pynslcd/service.py
   nss-pam-ldapd/pynslcd/shadow.py
   nss-pam-ldapd/pynslcd/tio.py

Modified: nss-pam-ldapd/pynslcd/alias.py
==============================================================================
--- nss-pam-ldapd/pynslcd/alias.py      Sun May  1 14:18:06 2011        (r1453)
+++ nss-pam-ldapd/pynslcd/alias.py      Sun May  1 21:08:39 2011        (r1454)
@@ -18,35 +18,32 @@
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 # 02110-1301 USA
 
-import ldap.filter
+import logging
 
 import constants
 import common
 
 
-class AliasRequest(common.Request):
-
-    filter = '(objectClass=nisMailAlias)'
+attmap = common.Attributes(cn='cn', rfc822MailMember='rfc822MailMember')
+filter = '(objectClass=nisMailAlias)'
 
-    attmap_cn         = 'cn'
-    attmap_rfc822MailMember = 'rfc822MailMember'
 
-    attributes = ( 'cn', 'rfc822MailMember' )
+class AliasRequest(common.Request):
 
     def write(self, dn, attributes):
         # get name and check against requested name
-        names = attributes.get(self.attmap_cn, [])
+        names = attributes['cn']
         if not names:
-            logging.error('Error: entry %s does not contain %s value', dn, 
self.attmap_cn)
+            logging.error('Error: entry %s does not contain %s value', dn, 
attmap['cn'])
             return
         if self.name:
             if self.name.lower() not in (x.lower() for x in names):
                 return
             names = ( self.name, )
         # get the members of the alias
-        members = attributes.get(self.attmap_rfc822MailMember, [])
+        members = attributes['rfc822MailMember']
         if not members:
-            logging.error('Error: entry %s does not contain %s value', dn, 
self.attmap_rfc822MailMember)
+            logging.error('Error: entry %s does not contain %s value', dn, 
attmap['rfc822MailMember'])
             return
         # write results
         for name in names:
@@ -58,14 +55,11 @@
 class AliasByNameRequest(AliasRequest):
 
     action = constants.NSLCD_ACTION_ALIAS_BYNAME
+    filter_attrs = dict(cn='name')
 
     def read_parameters(self):
         self.name = self.fp.read_string()
 
-    def mk_filter(self):
-        return '(&%s(%s=%s))' % ( self.filter,
-                  self.attmap_cn, ldap.filter.escape_filter_chars(self.name) )
-
 
 class AliasAllRequest(AliasRequest):
 

Added: nss-pam-ldapd/pynslcd/attmap.py
==============================================================================
--- /dev/null   00:00:00 1970   (empty, because file is newly added)
+++ nss-pam-ldapd/pynslcd/attmap.py     Sun May  1 21:08:39 2011        (r1454)
@@ -0,0 +1,194 @@
+
+# attributes.py - attribute mapping functions
+#
+# Copyright (C) 2011 Arthur de Jong
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 USA
+
+"""Module for handling attribute mappings used for LDAP searches.
+
+>>> attrs = Attributes(uid='uid',
+...                    userPassword='userPassword',
+...                    uidNumber='uidNumber',
+...                    gidNumber='gidNumber',
+...                    gecos='"${gecos:-$cn}"',
+...                    homeDirectory='homeDirectory',
+...                    loginShell='loginShell')
+>>> attrs.attributes()
+('uid', 'userPassword', 'uidNumber', 'gidNumber', 'gecos', 'cn', 
'homeDirectory', 'loginShell')
+>>> attrs.value('gecos', {'cn': 'test'})
+['test']
+>>> attrs.search('uidNumber', 100)
+'(uidNumber=100)'
+>>> attrs['foo'] = '\"bar\"'
+>>> attrs.get('foo', {})
+['bar']
+"""
+
+# exported names
+__all__ = ( 'Attributes', )
+
+
+# FIXME: support multiple attribute values
+# TODO: support objectSid attributes
+# TODO: do more expression validity checking
+# TODO: handle userPassword specially to do filtering of results
+
+
+class MyIter(object):
+    """Custom iterator-like class with a back() method."""
+
+    def __init__(self, value):
+        self.value = value
+        self.pos = 0
+
+    def next(self):
+        self.pos += 1
+        return self.value[self.pos-1]
+
+    def back(self):
+        self.pos -= 1
+
+    def __iter__(self):
+        return self
+
+
+class DollarExpression(object):
+    """Class for handling a variable $xxx ${xxx}, ${xxx:-yyy} or ${xxx:+yyy}
+    expression."""
+
+    def _parse_varname(self, value):
+        """Read a variable name from the value iterator."""
+        name = ''
+        for c in value:
+            if not c.isalnum():
+                value.back()
+                return name
+            name += c
+
+    def __init__(self, value):
+        """Parse the expression as the start of a $-expression."""
+        self.op = None
+        self.expr = None
+        c = value.next()
+        if c == '{':
+            self.name = self._parse_varname(value)
+            c = value.next()
+            if c == '}':
+                return
+            self.op = c + value.next()
+            self.expr = Expression(value, endat='}')
+        else:
+            value.back()
+            self.name = self._parse_varname(value)
+
+    def value(self, variables):
+        """Expand the expression using the variables specified."""
+        value = variables.get(self.name, [''])[0]
+        # FIXME: expand list
+        if self.op == ':-':
+            return value if value else self.expr.value(variables)
+        elif self.op == ':+':
+            return self.expr.value(variables) if value else ''
+        return value
+
+    def variables(self, results):
+        """Add the variables used in the expression to results."""
+        results.add(self.name)
+        if self.expr:
+            self.expr.variables(results)
+
+
+class Expression(object):
+    """Class for parsing and expanding an expression."""
+
+    def __init__(self, value, endat=None):
+        """Parse the expression as a string."""
+        if not isinstance(value, MyIter):
+            value = MyIter(value)
+        if not endat:
+            endat = value.next() # skip opening quote
+        expr = []
+        literal = ''
+        c = value.next()
+        while c != endat:
+            if c == '$':
+                if literal:
+                    expr.append(literal)
+                expr.append(DollarExpression(value))
+                literal = ''
+            elif c == '\\':
+                literal += value.next()
+            else:
+                literal += c
+            c = value.next()
+        if literal:
+            expr.append(literal)
+        self.expr = expr
+
+    def value(self, variables):
+        """Expand the expression using the variables specified."""
+        res = ''
+        for x in self.expr:
+            if hasattr(x, 'value'):
+                res += x.value(variables)
+            else:
+                res += x
+        return res
+
+    def variables(self, results=None):
+        """Return the variables defined in the expression."""
+        if not results:
+            results = set()
+        for x in self.expr:
+            if hasattr(x, 'variables'):
+                x.variables(results)
+        return results
+
+
+class Attributes(dict):
+    """Dictionary-like class for handling a list of attributes."""
+
+    def _prepare(self):
+        """Go over all values to parse any expressions."""
+        updates = dict()
+        for k, v in self.iteritems():
+            if isinstance(v, basestring) and v[0] == '"':
+                updates[k] = Expression(v)
+        self.update(updates)
+
+    def attributes(self):
+        """Return a set of attributes that are referenced in this attribute
+        mapping."""
+        self._prepare()
+        results = set()
+        for value in self.itervalues():
+            if hasattr(value, 'variables'):
+                results.update(value.variables())
+            else:
+                results.add(value)
+        return list(results)
+
+    def mapped(self, variables):
+        """Return a dictionary with every attribute mapped to their value from
+        the specified variables."""
+        results = dict()
+        for k, v in self.iteritems():
+            if hasattr(v, 'value'):
+                results[k] = [v.value(variables)]
+            else:
+                results[k] = variables.get(v, [])
+        return results

Modified: nss-pam-ldapd/pynslcd/common.py
==============================================================================
--- nss-pam-ldapd/pynslcd/common.py     Sun May  1 14:18:06 2011        (r1453)
+++ nss-pam-ldapd/pynslcd/common.py     Sun May  1 21:08:39 2011        (r1454)
@@ -21,9 +21,11 @@
 import re
 import ldap
 import ldap.dn
+import sys
 
 import cfg
 import constants
+from attmap import Attributes
 
 
 _validname_re = re.compile(r'^[a-z0-9._@$][a-z0-9._@$ 
\\~-]{0,98}[a-z0-9._@$~-]$', re.IGNORECASE)
@@ -63,9 +65,6 @@
     write: function that writes a single LDAP entry to the result stream
     """
 
-    bases = cfg.bases
-    scope = cfg.scope
-
     def __init__(self, fp, conn, calleruid):
         self.fp = fp
         self.conn = conn
@@ -75,14 +74,28 @@
         self.uid = None
         self.gid = None
         self.address = None
+        module = sys.modules[self.__module__]
+        self.attmap = module.attmap
+        self.filter = module.filter
+        self.bases = getattr(module, 'bases', cfg.bases)
+        self.scope = getattr(module, 'scope', cfg.scope)
 
     def read_parameters(self):
         """This method should read the parameters from ths stream and
         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):
         """Return the active search filter (based on the read parameters)."""
+        if hasattr(self, 'filter_attrs'):
+            return '(&%s(%s))' % ( self.filter,
+                ')('.join('%s=%s' % (self.attmap[attribute],
+                                     
ldap.filter.escape_filter_chars(str(getattr(self, name))))
+                          for attribute, name in self.filter_attrs.items()) )
         return self.filter
 
     def handle_request(self):
@@ -92,10 +105,10 @@
         for base in self.bases:
             # do the LDAP search
             try:
-                res = self.conn.search_s(base, self.scope, self.mk_filter(), 
self.attributes)
+                res = self.conn.search_s(base, self.scope, self.mk_filter(), 
self.attributes())
                 for entry in res:
                     if entry[0]:
-                        self.write(entry[0], entry[1])
+                        self.write(entry[0], self.attmap.mapped(entry[1]))
             except ldap.NO_SUCH_OBJECT:
                 # FIXME: log message
                 pass
@@ -122,5 +135,4 @@
     return res
 
 def get_rdn_value(dn, attribute):
-    dn, attributes = entry
     return dict((x, y) for x, y, z in ldap.dn.str2dn(dn)[0])[attribute]

Modified: nss-pam-ldapd/pynslcd/ether.py
==============================================================================
--- nss-pam-ldapd/pynslcd/ether.py      Sun May  1 14:18:06 2011        (r1453)
+++ nss-pam-ldapd/pynslcd/ether.py      Sun May  1 21:08:39 2011        (r1454)
@@ -19,27 +19,26 @@
 # 02110-1301 USA
 
 import struct
-import ldap.filter
 
 import constants
 import common
 
 
 def ether_aton(ether):
+    """Converst an ethernet address to binary form in network byte order."""
     return struct.pack('BBBBBB', *(int(x, 16) for x in ether.split(':')))
 
 def ether_ntoa(ether):
+    """Conversts an ethernet address in network byte order to the string
+    representation."""
     return ':'.join('%x' % x for x in struct.unpack('6B', ether))
 
 
-class EtherRequest(common.Request):
-
-    filter = '(objectClass=ieee802Device)'
+attmap = common.Attributes(cn='cn', macAddress='macAddress')
+filter = '(objectClass=ieee802Device)'
 
-    attmap_cn         = 'cn'
-    attmap_macAddress = 'macAddress'
 
-    attributes = ( 'cn', 'macAddress' )
+class EtherRequest(common.Request):
 
     def __init__(self, *args):
         super(EtherRequest, self).__init__(*args)
@@ -47,17 +46,17 @@
 
     def write(self, dn, attributes):
         # get name and check against requested name
-        names = attributes.get(self.attmap_cn, [])
+        names = attributes['cn']
         if not names:
-            print 'Error: entry %s does not contain %s value' % ( dn, 
self.attmap_cn)
+            print 'Error: entry %s does not contain %s value' % ( dn, 
attmap['cn'])
         if self.name:
             if self.name.lower() not in (x.lower() for x in names):
                 return # skip entry
             names = ( self.name, )
         # get addresses and convert to binary form
-        addresses = [ether_aton(x) for x in 
attributes.get(self.attmap_macAddress, [])]
+        addresses = [ether_aton(x) for x in attributes['macAddress']]
         if not addresses:
-            print 'Error: entry %s does not contain %s value' % ( dn, 
self.attmap_macAddress)
+            print 'Error: entry %s does not contain %s value' % ( dn, 
attmap['macAddress'])
         if self.ether:
             if self.ether not in addresses:
                 return
@@ -73,14 +72,11 @@
 class EtherByNameRequest(EtherRequest):
 
     action = constants.NSLCD_ACTION_ETHER_BYNAME
+    filter_attrs = dict(cn='name')
 
     def read_parameters(self):
         self.name = self.fp.read_string()
 
-    def mk_filter(self):
-        return '(&%s(%s=%s))' % ( self.filter,
-                  self.attmap_cn, ldap.filter.escape_filter_chars(self.name) )
-
 
 class EtherByEtherRequest(EtherRequest):
 
@@ -91,7 +87,7 @@
 
     def mk_filter(self):
         return '(&%s(%s=%s))' % ( self.filter,
-                  self.attmap_macAddress, ether_ntoa(self.ether) )
+                  attmap['macAddress'], ether_ntoa(self.ether) )
 
 
 class EtherAllRequest(EtherRequest):

Modified: nss-pam-ldapd/pynslcd/group.py
==============================================================================
--- nss-pam-ldapd/pynslcd/group.py      Sun May  1 14:18:06 2011        (r1453)
+++ nss-pam-ldapd/pynslcd/group.py      Sun May  1 21:08:39 2011        (r1454)
@@ -23,53 +23,50 @@
 
 import constants
 import common
+from passwd import dn2uid, uid2dn
 
 
 def clean(lst):
-    for i in lst:
-        yield i.replace('\0', '')
+    if lst:
+        for i in lst:
+            yield i.replace('\0', '')
 
 
-class GroupRequest(common.Request):
-
-    filter = '(|(objectClass=posixGroup)(objectClass=groupOfUniqueNames))'
+attmap = common.Attributes(cn='cn',
+                           userPassword='"*"',
+                           gidNumber='gidNumber',
+                           memberUid='memberUid',
+                           uniqueMember='uniqueMember')
+filter = '(|(objectClass=posixGroup)(objectClass=groupOfUniqueNames))'
 
-    attmap_group_cn           = 'cn'
-    attmap_group_userPassword = 'userPassword'
-    attmap_group_gidNumber    = 'gidNumber'
-    attmap_group_memberUid    = 'memberUid'
-    attmap_group_uniqueMember = 'uniqueMember'
 
-    attributes = ( 'cn', 'userPassword', 'gidNumber', 'memberUid',
-                   'uniqueMember' )
+class GroupRequest(common.Request):
 
     wantmembers = True
 
-    def write(self, entry):
-        dn, attributes = entry
+    def write(self, dn, attributes):
         # get group names and check against requested group name
-        names = attributes.get(self.attmap_group_cn, [])
+        names = attributes['cn']
         if self.name:
             if self.name not in names:
                 return
             names = ( self.name, )
         # get group group password
-        ( passwd, ) = attributes.get(self.attmap_group_userPassword, ['*'])
+        passwd = attributes['userPassword'][0]
         # get group id(s)
-        gids = ( self.gid, ) if self.gid else 
attributes.get(self.attmap_group_gidNumber, [])
+        gids = ( self.gid, ) if self.gid else attributes['gidNumber']
         gids = [ int(x) for x in gids ]
         # build member list
         members = set()
         if self.wantmembers:
             # add the memberUid values
-            for member in clean(attributes.get(self.attmap_group_memberUid, 
[])):
+            for member in clean(attributes['memberUid']):
                 if common.isvalidname(member):
                     members.add(member)
             # translate and add the uniqueMember values
-            from passwd import dn2uid
-            for memberdn in 
clean(attributes.get(self.attmap_group_uniqueMember, [])):
+            for memberdn in clean(attributes['uniqueMember']):
                 member = dn2uid(self.conn, memberdn)
-                if member:
+                if member and common.isvalidname(member):
                     members.add(member)
         # actually return the results
         for name in names:
@@ -87,50 +84,50 @@
 class GroupByNameRequest(GroupRequest):
 
     action = constants.NSLCD_ACTION_GROUP_BYNAME
+    filter_attrs = dict(cn='name')
 
     def read_parameters(self):
         self.name = self.fp.read_string()
         common.validate_name(self.name)
 
-    def mk_filter(self):
-        return '(&%s(%s=%s))' % ( self.filter,
-                  self.attmap_group_cn, 
ldap.filter.escape_filter_chars(self.name) )
-
 
 class GroupByGidRequest(GroupRequest):
 
     action = constants.NSLCD_ACTION_GROUP_BYGID
+    filter_attrs = dict(gidNumber='gid')
 
     def read_parameters(self):
         self.gid = self.fp.read_gid_t()
 
-    def mk_filter(self):
-        return '(&%s(%s=%d))' % ( self.filter,
-                  self.attmap_group_gidNumber, self.gid )
-
 
 class GroupByMemberRequest(GroupRequest):
 
     action = constants.NSLCD_ACTION_GROUP_BYMEMBER
     wantmembers = False
-    attributes = ( 'cn', 'userPassword', 'gidNumber' )
+
+    def __init__(self, *args, **kwargs):
+        super(GroupByMemberRequest, self).__init__(*args, **kwargs)
+        # set up our own attributes that leave out membership attributes
+        self.attmap = common.Attributes(attmap)
+        del self.attmap['memberUid']
+        del self.attmap['uniqueMember']
 
     def read_parameters(self):
         self.memberuid = self.fp.read_string()
         common.validate_name(self.memberuid)
 
+    def attributes(self):
+        return self.attmap.attributes()
+
     def mk_filter(self):
-        # try to translate uid to DN
-        # TODO: only do this if memberuid attribute is mapped
-        import passwd
-        dn = passwd.uid2dn(self.conn, self.memberuid)
-        if dn:
-            return '(&%s(|(%s=%s)(%s=%s)))' % ( self.filter,
-                      self.attmap_group_memberUid, 
ldap.filter.escape_filter_chars(self.memberuid),
-                      self.attmap_group_uniqueMember, 
ldap.filter.escape_filter_chars(dn) )
-        else:
-            return '(&%s(%s=%s))' % ( self.filter,
-                      self.attmap_group_memberUid, 
ldap.filter.escape_filter_chars(self.memberuid) )
+        if attmap['uniqueMember']:
+            dn = uid2dn(self.conn, self.memberuid)
+            if dn:
+                return '(&%s(|(%s=%s)(%s=%s)))' % ( self.filter,
+                          attmap['memberUid'], 
ldap.filter.escape_filter_chars(self.memberuid),
+                          attmap['uniqueMember'], 
ldap.filter.escape_filter_chars(dn) )
+        return '(&%s(%s=%s))' % ( self.filter,
+                  attmap['memberUid'], 
ldap.filter.escape_filter_chars(self.memberuid) )
 
 
 class GroupAllRequest(GroupRequest):

Modified: nss-pam-ldapd/pynslcd/host.py
==============================================================================
--- nss-pam-ldapd/pynslcd/host.py       Sun May  1 14:18:06 2011        (r1453)
+++ nss-pam-ldapd/pynslcd/host.py       Sun May  1 21:08:39 2011        (r1454)
@@ -18,33 +18,30 @@
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 # 02110-1301 USA
 
-import ldap.filter
+import logging
 
 import constants
 import common
 
 
-class HostRequest(common.Request):
-
-    filter = '(objectClass=ipHost)'
+attmap = common.Attributes(cn='cn', ipHostNumber='ipHostNumber')
+filter = '(objectClass=ipHost)'
 
-    attmap_cn           = 'cn'
-    attmap_ipHostNumber = 'ipHostNumber'
 
-    attributes = ( 'cn', 'ipHostNumber' )
+class HostRequest(common.Request):
 
     def write(self, dn, attributes):
-        hostname = common.get_rdn_value(dn, self.attmap_cn)
-        hostnames = attributes.get(self.attmap_cn, [])
+        hostname = common.get_rdn_value(dn, attmap['cn'])
+        hostnames = attributes['cn']
         if not hostnames:
-            print 'Error: entry %s does not contain %s value' % ( dn, 
self.attmap_cn )
+            print 'Error: entry %s does not contain %s value' % ( dn, 
attmap['cn'] )
         if not hostname:
             hostname = hostnames.pop(0)
         elif hostname in hostnames:
             hostnames.remove(hostname)
-        addresses = attributes.get(self.attmap_ipHostNumber, [])
+        addresses = attributes['ipHostNumber']
         if not addresses:
-            print 'Error: entry %s does not contain %s value' % ( dn, 
self.attmap_ipHostNumber )
+            print 'Error: entry %s does not contain %s value' % ( dn, 
attmap['ipHostNumber'] )
         # write result
         self.fp.write_int32(constants.NSLCD_RESULT_BEGIN)
         self.fp.write_string(hostname)
@@ -57,27 +54,20 @@
 class HostByNameRequest(HostRequest):
 
     action = constants.NSLCD_ACTION_HOST_BYNAME
+    filter_attrs = dict(cn='name')
 
     def read_parameters(self):
         self.name = self.fp.read_string()
 
-    def mk_filter(self):
-        return '(&%s(%s=%s))' % ( self.filter,
-                  self.attmap_cn, ldap.filter.escape_filter_chars(self.name) )
-
 
 class HostByAddressRequest(HostRequest):
 
     action = constants.NSLCD_ACTION_HOST_BYADDR
+    filter_attrs = dict(ipHostNumber='address')
 
     def read_parameters(self):
         self.address = self.fp.read_address()
 
-    def mk_filter(self):
-        return '(&%s(%s=%s))' % ( self.filter,
-                  self.attmap_ipHostNumber,
-                  ldap.filter.escape_filter_chars(self.address) )
-
 
 class HostAllRequest(HostRequest):
 

Modified: nss-pam-ldapd/pynslcd/netgroup.py
==============================================================================
--- nss-pam-ldapd/pynslcd/netgroup.py   Sun May  1 14:18:06 2011        (r1453)
+++ nss-pam-ldapd/pynslcd/netgroup.py   Sun May  1 21:08:39 2011        (r1454)
@@ -18,7 +18,7 @@
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 # 02110-1301 USA
 
-import ldap.filter
+import logging
 import re
 
 import constants
@@ -28,30 +28,28 @@
 _netgroup_triple_re = 
re.compile(r'^\s*\(\s*(?P<host>.*)\s*,\s*(?P<user>.*)\s*,\s*(?P<domain>.*)\s*\)\s*$')
 
 
-class NetgroupRequest(common.Request):
-
-    filter = '(objectClass=nisNetgroup)'
+attmap = common.Attributes(cn='cn',
+                           nisNetgroupTriple='nisNetgroupTriple',
+                           memberNisNetgroup='memberNisNetgroup')
+filter = '(objectClass=nisNetgroup)'
 
-    attmap_cn                = 'cn'
-    attmap_nisNetgroupTriple = 'nisNetgroupTriple'
-    attmap_memberNisNetgroup = 'memberNisNetgroup'
 
-    attributes = ( 'cn', 'nisNetgroupTriple', 'memberNisNetgroup' )
+class NetgroupRequest(common.Request):
 
     def write(self, dn, attributes):
         # get names and check against requested user name
-        names = attributes.get(self.attmap_cn, [])
+        names = attributes['cn']
         if self.name:
             if self.name not in names:
                 return
             names = ( self.name, )
         if not names:
-            print 'Error: entry %s does not contain %s value' % ( dn, 
self.attmap_cn)
+            print 'Error: entry %s does not contain %s value' % (dn, 
attmap['cn'])
         # write the netgroup triples
-        for triple in attributes.get(self.attmap_nisNetgroupTriple, []):
+        for triple in attributes['nisNetgroupTriple']:
             m = _netgroup_triple_re.match(triple)
             if not m:
-                print 'Warning: entry %s contains invalid %s value: %r' % ( 
dn, self.attmap_nisNetgroupTriple, triple)
+                print 'Warning: entry %s contains invalid %s value: %r' % ( 
dn, attmap['nisNetgroupTriple'], triple)
             else:
                 self.fp.write_int32(constants.NSLCD_RESULT_BEGIN)
                 self.fp.write_int32(constants.NSLCD_NETGROUP_TYPE_TRIPLE)
@@ -59,7 +57,7 @@
                 self.fp.write_string(m.group('user'))
                 self.fp.write_string(m.group('domain'))
         # write netgroup members
-        for member in attributes.get(self.attmap_memberNisNetgroup, []):
+        for member in attributes['memberNisNetgroup']:
             self.fp.write_int32(constants.NSLCD_RESULT_BEGIN)
             self.fp.write_int32(constants.NSLCD_NETGROUP_TYPE_NETGROUP)
             self.fp.write_string(member)
@@ -68,10 +66,7 @@
 class NetgroupByNameRequest(NetgroupRequest):
 
     action = constants.NSLCD_ACTION_NETGROUP_BYNAME
+    filter_attrs = dict(cn='name')
 
     def read_parameters(self):
         self.name = self.fp.read_string()
-
-    def mk_filter(self):
-        return '(&%s(%s=%s))' % ( self.filter,
-                  self.attmap_cn, ldap.filter.escape_filter_chars(self.name) )

Modified: nss-pam-ldapd/pynslcd/network.py
==============================================================================
--- nss-pam-ldapd/pynslcd/network.py    Sun May  1 14:18:06 2011        (r1453)
+++ nss-pam-ldapd/pynslcd/network.py    Sun May  1 21:08:39 2011        (r1454)
@@ -18,33 +18,31 @@
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 # 02110-1301 USA
 
-import ldap.filter
+import logging
 
 import constants
 import common
 
 
-class NetworkRequest(common.Request):
-
-    filter = '(objectClass=ipNetwork)'
+attmap = common.Attributes(cn='cn',
+                           ipNetworkNumber='ipNetworkNumber')
+filter = '(objectClass=ipNetwork)'
 
-    attmap_cn           = 'cn'
-    attmap_ipNetworkNumber = 'ipNetworkNumber'
 
-    attributes = ( 'cn', 'ipNetworkNumber' )
+class NetworkRequest(common.Request):
 
     def write(self, dn, attributes):
-        networkname = common.get_rdn_value(dn, self.attmap_cn)
-        networknames = attributes.get(self.attmap_cn, [])
+        networkname = common.get_rdn_value(dn, attmap['cn'])
+        networknames = attributes['cn']
         if not networknames:
-            print 'Error: entry %s does not contain %s value' % ( dn, 
self.attmap_cn)
+            print 'Error: entry %s does not contain %s value' % ( dn, 
attmap['cn'])
         if not networkname:
             networkname = networknames.pop(0)
         elif networkname in networknames:
             networknames.remove(networkname)
-        addresses = attributes.get(self.attmap_ipNetworkNumber, [])
+        addresses = attributes['ipNetworkNumber']
         if not addresses:
-            print 'Error: entry %s does not contain %s value' % ( dn, 
self.attmap_ipNetworkNumber)
+            print 'Error: entry %s does not contain %s value' % ( dn, 
attmap['ipNetworkNumber'])
         # write result
         self.fp.write_int32(constants.NSLCD_RESULT_BEGIN)
         self.fp.write_string(networkname)
@@ -57,27 +55,20 @@
 class NetworkByNameRequest(NetworkRequest):
 
     action = constants.NSLCD_ACTION_NETWORK_BYNAME
+    filter_attrs = dict(cn='name')
 
     def read_parameters(self):
         self.name = self.fp.read_string()
 
-    def mk_filter(self):
-        return '(&%s(%s=%s))' % ( self.filter,
-                  self.attmap_cn, ldap.filter.escape_filter_chars(self.name) )
-
 
 class NetworkByAddressRequest(NetworkRequest):
 
     action = constants.NSLCD_ACTION_NETWORK_BYADDR
+    filter_attrs = dict(ipNetworkNumber='address')
 
     def read_parameters(self):
         self.address = self.fp.read_address()
 
-    def mk_filter(self):
-        return '(&%s(%s=%s))' % ( self.filter,
-                  self.attmap_ipNetworkNumber,
-                  ldap.filter.escape_filter_chars(self.address) )
-
 
 class NetworkAllRequest(NetworkRequest):
 

Modified: nss-pam-ldapd/pynslcd/pam.py
==============================================================================
--- nss-pam-ldapd/pynslcd/pam.py        Sun May  1 14:18:06 2011        (r1453)
+++ nss-pam-ldapd/pynslcd/pam.py        Sun May  1 21:08:39 2011        (r1454)
@@ -55,16 +55,16 @@
             # save the DN
             self.userdn = entry[0]
             # get the "real" username
-            value = common.get_rdn_value(entry[0], 
passwd.PasswdRequest.attmap_passwd_uid)
+            value = common.get_rdn_value(entry[0], passwd.attmap['uid'])
             if not value:
                 # get the username from the uid attribute
-                values = myldap_get_values(entry, 
passwd.PasswdRequest.attmap_passwd_uid)
+                values = myldap_get_values(entry, passwd.attmap['uid'])
                 if not values or not values[0]:
-                    logging.warn('%s: is missing a %s attribute', entry.dn, 
passwd.PasswdRequest.attmap_passwd_uid)
+                    logging.warn('%s: is missing a %s attribute', dn, 
passwd.attmap['uid'])
                 value = values[0]
             # check the username
             if value and not common.isvalidname(value):
-                raise ValueError('%s: has invalid %s attribute', entry.dn, 
passwd.PasswdRequest.attmap_passwd_uid)
+                raise ValueError('%s: has invalid %s attribute', dn, 
passwd.attmap['uid'])
             # check if the username is different and update it if needed
             if value != self.username:
                 logging.info('username changed from %r to %r', self.username, 
value)

Modified: nss-pam-ldapd/pynslcd/passwd.py
==============================================================================
--- nss-pam-ldapd/pynslcd/passwd.py     Sun May  1 14:18:06 2011        (r1453)
+++ nss-pam-ldapd/pynslcd/passwd.py     Sun May  1 21:08:39 2011        (r1454)
@@ -25,60 +25,46 @@
 import common
 
 
-class PasswdRequest(common.Request):
+attmap = common.Attributes(uid='uid',
+                           userPassword='"*"',
+                           uidNumber='uidNumber',
+                           gidNumber='gidNumber',
+                           gecos='"${gecos:-$cn}"',
+                           homeDirectory='homeDirectory',
+                           loginShell='loginShell',
+                           objectClass='objectClass')
+filter = '(objectClass=posixAccount)'
+bases = ( 'ou=people,dc=test,dc=tld', )
 
-    attmap = { 'uid': 'uid', 'userPassword': 'userPassword',
-               'uidNumber': 'uidNumber', 'gidNumber': 'gidNumber',
-               'gecos': '"${gecos:-$cn}"', 'cn': 'cn',
-               'homeDirectory': 'homeDirectory',
-               'loginShell': 'loginShell',
-               'objectClass': 'objectClass' }
-    filter = '(objectClass=posixAccount)'
-
-    attmap_passwd_uid           = 'uid'
-    attmap_passwd_userPassword  = 'userPassword'
-    attmap_passwd_uidNumber     = 'uidNumber'
-    attmap_passwd_gidNumber     = 'gidNumber'
-    attmap_passwd_gecos         = '"${gecos:-$cn}"'
-    attmap_passwd_homeDirectory = 'homeDirectory'
-    attmap_passwd_loginShell    = 'loginShell'
-
-    # these should be removed
-    attmap_passwd_cn = 'cn'
-
-    attributes = ( 'uid', 'userPassword', 'uidNumber', 'gidNumber',
-                   'gecos', 'cn', 'homeDirectory', 'loginShell',
-                   'objectClass' )
 
-    bases = ( 'ou=people,dc=test,dc=tld', )
+class PasswdRequest(common.Request):
 
     def write(self, dn, attributes):
         # get uid attribute and check against requested user name
-        names = attributes.get('uid', [])
+        names = attributes['uid']
         if self.name:
             if self.name not in names:
                 return
             names = ( self.name, )
         # get user password entry
-        if 'shadowAccount' in attributes.get('objectClass', []):
+        if 'shadowAccount' in attributes['objectClass']:
             passwd = 'x'
         else:
-            passwd = '*';
+            passwd = attributes['userPassword'][0]
         # get numeric user and group ids
-        uids = ( self.uid, ) if self.uid else 
attributes.get(self.attmap_passwd_uidNumber, [])
+        uids = ( self.uid, ) if self.uid else attributes['uidNumber']
         uids = [ int(x) for x in uids ]
-        ( gid, ) = attributes[self.attmap_passwd_gidNumber]
-        gid = int(gid)
-        # FIXME: use expression here
-        gecos = attributes.get(self.attmap_passwd_gecos, [None])[0] or 
attributes.get(self.attmap_passwd_cn, [''])[0]
-        ( home, ) = attributes.get(self.attmap_passwd_homeDirectory, [''])
-        ( shell, ) = attributes.get(self.attmap_passwd_loginShell, [''])
+        # get other passwd properties
+        gid = int(attributes['gidNumber'][0])
+        gecos = attributes['gecos'][0]
+        home = attributes['homeDirectory'][0]
+        shell = attributes['loginShell'][0]
+        # write results
         for name in names:
             if not common.isvalidname(name):
                 print 'Warning: passwd entry %s contains invalid user name: 
"%s"' % ( dn, name )
             else:
                 for uid in uids:
-                    #print '%s:%s:%d:%d:%s:%s:%s' % ( name, passwd, uid, gid, 
gecos, home, shell )
                     self.fp.write_int32(constants.NSLCD_RESULT_BEGIN)
                     self.fp.write_string(name)
                     self.fp.write_string(passwd)
@@ -92,41 +78,37 @@
 class PasswdByNameRequest(PasswdRequest):
 
     action = constants.NSLCD_ACTION_PASSWD_BYNAME
+    filter_attrs = dict(uid='name')
 
     def read_parameters(self):
         self.name = self.fp.read_string()
         common.validate_name(self.name)
 
-    def mk_filter(self):
-        return '(&%s(%s=%s))' % ( self.filter,
-                  self.attmap_passwd_uid, 
ldap.filter.escape_filter_chars(self.name) )
-
 
 class PasswdByUidRequest(PasswdRequest):
 
     action = constants.NSLCD_ACTION_PASSWD_BYUID
+    filter_attrs = dict(uidNumber='uid')
 
     def read_parameters(self):
         self.uid = self.fp.read_uid_t()
 
-    def mk_filter(self):
-        return '(&%s(%s=%d))' % ( self.filter,
-                  self.attmap_passwd_uidNumber, self.uid )
-
 
 class PasswdAllRequest(PasswdRequest):
 
     action = constants.NSLCD_ACTION_PASSWD_ALL
 
 
-def do_search(conn, filter=None, base=None):
-    mybases = ( base, ) if base else PasswdRequest.bases
-    filter = filter or PasswdRequest.filter
+def do_search(conn, flt=None, base=None):
+    mybases = ( base, ) if base else bases
+    flt = flt or filter
+    import cfg
     # perform a search for each search base
     for base in mybases:
         # do the LDAP search
         try:
-            res = conn.search_s(base, PasswdRequest.scope, filter, 
[PasswdRequest.attmap_passwd_uid])
+            scope = locals().get('scope', cfg.scope)
+            res = conn.search_s(base, scope, flt, [attmap['uid']])
             for entry in res:
                 if entry[0]:
                     yield entry
@@ -137,10 +119,10 @@
 def uid2entry(conn, uid):
     """Look up the user by uid and return the LDAP entry or None if the user
     was not found."""
-    myfilter = '(&%s(%s=%s))' % ( PasswdRequest.filter,
-                  PasswdRequest.attmap_passwd_uid, 
ldap.filter.escape_filter_chars(uid) )
+    myfilter = '(&%s(%s=%s))' % ( filter,
+                  attmap['uid'], ldap.filter.escape_filter_chars(uid) )
     for dn, attributes in do_search(conn, myfilter):
-        if uid in attributes[PasswdRequest.attmap_passwd_uid]:
+        if uid in attributes[attmap['uid']]:
             return dn, attributes
 
 def uid2dn(conn, uid):
@@ -150,11 +132,13 @@
     if x is not None:
         return x[0]
 
+# FIXME: use cache of dn2uid and try to use DN to get uid attribute
+
 def dn2uid(conn, dn):
     """Look up the user by dn and return a uid or None if the user was
     not found."""
     try:
         for dn, attributes in do_search(conn, base=dn):
-            return attributes[PasswdRequest.attmap_passwd_uid][0]
+            return attributes[attmap['uid']][0]
     except ldap.NO_SUCH_OBJECT:
         return None

Modified: nss-pam-ldapd/pynslcd/protocol.py
==============================================================================
--- nss-pam-ldapd/pynslcd/protocol.py   Sun May  1 14:18:06 2011        (r1453)
+++ nss-pam-ldapd/pynslcd/protocol.py   Sun May  1 21:08:39 2011        (r1454)
@@ -18,27 +18,24 @@
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 # 02110-1301 USA
 
-import ldap.filter
+import logging
 
 import constants
 import common
 
 
-class ProtocolRequest(common.Request):
-
-    filter = '(objectClass=ipProtocol)'
+attmap = common.Attributes(cn='cn', ipProtocolNumber='ipProtocolNumber')
+filter = '(objectClass=ipProtocol)'
 
-    attmap_cn               = 'cn'
-    attmap_ipProtocolNumber = 'ipProtocolNumber'
 
-    attributes = ( 'cn', 'ipProtocolNumber' )
+class ProtocolRequest(common.Request):
 
     def write(self, dn, attributes):
         # get name
-        name = common.get_rdn_value(dn, self.attmap_cn)
-        names = attributes.get(self.attmap_cn, [])
+        name = common.get_rdn_value(dn, attmap['cn'])
+        names = attributes['cn']
         if not names:
-            print 'Error: entry %s does not contain %s value' % ( dn, 
self.attmap_cn )
+            print 'Error: entry %s does not contain %s value' % (dn, 
attmap['cn'])
         if self.name and self.name not in names:
             return # case of result entry did not match
         if not name:
@@ -46,9 +43,9 @@
         elif name in names:
             names.remove(name)
         # get number
-        ( number, ) = attributes.get(self.attmap_ipProtocolNumber, [])
+        ( number, ) = attributes['ipProtocolNumber']
         if not number:
-            print 'Error: entry %s does not contain %s value' % ( dn, 
self.attmap_ipProtocolNumber)
+            print 'Error: entry %s does not contain %s value' % (dn, 
attmap['ipProtocolNumber'])
         number = int(number)
         # write result
         self.fp.write_int32(constants.NSLCD_RESULT_BEGIN)
@@ -60,26 +57,20 @@
 class ProtocolByNameRequest(ProtocolRequest):
 
     action = constants.NSLCD_ACTION_PROTOCOL_BYNAME
+    filter_attrs = dict(cn='name')
 
     def read_parameters(self):
         self.name = self.fp.read_string()
 
-    def mk_filter(self):
-        return '(&%s(%s=%s))' % ( self.filter,
-                  self.attmap_cn, ldap.filter.escape_filter_chars(self.name) )
-
 
 class ProtocolByNumberRequest(ProtocolRequest):
 
     action = constants.NSLCD_ACTION_PROTOCOL_BYNUMBER
+    filter_attrs = dict(ipProtocolNumber='number')
 
     def read_parameters(self):
         self.number = self.fp.read_int32()
 
-    def mk_filter(self):
-        return '(&%s(%s=%d))' % ( self.filter,
-                  self.attmap_ipProtocolNumber, self.number )
-
 
 class ProtocolAllRequest(ProtocolRequest):
 

Modified: nss-pam-ldapd/pynslcd/rpc.py
==============================================================================
--- nss-pam-ldapd/pynslcd/rpc.py        Sun May  1 14:18:06 2011        (r1453)
+++ nss-pam-ldapd/pynslcd/rpc.py        Sun May  1 21:08:39 2011        (r1454)
@@ -18,27 +18,24 @@
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 # 02110-1301 USA
 
-import ldap.filter
+import logging
 
 import constants
 import common
 
 
-class RpcRequest(common.Request):
-
-    filter = '(objectClass=oncRpc)'
+attmap = common.Attributes(cn='cn', oncRpcNumber='oncRpcNumber')
+filter = '(objectClass=oncRpc)'
 
-    attmap_cn           = 'cn'
-    attmap_oncRpcNumber = 'oncRpcNumber'
 
-    attributes = ( 'cn', 'oncRpcNumber' )
+class RpcRequest(common.Request):
 
     def write(self, dn, attributes):
         # get name
-        name = common.get_rdn_value(dn, self.attmap_cn)
-        names = attributes.get(self.attmap_cn, [])
+        name = common.get_rdn_value(dn, attmap['cn'])
+        names = attributes['cn']
         if not names:
-            print 'Error: entry %s does not contain %s value' % ( dn, 
self.attmap_cn )
+            print 'Error: entry %s does not contain %s value' % ( dn, 
attmap['cn'] )
         if self.name and self.name not in names:
             return # case of result entry did not match
         if not name:
@@ -46,9 +43,9 @@
         elif name in names:
             names.remove(name)
         # get number
-        ( number, ) = attributes.get(self.attmap_oncRpcNumber, [])
+        ( number, ) = attributes['oncRpcNumber']
         if not number:
-            print 'Error: entry %s does not contain %s value' % ( dn, 
self.attmap_oncRpcNumber)
+            print 'Error: entry %s does not contain %s value' % ( dn, 
attmap['oncRpcNumber'])
         number = int(number)
         # write result
         self.fp.write_int32(constants.NSLCD_RESULT_BEGIN)
@@ -60,26 +57,20 @@
 class RpcByNameRequest(RpcRequest):
 
     action = constants.NSLCD_ACTION_RPC_BYNAME
+    filter_attrs = dict(cn='name')
 
     def read_parameters(self):
         self.name = self.fp.read_string()
 
-    def mk_filter(self):
-        return '(&%s(%s=%s))' % ( self.filter,
-                  self.attmap_cn, ldap.filter.escape_filter_chars(self.name) )
-
 
 class RpcByNumberRequest(RpcRequest):
 
     action = constants.NSLCD_ACTION_RPC_BYNUMBER
+    filter_attrs = dict(oncRpcNumber='number')
 
     def read_parameters(self):
         self.number = self.fp.read_int32()
 
-    def mk_filter(self):
-        return '(&%s(%s=%d))' % ( self.filter,
-                  self.attmap_oncRpcNumber, self.number )
-
 
 class RpcAllRequest(RpcRequest):
 

Modified: nss-pam-ldapd/pynslcd/service.py
==============================================================================
--- nss-pam-ldapd/pynslcd/service.py    Sun May  1 14:18:06 2011        (r1453)
+++ nss-pam-ldapd/pynslcd/service.py    Sun May  1 21:08:39 2011        (r1454)
@@ -18,21 +18,20 @@
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 # 02110-1301 USA
 
+import logging
 import ldap.filter
 
 import constants
 import common
 
 
-class ServiceRequest(common.Request):
-
-    filter = '(objectClass=ipService)'
+attmap = common.Attributes(cn='cn',
+                           ipServicePort='ipServicePort',
+                           ipServiceProtocol='ipServiceProtocol')
+filter = '(objectClass=ipService)'
 
-    attmap_cn                = 'cn'
-    attmap_ipServicePort     = 'ipServicePort'
-    attmap_ipServiceProtocol = 'ipServiceProtocol'
 
-    attributes = ( 'cn', 'ipServicePort', 'ipServiceProtocol' )
+class ServiceRequest(common.Request):
 
     def __init__(self, *args):
         super(ServiceRequest, self).__init__(*args)
@@ -40,10 +39,10 @@
 
     def write(self, dn, attributes):
         # get name
-        name = common.get_rdn_value(dn, self.attmap_cn)
-        names = attributes.get(self.attmap_cn, [])
+        name = common.get_rdn_value(dn, attmap['cn'])
+        names = attributes['cn']
         if not names:
-            print 'Error: entry %s does not contain %s value' % ( dn, 
self.attmap_cn )
+            print 'Error: entry %s does not contain %s value' % (dn, 
attmap['cn'])
         if self.name and self.name not in names + [ name, ]:
             return # case of result entry did not match
         if not name:
@@ -51,12 +50,12 @@
         elif name in names:
             names.remove(name)
         # get port number
-        ( port, ) = attributes.get(self.attmap_ipServicePort, [])
+        ( port, ) = attributes['ipServicePort']
         if not port:
-            print 'Error: entry %s does not contain %s value' % ( dn, 
self.attmap_ipServicePort)
+            print 'Error: entry %s does not contain %s value' % (dn, 
attmap['ipServicePort'])
         port = int(port)
         # get protocol
-        protocols = attributes.get(self.attmap_ipServiceProtocol, [])
+        protocols = attributes['ipServiceProtocol']
         if self.protocol:
             if self.protocol not in protocols:
                 return
@@ -80,12 +79,12 @@
 
     def mk_filter(self):
         if self.protocol:
-          return '(&%s(%s=%s)(%s=%s))' % ( self.filter,
-                    self.attmap_cn, ldap.filter.escape_filter_chars(self.name),
-                    self.attmap_ipServiceProtocol, 
ldap.filter.escape_filter_chars(self.protocol) )
+            return '(&%s(%s=%s)(%s=%s))' % ( self.filter,
+                      attmap['cn'], ldap.filter.escape_filter_chars(self.name),
+                      attmap['ipServiceProtocol'], 
ldap.filter.escape_filter_chars(self.protocol) )
         else:
-          return '(&%s(%s=%s))' % ( self.filter,
-                    self.attmap_cn, ldap.filter.escape_filter_chars(self.name) 
)
+            return '(&%s(%s=%s))' % ( self.filter,
+                      attmap['cn'], ldap.filter.escape_filter_chars(self.name) 
)
 
 
 class ServiceByNumberRequest(ServiceRequest):
@@ -98,12 +97,12 @@
 
     def mk_filter(self):
         if self.protocol:
-          return '(&%s(%s=%d)(%s=%s))' % ( self.filter,
-                    self.attmap_ipServicePort, self.number,
-                    self.attmap_ipServiceProtocol, 
ldap.filter.escape_filter_chars(self.protocol) )
+            return '(&%s(%s=%d)(%s=%s))' % ( self.filter,
+                      attmap['ipServicePort'], self.number,
+                      attmap['ipServiceProtocol'], 
ldap.filter.escape_filter_chars(self.protocol) )
         else:
-          return '(&%s(%s=%d))' % ( self.filter,
-                    self.attmap_ipServicePort, self.number )
+            return '(&%s(%s=%d))' % ( self.filter,
+                      attmap['ipServicePort'], self.number )
 
 
 class ServiceAllRequest(ServiceRequest):

Modified: nss-pam-ldapd/pynslcd/shadow.py
==============================================================================
--- nss-pam-ldapd/pynslcd/shadow.py     Sun May  1 14:18:06 2011        (r1453)
+++ nss-pam-ldapd/pynslcd/shadow.py     Sun May  1 21:08:39 2011        (r1454)
@@ -18,46 +18,41 @@
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 # 02110-1301 USA
 
-import ldap.filter
+import logging
 
 import constants
 import common
 
 
-class ShadowRequest(common.Request):
-
-    filter = '(objectClass=shadowAccount)'
+attmap = common.Attributes(uid='uid',
+                           userPassword='"*"',
+                           shadowLastChange='"${shadowLastChange:--1}"',
+                           shadowMin='"${shadowMin:--1}"',
+                           shadowMax='"${shadowMax:--1}"',
+                           shadowWarning='"${shadowWarning:--1}"',
+                           shadowInactive='"${shadowInactive:--1}"',
+                           shadowExpire='"${shadowExpire:--1}"',
+                           shadowFlag='"${shadowFlag:-0}"')
+filter = '(objectClass=shadowAccount)'
+bases = ( 'ou=people,dc=test,dc=tld', )
 
-    attmap_uid              = 'uid'
-    attmap_userPassword     = 'userPassword'
-    attmap_shadowLastChange = 'shadowLastChange'
-    attmap_shadowMin        = 'shadowMin'
-    attmap_shadowMax        = 'shadowMax'
-    attmap_shadowWarning    = 'shadowWarning'
-    attmap_shadowInactive   = 'shadowInactive'
-    attmap_shadowExpire     = 'shadowExpire'
-    attmap_shadowFlag       = 'shadowFlag'
-
-    attributes = ( 'uid', 'userPassword', 'shadowLastChange', 'shadowMin',
-                   'shadowMax', 'shadowWarning', 'shadowInactive',
-                   'shadowExpire', 'shadowFlag' )
 
-    bases = ( 'ou=people,dc=test,dc=tld', )
+class ShadowRequest(common.Request):
 
     def write(self, dn, attributes):
         # get name and check against requested name
-        names = attributes.get(self.attmap_uid, [])
+        names = attributes['uid']
         if not names:
-            print 'Error: entry %s does not contain %s value' % ( dn, 
self.attmap_uid)
+            print 'Error: entry %s does not contain %s value' % ( dn, 
attmap['uid'] )
             return
         if self.name:
             if self.name not in names:
                 return
             names = ( self.name, )
         # get password
-        (passwd, ) = attributes.get(self.attmap_userPassword, ['x'])
+        (passwd, ) = attributes['userPassword']
         if not passwd or self.calleruid != 0:
-            passwd = '*';
+            passwd = '*'
         # function for making an int
         def mk_int(attr):
             try:
@@ -65,22 +60,22 @@
             except TypeError:
                 return None
         # get lastchange date
-        lastchangedate = int(attributes.get(self.attmap_shadowLastChange, 
[-1])[0])
+        lastchangedate = int(attributes.get('shadowLastChange', [0])[0])
         # we expect an AD 64-bit datetime value;
         # we should do date=date/864000000000-134774
         # but that causes problems on 32-bit platforms,
         # first we devide by 1000000000 by stripping the
         # last 9 digits from the string and going from there */
-        if self.attmap_shadowLastChange == 'pwdLastSet':
+        if attmap['shadowLastChange'] == 'pwdLastSet':
             lastchangedate = ( lastchangedate / 864000000000 ) - 134774
         # get longs
-        mindays = int(attributes.get(self.attmap_shadowMin, [-1])[0])
-        maxdays = int(attributes.get(self.attmap_shadowMax, [-1])[0])
-        warndays = int(attributes.get(self.attmap_shadowWarning, [-1])[0])
-        inactdays = int(attributes.get(self.attmap_shadowInactive, [-1])[0])
-        expiredate = int(attributes.get(self.attmap_shadowExpire, [-1])[0])
-        flag = int(attributes.get(self.attmap_shadowFlag, [0])[0])
-        if self.attmap_shadowFlag == 'pwdLastSet':
+        mindays = int(attributes.get('shadowMin', [-1])[0])
+        maxdays = int(attributes.get('shadowMax', [-1])[0])
+        warndays = int(attributes.get('shadowWarning', [-1])[0])
+        inactdays = int(attributes.get('shadowInactive', [-1])[0])
+        expiredate = int(attributes.get('shadowExpire', [-1])[0])
+        flag = int(attributes.get('shadowFlag', [0])[0])
+        if attmap['shadowFlag'] == 'pwdLastSet':
             if flag & 0x10000:
                 maxdays = -1
             flag = 0
@@ -101,14 +96,11 @@
 class ShadowByNameRequest(ShadowRequest):
 
     action = constants.NSLCD_ACTION_SHADOW_BYNAME
+    filter_attrs = dict(uid='name')
 
     def read_parameters(self):
         self.name = self.fp.read_string()
 
-    def mk_filter(self):
-        return '(&%s(%s=%s))' % ( self.filter,
-                  self.attmap_uid, ldap.filter.escape_filter_chars(self.name) )
-
 
 class ShadowAllRequest(ShadowRequest):
 

Modified: nss-pam-ldapd/pynslcd/tio.py
==============================================================================
--- nss-pam-ldapd/pynslcd/tio.py        Sun May  1 14:18:06 2011        (r1453)
+++ nss-pam-ldapd/pynslcd/tio.py        Sun May  1 21:08:39 2011        (r1454)
@@ -21,7 +21,7 @@
 import struct
 import os
 import socket
-import errno
+
 
 # definition for reading and writing INT32 values
 _int32 = struct.Struct('i')
-- 
To unsubscribe send an email to
nss-pam-ldapd-commits-unsubscribe@lists.arthurdejong.org or see
http://lists.arthurdejong.org/nss-pam-ldapd-commits