diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 0cb9044de9701e8ff7b807f45defa4eb5ffac5f2..1e833fdabc7c40d402a9de69ab1a1af58f047741 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -2,6 +2,8 @@ image: registry.git.cccv.de/infra/uffd/docker-images/buster variables: DEBIAN_FRONTEND: noninteractive + GIT_SUBMODULE_STRATEGY: normal + PYTHONPATH: deps/ldapalchemy before_script: - python3 -V @@ -28,7 +30,7 @@ unittests: stage: test script: - service slapd start - - UNITTEST_OPENLDAP=1 python3-coverage run --include './*.py' --omit 'tests/*.py' -m pytest --junitxml=report.xml || true + - UNITTEST_OPENLDAP=1 python3-coverage run --include 'uffd/*.py' -m pytest --junitxml=report.xml || true - python3-coverage report -m - python3-coverage html - python3-coverage xml diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000000000000000000000000000000000000..51e8aed7051feb4f6e990a67fdbd2aa5b24e7957 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "deps/ldapalchemy"] + path = deps/ldapalchemy + url = ../ldapalchemy.git diff --git a/deps/ldapalchemy b/deps/ldapalchemy new file mode 160000 index 0000000000000000000000000000000000000000..e223f1617e3452d66d20b9368a74d2bdf6cc1ba4 --- /dev/null +++ b/deps/ldapalchemy @@ -0,0 +1 @@ +Subproject commit e223f1617e3452d66d20b9368a74d2bdf6cc1ba4 diff --git a/ldap_mapper/__init__.py b/ldap_mapper/__init__.py deleted file mode 100644 index 3c0730e8c7c033bb53637547e58e81d62b77121a..0000000000000000000000000000000000000000 --- a/ldap_mapper/__init__.py +++ /dev/null @@ -1,30 +0,0 @@ -import ldap3 - -from .core import LDAPCommitError -from . import model, attribute, relationship - -__all__ = ['LDAPMapper', 'LDAPCommitError'] - -class LDAPMapper: - def __init__(self, server=None, bind_dn=None, bind_password=None): - - class Model(model.Model): - ldap_mapper = self - - self.Model = Model # pylint: disable=invalid-name - self.Session = model.Session # pylint: disable=invalid-name - self.Attribute = attribute.Attribute # pylint: disable=invalid-name - self.Relationship = relationship.Relationship # pylint: disable=invalid-name - self.Backreference = relationship.Backreference # pylint: disable=invalid-name - - if not hasattr(type(self), 'server'): - self.server = server - if not hasattr(type(self), 'bind_dn'): - self.bind_dn = bind_dn - if not hasattr(type(self), 'bind_password'): - self.bind_password = bind_password - if not hasattr(type(self), 'session'): - self.session = self.Session(self.get_connection) - - def get_connection(self): - return ldap3.Connection(self.server, self.bind_dn, self.bind_password, auto_bind=True) diff --git a/ldap_mapper/attribute.py b/ldap_mapper/attribute.py deleted file mode 100644 index bd776313fcfefd93238362a8263a03e40822d3b0..0000000000000000000000000000000000000000 --- a/ldap_mapper/attribute.py +++ /dev/null @@ -1,66 +0,0 @@ -from collections.abc import MutableSequence - -class AttributeList(MutableSequence): - def __init__(self, ldap_object, name, aliases): - self.__ldap_object = ldap_object - self.__name = name - self.__aliases = [name] + aliases - - def __get(self): - return list(self.__ldap_object.getattr(self.__name)) - - def __set(self, values): - for name in self.__aliases: - self.__ldap_object.setattr(name, values) - - def __repr__(self): - return repr(self.__get()) - - def __setitem__(self, key, value): - tmp = self.__get() - tmp[key] = value - self.__set(tmp) - - def __delitem__(self, key): - tmp = self.__get() - del tmp[key] - self.__set(tmp) - - def __len__(self): - return len(self.__get()) - - def __getitem__(self, key): - return self.__get()[key] - - def insert(self, index, value): - tmp = self.__get() - tmp.insert(index, value) - self.__set(tmp) - -class Attribute: - def __init__(self, name, aliases=None, multi=False, default=None): - self.name = name - self.aliases = aliases or [] - self.multi = multi - self.default = default - - def add_hook(self, obj): - if obj.ldap_object.getattr(self.name) == []: - self.__set__(obj, self.default() if callable(self.default) else self.default) - - def __set_name__(self, cls, name): - if self.default is not None: - cls.ldap_add_hooks = cls.ldap_add_hooks + (self.add_hook,) - - def __get__(self, obj, objtype=None): - if obj is None: - return self - if self.multi: - return AttributeList(obj.ldap_object, self.name, self.aliases) - return (obj.ldap_object.getattr(self.name) or [None])[0] - - def __set__(self, obj, values): - if not self.multi: - values = [values] - for name in [self.name] + self.aliases: - obj.ldap_object.setattr(name, values) diff --git a/ldap_mapper/core.py b/ldap_mapper/core.py deleted file mode 100644 index 57c8663ec421998c94f147860d3562512d4c15aa..0000000000000000000000000000000000000000 --- a/ldap_mapper/core.py +++ /dev/null @@ -1,277 +0,0 @@ -from ldap3 import MODIFY_REPLACE, MODIFY_DELETE, MODIFY_ADD, ALL_ATTRIBUTES, ALL_OPERATIONAL_ATTRIBUTES -from ldap3.utils.conv import escape_filter_chars - -def encode_filter(filter_params): - return '(&%s)'%(''.join(['(%s=%s)'%(attr, escape_filter_chars(value)) for attr, value in filter_params])) - -def match_dn(dn, base): - return dn.endswith(base) # Probably good enougth for all valid dns - -def make_cache_key(search_base, filter_params): - res = (search_base,) - for attr, value in sorted(filter_params): - res += ((attr, value),) - return res - -class LDAPCommitError(Exception): - pass - -class SessionState: - def __init__(self, objects=None, deleted_objects=None, references=None): - self.objects = objects or {} - self.deleted_objects = deleted_objects or {} - self.references = references or {} # {(attr_name, value): {srcobj, ...}, ...} - - def copy(self): - objects = self.objects.copy() - deleted_objects = self.deleted_objects.copy() - references = {key: objs.copy() for key, objs in self.references.items()} - return SessionState(objects, deleted_objects, references) - - def ref(self, obj, attr, values): - for value in values: - key = (attr, value) - if key not in self.references: - self.references[key] = {obj} - else: - self.references[key].add(obj) - - def unref(self, obj, attr, values): - for value in values: - self.references.get((attr, value), set()).discard(obj) - -class ObjectState: - def __init__(self, session=None, attributes=None, dn=None): - self.session = session - self.attributes = attributes or {} - self.dn = dn - - def copy(self): - attributes = {name: values.copy() for name, values in self.attributes.items()} - return ObjectState(attributes=attributes, dn=self.dn, session=self.session) - -class AddOperation: - def __init__(self, obj, dn, object_classes): - self.obj = obj - self.dn = dn - self.object_classes = object_classes - self.attributes = {name: values.copy() for name, values in obj.state.attributes.items()} - - def apply_object(self, obj_state): - obj_state.dn = self.dn - obj_state.attributes = {name: values.copy() for name, values in self.attributes.items()} - obj_state.attributes['objectClass'] = obj_state.attributes.get('objectClass', []) + list(self.object_classes) - - def apply_session(self, session_state): - assert self.dn not in session_state.objects - session_state.objects[self.dn] = self.obj - for name, values in self.attributes.items(): - session_state.ref(self.obj, name, values) - session_state.ref(self.obj, 'objectClass', self.object_classes) - - def apply_ldap(self, conn): - success = conn.add(self.dn, self.object_classes, self.attributes) - if not success: - raise LDAPCommitError() - -class DeleteOperation: - def __init__(self, obj): - self.dn = obj.state.dn - self.obj = obj - self.attributes = {name: values.copy() for name, values in obj.state.attributes.items()} - - def apply_object(self, obj_state): - obj_state.dn = None - - def apply_session(self, session_state): - assert self.dn in session_state.objects - del session_state.objects[self.dn] - session_state.deleted_objects[self.dn] = self.obj - for name, values in self.attributes.items(): - session_state.unref(self.obj, name, values) - - def apply_ldap(self, conn): - success = conn.delete(self.dn) - if not success: - raise LDAPCommitError() - -class ModifyOperation: - def __init__(self, obj, changes): - self.obj = obj - self.attributes = {name: values.copy() for name, values in obj.state.attributes.items()} - self.changes = changes - - def apply_object(self, obj_state): - for attr, changes in self.changes.items(): - for action, values in changes: - if action == MODIFY_REPLACE: - obj_state.attributes[attr] = values - elif action == MODIFY_ADD: - obj_state.attributes[attr] += values - elif action == MODIFY_DELETE: - for value in values: - if value in obj_state.attributes[attr]: - obj_state.attributes[attr].remove(value) - - def apply_session(self, session_state): - for attr, changes in self.changes.items(): - for action, values in changes: - if action == MODIFY_REPLACE: - session_state.unref(self.obj, attr, self.attributes.get(attr, [])) - session_state.ref(self.obj, attr, values) - elif action == MODIFY_ADD: - session_state.ref(self.obj, attr, values) - elif action == MODIFY_DELETE: - session_state.unref(self.obj, attr, values) - - def apply_ldap(self, conn): - success = conn.modify(self.obj.state.dn, self.changes) - if not success: - raise LDAPCommitError() - -class Session: - def __init__(self, get_connection): - self.get_connection = get_connection - self.committed_state = SessionState() - self.state = SessionState() - self.changes = [] - self.cached_searches = set() - - def add(self, obj, dn, object_classes): - if self.state.objects.get(dn) == obj: - return - assert obj.state.session is None - oper = AddOperation(obj, dn, object_classes) - oper.apply_object(obj.state) - obj.state.session = self - oper.apply_session(self.state) - self.changes.append(oper) - - def delete(self, obj): - if obj.state.dn not in self.state.objects: - return - assert obj.state.session == self - oper = DeleteOperation(obj) - oper.apply_object(obj.state) - obj.state.session = None - oper.apply_session(self.state) - self.changes.append(oper) - - def record(self, oper): - assert oper.obj.state.session == self - self.changes.append(oper) - - def commit(self): - conn = self.get_connection() - while self.changes: - oper = self.changes.pop(0) - try: - oper.apply_ldap(conn) - except Exception as err: - self.changes.insert(0, oper) - raise err - oper.apply_object(oper.obj.committed_state) - oper.apply_session(self.committed_state) - self.committed_state = self.state.copy() - - def rollback(self): - for obj in self.state.objects.values(): - obj.state = obj.committed_state.copy() - for obj in self.state.deleted_objects.values(): - obj.state = obj.committed_state.copy() - self.state = self.committed_state.copy() - self.changes.clear() - - def get(self, dn, filter_params): - if dn in self.state.objects: - obj = self.state.objects[dn] - return obj if obj.match(filter_params) else None - if dn in self.state.deleted_objects: - return None - conn = self.get_connection() - conn.search(dn, encode_filter(filter_params), attributes=[ALL_ATTRIBUTES, ALL_OPERATIONAL_ATTRIBUTES]) - if not conn.response: - return None - assert len(conn.response) == 1 - assert conn.response[0]['dn'] == dn - obj = Object(self, conn.response[0]) - self.state.objects[dn] = obj - self.committed_state.objects[dn] = obj - for attr, values in obj.state.attributes.items(): - self.state.ref(obj, attr, values) - return obj - - def filter(self, search_base, filter_params): - if not filter_params: - matches = self.state.objects.values() - else: - submatches = [self.state.references.get((attr, value), set()) for attr, value in filter_params] - matches = submatches.pop(0) - while submatches: - matches = matches.intersection(submatches.pop(0)) - res = [obj for obj in matches if match_dn(obj.state.dn, search_base)] - cache_key = make_cache_key(search_base, filter_params) - if cache_key in self.cached_searches: - return res - conn = self.get_connection() - conn.search(search_base, encode_filter(filter_params), attributes=[ALL_ATTRIBUTES, ALL_OPERATIONAL_ATTRIBUTES]) - for response in conn.response: - dn = response['dn'] - if dn in self.state.objects or dn in self.state.deleted_objects: - continue - obj = Object(self, response) - self.state.objects[dn] = obj - self.committed_state.objects[dn] = obj - for attr, values in obj.state.attributes.items(): - self.state.ref(obj, attr, values) - res.append(obj) - self.cached_searches.add(cache_key) - return res - -class Object: - def __init__(self, session=None, response=None): - if response is None: - self.committed_state = ObjectState() - else: - assert session is not None - attrs = {attr: value if isinstance(value, list) else [value] for attr, value in response['attributes'].items()} - self.committed_state = ObjectState(session, attrs, response['dn']) - self.state = self.committed_state.copy() - - @property - def dn(self): - return self.state.dn - - @property - def session(self): - return self.state.session - - def getattr(self, name): - return self.state.attributes.get(name, []) - - def setattr(self, name, values): - oper = ModifyOperation(self, {name: [(MODIFY_REPLACE, values)]}) - oper.apply_object(self.state) - if self.state.session: - oper.apply_session(self.state.session.state) - self.state.session.changes.append(oper) - - def attr_append(self, name, value): - oper = ModifyOperation(self, {name: [(MODIFY_ADD, [value])]}) - oper.apply_object(self.state) - if self.state.session: - oper.apply_session(self.state.session.state) - self.state.session.changes.append(oper) - - def attr_remove(self, name, value): - oper = ModifyOperation(self, {name: [(MODIFY_DELETE, [value])]}) - oper.apply_object(self.state) - if self.state.session: - oper.apply_session(self.state.session.state) - self.state.session.changes.append(oper) - - def match(self, filter_params): - for attr, value in filter_params: - if value not in self.getattr(attr): - return False - return True diff --git a/ldap_mapper/dbutils.py b/ldap_mapper/dbutils.py deleted file mode 100644 index 42fccb96bbac3c1ad746b151260251c4260fe8af..0000000000000000000000000000000000000000 --- a/ldap_mapper/dbutils.py +++ /dev/null @@ -1,129 +0,0 @@ -from collections.abc import MutableSet - -from .model import add_to_session - -class DBRelationshipSet(MutableSet): - def __init__(self, dbobj, relattr, ldapcls, mapcls): - self.__dbobj = dbobj - self.__relattr = relattr - self.__ldapcls = ldapcls - self.__mapcls = mapcls - - def __get_dns(self): - return [mapobj.dn for mapobj in getattr(self.__dbobj, self.__relattr)] - - def __repr__(self): - return repr(set(self)) - - def __contains__(self, value): - if value is None or not isinstance(value, self.__ldapcls): - return False - return value.ldap_object.dn in self.__get_dns() - - def __iter__(self): - return iter(filter(lambda obj: obj is not None, [self.__ldapcls.query.get(dn) for dn in self.__get_dns()])) - - def __len__(self): - return len(set(self)) - - def add(self, value): - if not isinstance(value, self.__ldapcls): - raise TypeError() - if value.ldap_object.session is None: - add_to_session(value, self.__ldapcls.ldap_mapper.session.ldap_session) - if value.ldap_object.dn not in self.__get_dns(): - getattr(self.__dbobj, self.__relattr).append(self.__mapcls(dn=value.ldap_object.dn)) - - def discard(self, value): - if not isinstance(value, self.__ldapcls): - raise TypeError() - rel = getattr(self.__dbobj, self.__relattr) - for mapobj in list(rel): - if mapobj.dn == value.ldap_object.dn: - rel.remove(mapobj) - -class DBRelationship: - def __init__(self, relattr, ldapcls, mapcls, backref=None, backattr=None): - self.relattr = relattr - self.ldapcls = ldapcls - self.mapcls = mapcls - self.backref = backref - self.backattr = backattr - - def __set_name__(self, cls, name): - if self.backref: - assert self.backattr - setattr(self.ldapcls, self.backref, DBBackreference(cls, self.relattr, self.mapcls, self.backattr)) - - def __get__(self, obj, objtype=None): - if obj is None: - return self - return DBRelationshipSet(obj, self.relattr, self.ldapcls, self.mapcls) - - def __set__(self, obj, values): - tmp = self.__get__(obj) - tmp.clear() - for value in values: - tmp.add(value) - -class DBBackreferenceSet(MutableSet): - def __init__(self, ldapobj, dbcls, relattr, mapcls, backattr): - self.__ldapobj = ldapobj - self.__dbcls = dbcls - self.__relattr = relattr - self.__mapcls = mapcls - self.__backattr = backattr - - @property - def __dn(self): - return self.__ldapobj.ldap_object.dn - - def __get(self): - return {getattr(mapobj, self.__backattr) for mapobj in self.__mapcls.query.filter_by(dn=self.__dn)} - - def __repr__(self): - return repr(self.__get()) - - def __contains__(self, value): - return value in self.__get() - - def __iter__(self): - return iter(self.__get()) - - def __len__(self): - return len(self.__get()) - - def add(self, value): - # TODO: add value to db session if necessary - assert self.__ldapobj.ldap_object.session is not None - if not isinstance(value, self.__dbcls): - raise TypeError() - rel = getattr(value, self.__relattr) - if self.__dn not in {mapobj.dn for mapobj in rel}: - rel.append(self.__mapcls(dn=self.__dn)) - - def discard(self, value): - if not isinstance(value, self.__dbcls): - raise TypeError() - rel = getattr(value, self.__relattr) - for mapobj in list(rel): - if mapobj.dn == self.__dn: - rel.remove(mapobj) - -class DBBackreference: - def __init__(self, dbcls, relattr, mapcls, backattr): - self.dbcls = dbcls - self.relattr = relattr - self.mapcls = mapcls - self.backattr = backattr - - def __get__(self, obj, objtype=None): - if obj is None: - return self - return DBBackreferenceSet(obj, self.dbcls, self.relattr, self.mapcls, self.backattr) - - def __set__(self, obj, values): - tmp = self.__get__(obj) - tmp.clear() - for value in values: - tmp.add(value) diff --git a/ldap_mapper/model.py b/ldap_mapper/model.py deleted file mode 100644 index 0dc9f240251aacf43ae457b3ff69aa7d82cdc0b9..0000000000000000000000000000000000000000 --- a/ldap_mapper/model.py +++ /dev/null @@ -1,118 +0,0 @@ -try: - # Added in v2.5 - from ldap3.utils.dn import escape_rdn -except ImportError: - # From ldap3 source code, Copyright Giovanni Cannata, LGPL v3 license - def escape_rdn(rdn): - # '/' must be handled first or the escape slashes will be escaped! - for char in ['\\', ',', '+', '"', '<', '>', ';', '=', '\x00']: - rdn = rdn.replace(char, '\\' + char) - if rdn[0] == '#' or rdn[0] == ' ': - rdn = ''.join(('\\', rdn)) - if rdn[-1] == ' ': - rdn = ''.join((rdn[:-1], '\\ ')) - return rdn - -from . import core - -def add_to_session(obj, session): - for func in obj.ldap_add_hooks: - func(obj) - session.add(obj.ldap_object, obj.dn, obj.ldap_object_classes) - -class Session: - def __init__(self, get_connection): - self.ldap_session = core.Session(get_connection) - - def add(self, obj): - add_to_session(obj, self.ldap_session) - - def delete(self, obj): - self.ldap_session.delete(obj.ldap_object) - - def commit(self): - self.ldap_session.commit() - - def rollback(self): - self.ldap_session.rollback() - -def make_modelobj(obj, model): - if obj is None: - return None - if not hasattr(obj, 'model'): - obj.model = model() - obj.model.ldap_object = obj - if not isinstance(obj.model, model): - return None - return obj.model - -def make_modelobjs(objs, model): - modelobjs = [] - for obj in objs: - modelobj = make_modelobj(obj, model) - if modelobj is not None: - modelobjs.append(modelobj) - return modelobjs - -class ModelQuery: - def __init__(self, model): - self.model = model - - def get(self, dn): - session = self.model.ldap_mapper.session.ldap_session - return make_modelobj(session.get(dn, self.model.ldap_filter_params), self.model) - - def all(self): - session = self.model.ldap_mapper.session.ldap_session - objs = session.filter(self.model.ldap_search_base, self.model.ldap_filter_params) - return make_modelobjs(objs, self.model) - - def filter_by(self, **kwargs): - filter_params = self.model.ldap_filter_params - filter_params += tuple((getattr(self.model, attr).name, value) for attr, value in kwargs.items()) - session = self.model.ldap_mapper.session.ldap_session - objs = session.filter(self.model.ldap_search_base, filter_params) - return make_modelobjs(objs, self.model) - -class ModelQueryWrapper: - def __get__(self, obj, objtype=None): - return ModelQuery(objtype) - -class Model: - # Overwritten by mapper - ldap_mapper = None - query = ModelQueryWrapper() - ldap_add_hooks = () - - # Overwritten by models - ldap_search_base = None - ldap_filter_params = () - ldap_object_classes = () - ldap_dn_base = None - ldap_dn_attribute = None - - def __init__(self, **kwargs): - self.ldap_object = core.Object() - for key, value, in kwargs.items(): - if not hasattr(self, key): - raise Exception() - setattr(self, key, value) - - @property - def dn(self): - if self.ldap_object.dn is not None: - return self.ldap_object.dn - if self.ldap_dn_base is None or self.ldap_dn_attribute is None: - return None - values = self.ldap_object.getattr(self.ldap_dn_attribute) - if not values: - return None - # escape_rdn can't handle empty strings - rdn = escape_rdn(values[0]) if values[0] else '' - return '%s=%s,%s'%(self.ldap_dn_attribute, rdn, self.ldap_dn_base) - - def __repr__(self): - cls_name = '%s.%s'%(type(self).__module__, type(self).__name__) - if self.dn is not None: - return '<%s %s>'%(cls_name, self.dn) - return '<%s>'%cls_name diff --git a/ldap_mapper/relationship.py b/ldap_mapper/relationship.py deleted file mode 100644 index cf5e42bb055e661bc6a72ba77d14900e38b53b7f..0000000000000000000000000000000000000000 --- a/ldap_mapper/relationship.py +++ /dev/null @@ -1,136 +0,0 @@ -from collections.abc import MutableSet - -from .model import make_modelobj, make_modelobjs, add_to_session - -class UnboundObjectError(Exception): - pass - -class RelationshipSet(MutableSet): - def __init__(self, ldap_object, name, model, destmodel): - self.__ldap_object = ldap_object - self.__name = name - self.__model = model - self.__destmodel = destmodel - - def __modify_check(self, value): - if self.__ldap_object.session is None: - raise UnboundObjectError() - if not isinstance(value, self.__destmodel): - raise TypeError() - - def __repr__(self): - return repr(set(self)) - - def __contains__(self, value): - if value is None or not isinstance(value, self.__destmodel): - return False - return value.ldap_object.dn in self.__ldap_object.getattr(self.__name) - - def __iter__(self): - def get(dn): - return make_modelobj(self.__ldap_object.session.get(dn, self.__destmodel.ldap_filter_params), self.__destmodel) - dns = set(self.__ldap_object.getattr(self.__name)) - return iter(filter(lambda obj: obj is not None, map(get, dns))) - - def __len__(self): - return len(set(self)) - - def add(self, value): - self.__modify_check(value) - if value.ldap_object.session is None: - add_to_session(value, self.__ldap_object.session) - assert value.ldap_object.session == self.__ldap_object.session - self.__ldap_object.attr_append(self.__name, value.dn) - - def discard(self, value): - self.__modify_check(value) - self.__ldap_object.attr_remove(self.__name, value.dn) - - def update(self, values): - for value in values: - self.add(value) - -class Relationship: - def __init__(self, name, destmodel, backref=None): - self.name = name - self.destmodel = destmodel - self.backref = backref - - def __set_name__(self, cls, name): - if self.backref is not None: - setattr(self.destmodel, self.backref, Backreference(self.name, cls)) - - def __get__(self, obj, objtype=None): - if obj is None: - return self - return RelationshipSet(obj.ldap_object, self.name, type(obj), self.destmodel) - - def __set__(self, obj, values): - tmp = self.__get__(obj) - tmp.clear() - for value in values: - tmp.add(value) - -class BackreferenceSet(MutableSet): - def __init__(self, ldap_object, name, model, srcmodel): - self.__ldap_object = ldap_object - self.__name = name - self.__model = model - self.__srcmodel = srcmodel - - def __modify_check(self, value): - if self.__ldap_object.session is None: - raise UnboundObjectError() - if not isinstance(value, self.__srcmodel): - raise TypeError() - - def __get(self): - if self.__ldap_object.session is None: - return set() - filter_params = self.__srcmodel.ldap_filter_params + ((self.__name, self.__ldap_object.dn),) - objs = self.__ldap_object.session.filter(self.__srcmodel.ldap_search_base, filter_params) - return set(make_modelobjs(objs, self.__srcmodel)) - - def __repr__(self): - return repr(self.__get()) - - def __contains__(self, value): - return value in self.__get() - - def __iter__(self): - return iter(self.__get()) - - def __len__(self): - return len(self.__get()) - - def add(self, value): - self.__modify_check(value) - if value.ldap_object.session is None: - add_to_session(value, self.__ldap_object.session) - assert value.ldap_object.session == self.__ldap_object.session - if self.__ldap_object.dn not in value.ldap_object.getattr(self.__name): - value.ldap_object.attr_append(self.__name, self.__ldap_object.dn) - - def discard(self, value): - self.__modify_check(value) - value.ldap_object.attr_remove(self.__name, self.__ldap_object.dn) - - def update(self, values): - for value in values: - self.add(value) - -class Backreference: - def __init__(self, name, srcmodel): - self.name = name - self.srcmodel = srcmodel - - def __get__(self, obj, objtype=None): - if obj is None: - return self - return BackreferenceSet(obj.ldap_object, self.name, type(obj), self.srcmodel) - - def __set__(self, obj, values): - tmp = self.__get__(obj) - tmp.clear() - for value in values: - tmp.add(value) diff --git a/uffd/__init__.py b/uffd/__init__.py index 3d5ae6262855692f379638f88a0f89b4a6adf067..417b982001f55777c08a946e3fb893c4c1708687 100644 --- a/uffd/__init__.py +++ b/uffd/__init__.py @@ -1,13 +1,17 @@ import os import secrets +import sys from flask import Flask, redirect, url_for from werkzeug.routing import IntegerConverter +sys.path.append('deps/ldapalchemy') + +# pylint: disable=wrong-import-position from uffd.database import db, SQLAlchemyJSON from uffd.template_helper import register_template_helper from uffd.navbar import setup_navbar - +# pylint: enable=wrong-import-position def create_app(test_config=None): # create and configure the app diff --git a/uffd/ldap.py b/uffd/ldap.py index 2b9fe0a47e2a795a815847d697198774b611aad0..237271e7e004d7103f3b4966a5b4a46f98b5d297 100644 --- a/uffd/ldap.py +++ b/uffd/ldap.py @@ -2,7 +2,7 @@ from flask import current_app, request import ldap3 -from ldap_mapper import LDAPMapper, LDAPCommitError # pylint: disable=unused-import +from ldapalchemy import LDAPMapper, LDAPCommitError # pylint: disable=unused-import class FlaskLDAPMapper(LDAPMapper): def __init__(self): diff --git a/uffd/role/models.py b/uffd/role/models.py index d7e0daf91d4de1b7204d46bbbfea9ee7f5e5e6f9..533d3677f36f885189c7730a84cdfee4e6ecdcb1 100644 --- a/uffd/role/models.py +++ b/uffd/role/models.py @@ -2,7 +2,7 @@ from sqlalchemy import Column, String, Integer, Text, ForeignKey from sqlalchemy.orm import relationship from sqlalchemy.ext.declarative import declared_attr -from ldap_mapper.dbutils import DBRelationship +from ldapalchemy.dbutils import DBRelationship from uffd.database import db from uffd.user.models import User, Group