lists.arthurdejong.org
RSS feed

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

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

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



Author: arthur
Date: Fri May  4 22:13:57 2012
New Revision: 1682
URL: http://arthurdejong.org/viewvc/nss-pam-ldapd?revision=1682&view=revision

Log:
move expression handling to own module

Added:
   nss-pam-ldapd/pynslcd/expr.py
      - copied, changed from r1679, nss-pam-ldapd/pynslcd/attmap.py
Modified:
   nss-pam-ldapd/pynslcd/Makefile.am
   nss-pam-ldapd/pynslcd/attmap.py
   nss-pam-ldapd/pynslcd/cfg.py

Modified: nss-pam-ldapd/pynslcd/Makefile.am
==============================================================================
--- nss-pam-ldapd/pynslcd/Makefile.am   Fri May  4 22:10:48 2012        (r1681)
+++ nss-pam-ldapd/pynslcd/Makefile.am   Fri May  4 22:13:57 2012        (r1682)
@@ -1,6 +1,6 @@
 # Makefile.am - use automake to generate Makefile.in
 #
-# Copyright (C) 2010, 2011 Arthur de Jong
+# Copyright (C) 2010, 2011, 2012 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
@@ -20,6 +20,7 @@
 pynslcddir = $(datadir)/pynslcd
 
 pynslcd_PYTHON = pynslcd.py cfg.py common.py tio.py mypidfile.py attmap.py \
+                 expr.py \
                  alias.py ether.py group.py host.py netgroup.py network.py \
                  pam.py passwd.py protocol.py rpc.py service.py shadow.py
 nodist_pynslcd_PYTHON = constants.py config.py

Modified: nss-pam-ldapd/pynslcd/attmap.py
==============================================================================
--- nss-pam-ldapd/pynslcd/attmap.py     Fri May  4 22:10:48 2012        (r1681)
+++ nss-pam-ldapd/pynslcd/attmap.py     Fri May  4 22:13:57 2012        (r1682)
@@ -41,154 +41,20 @@
 import re
 from ldap.filter import escape_filter_chars as escape
 
+from expr import Expression
+
 
 # exported names
 __all__ = ('Attributes', )
 
 
-# FIXME: support multiple attribute values
 # TODO: support objectSid attributes
-# TODO: do more expression validity checking
 
 
 # regular expression to match function attributes
 attribute_func_re = re.compile('^(?P<function>[a-z]+)\((?P<attribute>.*)\)$')
 
 
-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
-
-    def get_name(self):
-        """Read a variable name from the value iterator."""
-        name = ''
-        for c in self:
-            if not c.isalnum():
-                self.back()
-                return name
-            name += c
-        return name
-
-
-class DollarExpression(object):
-    """Class for handling a variable $xxx ${xxx}, ${xxx:-yyy} or ${xxx:+yyy}
-    expression."""
-
-    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 = value.get_name()
-            c = value.next()
-            if c == '}':
-                return
-            self.op = c + value.next()
-            self.expr = Expression(value, endat='}')
-        elif c == '(':
-            self.name = None
-            self.op = value.get_name()
-            c = value.next()
-            if c != '(':
-                raise ValueError("Expecting '('")
-            self.expr = Expression(value, endat=')')
-            c = value.next()
-            if c != ')':
-                raise ValueError("Expecting ')'")
-        else:
-            value.back()
-            self.name = value.get_name()
-
-    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 ''
-        elif self.op == 'lower':
-            return self.expr.value(variables).lower()
-        elif self.op == 'upper':
-            return self.expr.value(variables).upper()
-        return value
-
-    def variables(self, results):
-        """Add the variables used in the expression to results."""
-        if self.name:
-            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):
-            self.expression = value
-            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
-
-    def __str__(self):
-        return self.expression
-
-    def __repr__(self):
-        return repr(str(self))
-
-
 class SimpleMapping(str):
     """Simple mapping to another attribute name."""
 
@@ -209,6 +75,7 @@
     def __init__(self, value):
         """Parse the expression as a string."""
         self.expression = Expression(value)
+        super(ExpressionMapping, self).__init__(value)
 
     def values(self, variables):
         """Expand the expression using the variables specified."""
@@ -227,6 +94,7 @@
         m = attribute_func_re.match(mapping)
         self.attribute = m.group('attribute')
         self.function = getattr(self, m.group('function'))
+        super(FunctionMapping, self).__init__(mapping)
 
     def upper(self, value):
         return value.upper()
