|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
# attmap.py - attribute mapping class
|
|
Packit |
6bd9ab |
#
|
|
Packit |
6bd9ab |
# Copyright (C) 2011, 2012, 2013 Arthur de Jong
|
|
Packit |
6bd9ab |
#
|
|
Packit |
6bd9ab |
# This library is free software; you can redistribute it and/or
|
|
Packit |
6bd9ab |
# modify it under the terms of the GNU Lesser General Public
|
|
Packit |
6bd9ab |
# License as published by the Free Software Foundation; either
|
|
Packit |
6bd9ab |
# version 2.1 of the License, or (at your option) any later version.
|
|
Packit |
6bd9ab |
#
|
|
Packit |
6bd9ab |
# This library is distributed in the hope that it will be useful,
|
|
Packit |
6bd9ab |
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit |
6bd9ab |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Packit |
6bd9ab |
# Lesser General Public License for more details.
|
|
Packit |
6bd9ab |
#
|
|
Packit |
6bd9ab |
# You should have received a copy of the GNU Lesser General Public
|
|
Packit |
6bd9ab |
# License along with this library; if not, write to the Free Software
|
|
Packit |
6bd9ab |
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
|
Packit |
6bd9ab |
# 02110-1301 USA
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
"""Module for handling attribute mappings used for LDAP searches.
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
>>> attrs = Attributes(uid='uid',
|
|
Packit |
6bd9ab |
... userPassword='userPassword',
|
|
Packit |
6bd9ab |
... uidNumber='uidNumber',
|
|
Packit |
6bd9ab |
... gidNumber='gidNumber',
|
|
Packit |
6bd9ab |
... gecos='"${gecos:-$cn}"',
|
|
Packit |
6bd9ab |
... homeDirectory='homeDirectory',
|
|
Packit |
6bd9ab |
... loginShell='loginShell')
|
|
Packit |
6bd9ab |
>>> 'cn' in attrs.attributes()
|
|
Packit |
6bd9ab |
True
|
|
Packit |
6bd9ab |
>>> attrs.translate({'uid': ['UIDVALUE', '2nduidvalue'], 'cn': ['COMMON NAME', ]})
|
|
Packit |
6bd9ab |
{'uid': ['UIDVALUE', '2nduidvalue'], 'loginShell': [], 'userPassword': [], 'uidNumber': [], 'gidNumber': [], 'gecos': ['COMMON NAME'], 'homeDirectory': []}
|
|
Packit |
6bd9ab |
>>> attrs['uidNumber'] # a representation fit for logging and filters
|
|
Packit |
6bd9ab |
'uidNumber'
|
|
Packit |
6bd9ab |
>>> attrs['gecos']
|
|
Packit |
6bd9ab |
'"${gecos:-$cn}"'
|
|
Packit |
6bd9ab |
"""
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
import re
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
from ldap.filter import escape_filter_chars
|
|
Packit |
6bd9ab |
import ldap.dn
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
from expr import Expression
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
# exported names
|
|
Packit |
6bd9ab |
__all__ = ('Attributes', )
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
# TODO: support objectSid attributes
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
# regular expression to match function attributes
|
|
Packit |
6bd9ab |
attribute_func_re = re.compile('^(?P<function>[a-z]+)\((?P<attribute>.*)\)$')
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
class SimpleMapping(str):
|
|
Packit |
6bd9ab |
"""Simple mapping to another attribute name."""
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
def attributes(self):
|
|
Packit |
6bd9ab |
return [self]
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
def mk_filter(self, value):
|
|
Packit |
6bd9ab |
return '(%s=%s)' % (
|
|
Packit |
6bd9ab |
self, escape_filter_chars(str(value))
|
|
Packit |
6bd9ab |
)
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
def values(self, variables):
|
|
Packit |
6bd9ab |
"""Expand the expression using the variables specified."""
|
|
Packit |
6bd9ab |
return variables.get(self, [])
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
class ExpressionMapping(str):
|
|
Packit |
6bd9ab |
"""Class for parsing and expanding an expression."""
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
def __init__(self, value):
|
|
Packit |
6bd9ab |
"""Parse the expression as a string."""
|
|
Packit |
6bd9ab |
self.expression = Expression(value[1:-1])
|
|
Packit |
6bd9ab |
super(ExpressionMapping, self).__init__(value)
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
def values(self, variables):
|
|
Packit |
6bd9ab |
"""Expand the expression using the variables specified."""
|
|
Packit |
6bd9ab |
return [self.expression.value(variables)]
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
def attributes(self):
|
|
Packit |
6bd9ab |
"""Return the attributes defined in the expression."""
|
|
Packit |
6bd9ab |
return self.expression.variables()
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
class FunctionMapping(str):
|
|
Packit |
6bd9ab |
"""Mapping to a function to another attribute."""
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
def __init__(self, mapping):
|
|
Packit |
6bd9ab |
self.mapping = mapping
|
|
Packit |
6bd9ab |
m = attribute_func_re.match(mapping)
|
|
Packit |
6bd9ab |
self.attribute = m.group('attribute')
|
|
Packit |
6bd9ab |
self.function = getattr(self, m.group('function'))
|
|
Packit |
6bd9ab |
super(FunctionMapping, self).__init__(mapping)
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
def upper(self, value):
|
|
Packit |
6bd9ab |
return value.upper()
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
def lower(self, value):
|
|
Packit |
6bd9ab |
return value.lower()
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
def attributes(self):
|
|
Packit |
6bd9ab |
return [self.attribute]
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
def mk_filter(self, value):
|
|
Packit |
6bd9ab |
return '(%s=%s)' % (
|
|
Packit |
6bd9ab |
self.attribute, escape_filter_chars(value)
|
|
Packit |
6bd9ab |
)
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
def values(self, variables):
|
|
Packit |
6bd9ab |
return [self.function(value)
|
|
Packit |
6bd9ab |
for value in variables.get(self.attribute, [])]
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
class Attributes(dict):
|
|
Packit |
6bd9ab |
"""Dictionary-like class for handling attribute mapping."""
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
def __init__(self, *args, **kwargs):
|
|
Packit |
6bd9ab |
self.update(*args, **kwargs)
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
def __setitem__(self, attribute, mapping):
|
|
Packit |
6bd9ab |
# translate the mapping into a mapping object
|
|
Packit |
6bd9ab |
if mapping[0] == '"' and mapping[-1] == '"':
|
|
Packit |
6bd9ab |
mapping = ExpressionMapping(mapping)
|
|
Packit |
6bd9ab |
elif '(' in mapping:
|
|
Packit |
6bd9ab |
mapping = FunctionMapping(mapping)
|
|
Packit |
6bd9ab |
else:
|
|
Packit |
6bd9ab |
mapping = SimpleMapping(mapping)
|
|
Packit |
6bd9ab |
super(Attributes, self).__setitem__(attribute, mapping)
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
def update(self, *args, **kwargs):
|
|
Packit |
6bd9ab |
for arg in args:
|
|
Packit |
6bd9ab |
other = dict(arg)
|
|
Packit |
6bd9ab |
for key in other:
|
|
Packit |
6bd9ab |
self[key] = other[key]
|
|
Packit |
6bd9ab |
for key in kwargs:
|
|
Packit |
6bd9ab |
self[key] = kwargs[key]
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
def attributes(self):
|
|
Packit |
6bd9ab |
"""Return the list of attributes that are referenced in this
|
|
Packit |
6bd9ab |
attribute mapping. These are the attributes that should be
|
|
Packit |
6bd9ab |
requested in the search."""
|
|
Packit |
6bd9ab |
attributes = set()
|
|
Packit |
6bd9ab |
for mapping in self.itervalues():
|
|
Packit |
6bd9ab |
attributes.update(mapping.attributes())
|
|
Packit |
6bd9ab |
return list(attributes)
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
def mk_filter(self, attribute, value):
|
|
Packit |
6bd9ab |
"""Construct a search filter for searching for the attribute value
|
|
Packit |
6bd9ab |
combination."""
|
|
Packit |
6bd9ab |
mapping = self.get(attribute, SimpleMapping(attribute))
|
|
Packit |
6bd9ab |
return mapping.mk_filter(value)
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
def translate(self, variables):
|
|
Packit |
6bd9ab |
"""Return a dictionary with every attribute mapped to their value from
|
|
Packit |
6bd9ab |
the specified variables."""
|
|
Packit |
6bd9ab |
results = dict()
|
|
Packit |
6bd9ab |
for attribute, mapping in self.iteritems():
|
|
Packit |
6bd9ab |
results[attribute] = mapping.values(variables)
|
|
Packit |
6bd9ab |
return results
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
def get_rdn_value(self, dn, attribute):
|
|
Packit |
6bd9ab |
"""Extract the attribute value from from DN if possible. Return None
|
|
Packit |
6bd9ab |
otherwise."""
|
|
Packit |
6bd9ab |
return self.translate(dict(
|
|
Packit |
6bd9ab |
(x, [y])
|
|
Packit |
6bd9ab |
for x, y, z in ldap.dn.str2dn(dn)[0]
|
|
Packit |
6bd9ab |
))[attribute][0]
|