diff --git a/uffd/ldap/__init__.py b/uffd/ldap/__init__.py index 0ba10c72f766f8344737250c7584b151f949660f..436be193007b4c7543e633bbb1f3009d68a27046 100644 --- a/uffd/ldap/__init__.py +++ b/uffd/ldap/__init__.py @@ -1,4 +1,6 @@ from .ldap import bp as ldap_bp -from .ldap import get_conn, user_conn, escape_filter_chars, uid_to_dn, loginname_to_dn, get_next_uid, loginname_is_safe +from .ldap import get_conn, user_conn, escape_filter_chars, uid_to_dn +from .ldap import loginname_to_dn, get_next_uid, loginname_is_safe +from .ldap import get_ldap_array_attribute_safe, get_ldap_attribute_safe bp = [ldap_bp] diff --git a/uffd/ldap/ldap.py b/uffd/ldap/ldap.py index 2d6d9c32f4c769e48bff5f77d42309e55da263fc..39fee2ff0d1eb97d497548419da17061f7461b3e 100644 --- a/uffd/ldap/ldap.py +++ b/uffd/ldap/ldap.py @@ -2,7 +2,7 @@ import string from flask import Blueprint, current_app from ldap3.utils.conv import escape_filter_chars -from ldap3.core.exceptions import LDAPBindError +from ldap3.core.exceptions import LDAPBindError, LDAPCursorError from ldap3 import Server, Connection, ALL, ALL_ATTRIBUTES, ALL_OPERATIONAL_ATTRIBUTES @@ -42,8 +42,7 @@ def uid_to_dn(uid): def loginname_to_dn(loginname): if loginname_is_safe(loginname): return 'uid={},{}'.format(loginname, current_app.config["LDAP_BASE_USER"]) - else: - raise Exception('unsafe login name') + raise Exception('unsafe login name') def loginname_is_safe(value): if len(value) > 32 or len(value) < 1: @@ -68,3 +67,25 @@ def get_next_uid(): if uid_to_dn(next_uid): raise Exception('No free uid found') return next_uid + +def get_ldap_attribute_safe(ldapobject, attribute): + try: + result = ldapobject[attribute].value if attribute in ldapobject else None + # we have to catch LDAPCursorError here, because ldap3 in older versions has a broken __contains__ function + # see https://github.com/cannatag/ldap3/issues/493 + # fixed in version 2.5 + # debian buster ships 2.4.1 + except LDAPCursorError: + result = None + return result + +def get_ldap_array_attribute_safe(ldapobject, attribute): + # if the aray is empty, the attribute does not exist. + # if there is only one elemtent, ldap returns a string and not an array with one element + # we sanitize this to always be an array + result = get_ldap_attribute_safe(ldapobject, attribute) + if not result: + result = [] + if isinstance(result, str): + result = [result] + return result diff --git a/uffd/role/models.py b/uffd/role/models.py index f91d9cc2ef98a44684361fe4ebb0bcac0da73199..3b4ca85fe495aaecd060c42eed6c3c2d8ec576a6 100644 --- a/uffd/role/models.py +++ b/uffd/role/models.py @@ -21,12 +21,12 @@ class LdapMapping(): id = Column(Integer(), primary_key=True, autoincrement=True) dn = Column(String(128)) @declared_attr - def role_id(cls): + def role_id(self): return Column(ForeignKey('role.id')) ldapclass = None def get_ldap(self): - return self.ldapclass.from_ldap_dn(dn) + return self.ldapclass.from_ldap_dn(self.dn) def set_ldap(self, value): self.dn = value['dn'] @@ -38,4 +38,3 @@ class RoleGroup(LdapMapping, db.Model): class RoleUser(LdapMapping, db.Model): __tablename__ = 'role-user' ldapclass = Group - diff --git a/uffd/role/views.py b/uffd/role/views.py index 8d4a28c8924d5669e81dc2c2b6182115a3d918ba..9863d56554092d170df68fe3072785bbf7df0a25 100644 --- a/uffd/role/views.py +++ b/uffd/role/views.py @@ -2,10 +2,8 @@ from flask import Blueprint, render_template, request, url_for, redirect, flash, from uffd.navbar import register_navbar from uffd.csrf import csrf_protect -from uffd.user.models import User, Group from uffd.role.models import Role from uffd.session import get_current_user, login_required, is_valid_session -from uffd.ldap import loginname_to_dn from uffd.database import db bp = Blueprint("role", __name__, template_folder='templates', url_prefix='/role/') diff --git a/uffd/user/models.py b/uffd/user/models.py index fb04136e9c86cd6c5c8584e442dba57702ee1027..fa3dbf6695bc760866c463fa0ee255f84059fe6e 100644 --- a/uffd/user/models.py +++ b/uffd/user/models.py @@ -16,21 +16,12 @@ class User(): @classmethod def from_ldap(cls, ldapobject): - # if you are in no groups, the "memberOf" attribute does not exist - # if you are only in one group, ldap returns a string not an array with one element - # we sanitize this to always be an array - try: - sanitized_groups = ldapobject['memberOf'].value if hasattr(ldapobject, 'memberOf') else [] - except: - sanitized_groups = [] - if isinstance(sanitized_groups, str): - sanitized_groups = [sanitized_groups] return User( uid=ldapobject['uidNumber'].value, loginname=ldapobject['uid'].value, displayname=ldapobject['cn'].value, mail=ldapobject['mail'].value, - groups=sanitized_groups, + groups=ldap.get_ldap_array_attribute_safe(ldapobject, 'memberOf') ) @classmethod @@ -124,21 +115,11 @@ class Group(): @classmethod def from_ldap(cls, ldapobject): - try: - description = ldapobject['description'].value if hasattr(ldapobject, 'description') else '' - except: - description = '' - # if a group has no members, "uniqueMember" attribute does not exist - # if a group has exactly one member, ldap returns a string not an array with one element - # we sanitize this to always be an array - sanitized_members = ldapobject['uniqueMember'] - if isinstance(sanitized_members, str): - sanitized_members = [sanitized_members] return Group( gid=ldapobject['gidNumber'].value, name=ldapobject['cn'].value, - members=sanitized_members, - description=description, + members=ldap.get_ldap_array_attribute_safe(ldapobject, 'uniqueMember'), + description=ldap.get_ldap_attribute_safe(ldapobject, 'description') or '', ) @classmethod