@@ -253,8 +121,8 @@
 
     def __setitem__(self, attribute, mapping):
         # translate the mapping into a mapping object
-        if mapping[0] == '"':
-            mapping = ExpressionMapping(mapping)
+        if mapping[0] == '"' and mapping[-1] == '"':
+            mapping = ExpressionMapping(mapping[1:-1])
         elif '(' in mapping:
             mapping = FunctionMapping(mapping)
         else:

Modified: nss-pam-ldapd/pynslcd/cfg.py
==============================================================================
--- nss-pam-ldapd/pynslcd/cfg.py        Fri May  4 22:10:48 2012        (r1681)
+++ nss-pam-ldapd/pynslcd/cfg.py        Fri May  4 22:13:57 2012        (r1682)
@@ -24,6 +24,8 @@
 
 import ldap
 
+from expr import Expression
+
 
 # the number of threads to start
 threads = 5
@@ -251,7 +253,6 @@
         # pam_authz_search <FILTER>
         m = re.match('pam_authz_search\s+(?P<value>\S.*)', line, re.IGNORECASE)
         if m:
-            from attmap import Expression
             pam_authz_search.append(Expression(m.group('value')))
             # TODO: check pam_authz_search expression to only contain
             # username, service, ruser, rhost, tty, hostname, fqdn, dn or

Copied and modified: nss-pam-ldapd/pynslcd/expr.py (from r1679, 
nss-pam-ldapd/pynslcd/attmap.py)
==============================================================================
--- nss-pam-ldapd/pynslcd/attmap.py     Fri May  4 15:36:45 2012        (r1679, 
copy source)
+++ nss-pam-ldapd/pynslcd/expr.py       Fri May  4 22:13:57 2012        (r1682)
@@ -1,5 +1,5 @@
 
-# attmap.py - attribute mapping class
+# expr.py - expression handling functions
 #
 # Copyright (C) 2011, 2012 Arthur de Jong
 #
@@ -18,43 +18,25 @@
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 # 02110-1301 USA
 
-"""Module for handling attribute mappings used for LDAP searches.
+"""Module for handling expressions used for LDAP searches.
 
