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
- From: Commits of the nss-pam-ldapd project <nss-pam-ldapd-commits [at] lists.arthurdejong.org>
- To: nss-pam-ldapd-commits [at] lists.arthurdejong.org
- Reply-to: nss-pam-ldapd-users [at] lists.arthurdejong.org
- Subject: nss-pam-ldapd commit: r1682 - nss-pam-ldapd/pynslcd
- Date: Fri, 4 May 2012 22:13:58 +0200 (CEST)
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/
- nss-pam-ldapd commit: r1682 - nss-pam-ldapd/pynslcd,
Commits of the nss-pam-ldapd project