->>> attrs = Attributes(uid='uid',
-...                    userPassword='userPassword',
-...                    uidNumber='uidNumber',
-...                    gidNumber='gidNumber',
-...                    gecos='"${gecos:-$cn}"',
-...                    homeDirectory='homeDirectory',
-...                    loginShell='loginShell')
->>> 'cn' in attrs.attributes()
-True
->>> attrs.translate({'uid': ['UIDVALUE', '2nduidvalue'], 'cn': ['COMMON NAME', 
]})
-{'uid': ['UIDVALUE', '2nduidvalue'], 'loginShell': [], 'userPassword': [], 
'uidNumber': [], 'gidNumber': [], 'gecos': ['COMMON NAME'], 'homeDirectory': []}
->>> attrs['uidNumber']  # a representation fit for logging and filters
-'uidNumber'
->>> attrs['gecos']
-'"${gecos:-$cn}"'
+>>> expr = Expression('foo=$foo')
+>>> expr.value(dict(foo='XX'))
+'foo=XX'
+>>> expr = Expression('foo=${foo:-$bar}')
+>>> expr.value(dict(foo='', bar='YY'))
+'foo=YY'
+>>> expr.value(dict(bar=['YY', 'ZZ']))
+'foo=YY'
 """
 
-import ldap
-import re
-from ldap.filter import escape_filter_chars as escape
-
-
 # exported names
-__all__ = ('Attributes', )
+__all__ = ('Expression', )
 
 
-# FIXME: support multiple attribute values
-# TODO: support objectSid attributes
 # TODO: do more expression validity checking
 
 
-# regular expression to match function attributes
-attribute_func_re = re.compile('^(?P<function>[a-z]+)\((?P<attribute>.*)\)$')
-
-
 class MyIter(object):
     """Custom iterator-like class with a back() method."""
 
@@ -64,7 +46,10 @@
 
     def next(self):
         self.pos += 1
-        return self.value[self.pos - 1]
+        try:
+            return self.value[self.pos - 1]
+        except IndexError:
+            return None
 
     def back(self):
         self.pos -= 1
@@ -76,7 +61,7 @@
         """Read a variable name from the value iterator."""
         name = ''
         for c in self:
-            if not c.isalnum():
+            if not c or not c.isalnum():
                 self.back()
                 return name
             name += c
@@ -115,8 +100,13 @@
 
     def value(self, variables):
         """Expand the expression using the variables specified."""
-        value = variables.get(self.name, [''])[0]
-        # FIXME: expand list
+        # lookup the value
+        value = variables.get(self.name, '')
+        if value in (None, [], ()):
+            value = ''
+        elif isinstance(value, (list, tuple)):
+            value = value[0]
+        # TODO: try to return multiple values, one for each value of the list
         if self.op == ':-':
             return value if value else self.expr.value(variables)
         elif self.op == ':+':
@@ -143,8 +133,6 @@
         if not isinstance(value, MyIter):
             self.expression = value
             value = MyIter(value)
-        if not endat:
-            endat = value.next()  # skip opening quote
         expr = []
         literal = ''
         c = value.next()
@@ -187,112 +175,3 @@
 
     def __repr__(self):
         return repr(str(self))
-
-
-class SimpleMapping(str):
-    """Simple mapping to another attribute name."""
-
-    def attributes(self):
-        return [self]
-
-    def mk_filter(self, value):
-        return '(%s=%s)' % (self, escape(str(value)))
-
-    def values(self, variables):
-        """Expand the expression using the variables specified."""
-        return variables.get(self, [])
-
-
-class ExpressionMapping(str):
-    """Class for parsing and expanding an expression."""
-
-    def __init__(self, value):
-        """Parse the expression as a string."""
-        self.expression = Expression(value)
-
-    def values(self, variables):
-        """Expand the expression using the variables specified."""
-        return [self.expression.value(variables)]
-
-    def attributes(self):
-        """Return the attributes defined in the expression."""
-        return self.expression.variables()
-
-
-class FunctionMapping(str):
-    """Mapping to a function to another attribute."""
-
-    def __init__(self, mapping):
-        self.mapping = mapping
-        m = attribute_func_re.match(mapping)
-        self.attribute = m.group('attribute')
-        self.function = getattr(self, m.group('function'))
-
-    def upper(self, value):
-        return value.upper()
-
-    def lower(self, value):
-        return value.lower()
-
-    def attributes(self):
-        return [self.attribute]
-
-    def mk_filter(self, value):
-        return '(%s=%s)' % (self.attribute, escape(value))
-
-    def values(self, variables):
-        return [self.function(value)
-                for value in variables.get(self.attribute, [])]
-
-
-class Attributes(dict):
-    """Dictionary-like class for handling attribute mapping."""
-
-    def __init__(self, *args, **kwargs):
-        self.update(*args, **kwargs)
-
-    def __setitem__(self, attribute, mapping):
-        # translate the mapping into a mapping object
-        if mapping[0] == '"':
-            mapping = ExpressionMapping(mapping)
-        elif '(' in mapping:
-            mapping = FunctionMapping(mapping)
-        else:
-            mapping = SimpleMapping(mapping)
-        super(Attributes, self).__setitem__(attribute, mapping)
-
-    def update(self, *args, **kwargs):
-        for arg in args:
-            other = dict(arg)
-            for key in other:
-                self[key] = other[key]
-        for key in kwargs:
-            self[key] = kwargs[key]
-
-    def attributes(self):
-        """Return the list of attributes that are referenced in this
-        attribute mapping. These are the attributes that should be
-        requested in the search."""
-        attributes = set()
-        for mapping in self.itervalues():
-            attributes.update(mapping.attributes())
-        return list(attributes)
-
-    def mk_filter(self, attribute, value):
-        """Construct a search filter for searching for the attribute value
-        combination."""
-        mapping = self.get(attribute, SimpleMapping(attribute))
-        return mapping.mk_filter(value)
-
-    def translate(self, variables):
-        """Return a dictionary with every attribute mapped to their value from
-        the specified variables."""
-        results = dict()
-        for attribute, mapping in self.iteritems():
-            results[attribute] = mapping.values(variables)
-        return results
-
-    def get_rdn_value(self, dn, attribute):
-        """Extract the attribute value from from DN if possible. Return None
-        otherwise."""
-        return self.translate(dict((x, [y]) for x, y, z in 
ldap.dn.str2dn(dn)[0]))[attribute][0]
-- 
To unsubscribe send an email to
nss-pam-ldapd-commits-unsubscribe@lists.arthurdejong.org or see
http://lists.arthurdejong.org/nss-pam-ldapd-commits/