Projet

Général

Profil

0001-Manage-LDAP-extra-attributes-19365.patch

Patch - Benjamin Renard, 13 octobre 2017 14:06

Télécharger (6,12 ko)

Voir les différences:

Subject: [PATCH] Manage LDAP extra attributes (#19365)

This extra attributes are retreived by making other LDAP queries
with parameters composed by looping on an user object's attribute
values. A mapping will be apply on corresponding objects's attributes
and resulting informations are compiled in a list and serialize in
JSON (configurable, but JSON is the only format available for now).
 src/authentic2/backends/ldap_backend.py | 58 +++++++++++++++++++++++++++++++++
 1 file changed, 58 insertions(+)
src/authentic2/backends/ldap_backend.py
13 13
import urllib
14 14
import six
15 15
import os
16
import json
16 17

  
17 18
# code originaly copied from by now merely inspired by
18 19
# http://www.amherst.k12.oh.us/django-ldap.html
......
250 251
        'mandatory_attributes_values': {},
251 252
        # mapping from LDAP attributes name to other names
252 253
        'attribute_mappings': [],
254
        # extra attributes retrieve by making other LDAP search using user object informations
255
        'extra_attributes': {},
253 256
        # realm for selecting an ldap configuration or formatting usernames
254 257
        'realm': 'ldap',
255 258
        # template for building username
......
663 666
                external_id_tuple))
664 667
        for from_at, to_at in block['attribute_mappings']:
665 668
            attributes.add(to_at)
669
        for extra_at in block.get('extra_attributes', {}):
670
            if 'loop_over_attribute' in block['extra_attributes'][extra_at]:
671
                attributes.add(block['extra_attributes'][extra_at]['loop_over_attribute'])
672
            at_mapping = block['extra_attributes'][extra_at].get('mapping',{})
673
            for key in at_mapping:
674
                if at_mapping[key] != 'dn':
675
                    attributes.add(at_mapping[key])
666 676
        return list(set(map(str.lower, map(str, attributes))))
667 677

  
668 678
    @classmethod
......
693 703
            old = attribute_map.setdefault(to_attribute, [])
694 704
            new = set(old) | set(attribute_map[from_attribute])
695 705
            attribute_map[to_attribute] = list(new)
706

  
696 707
        attribute_map['dn'] = dn
708

  
709
        # extra attributes
710
        ldap_scopes = {
711
          'base': ldap.SCOPE_BASE,
712
          'one': ldap.SCOPE_ONELEVEL,
713
          'sub': ldap.SCOPE_SUBTREE,
714
        }
715
        for extra_attribute_name in block.get('extra_attributes', {}):
716
            extra_attribute_config = block['extra_attributes'][extra_attribute_name]
717
            extra_attribute_values = []
718
            if 'loop_over_attribute' in extra_attribute_config:
719
                extra_attribute_config['loop_over_attribute']=str.lower(extra_attribute_config['loop_over_attribute'])
720
                if extra_attribute_config['loop_over_attribute'] not in attribute_map:
721
                    raise ImproperlyConfigured('loop_over_attribute %s not present in user object attributes retreived' % extra_attribute_config['loop_over_attribute'])
722
                if 'filter' not in extra_attribute_config and 'basedn' not in extra_attribute_config:
723
                    raise ImproperlyConfigured('Extra attribute %s not correctly configured : you need to defined at least one of filter or basedn parameters' % extra_attribute_name)
724
                for item in attribute_map[extra_attribute_config['loop_over_attribute']]:
725
                    ldap_filter = unicode(extra_attribute_config.get('filter','objectClass=*')).format(item=item, **attribute_map)
726
                    ldap_basedn = unicode(extra_attribute_config.get('basedn',block.get('basedn'))).format(item=item, **attribute_map)
727
                    ldap_scope = ldap_scopes.get(extra_attribute_config.get('scope','sub'), ldap.SCOPE_SUBTREE)
728
                    ldap_attributes_mapping = extra_attribute_config.get('mapping', {})
729
                    ldap_attributes_names = filter(lambda a: a != 'dn', ldap_attributes_mapping.values())
730
                    try:
731
                        results = conn.search_s(ldap_basedn, ldap_scope, ldap_filter, ldap_attributes_names)
732
                    except ldap.LDAPError:
733
                        log.exception('unable to retrieve extra attribute %s for item %s' % (extra_attribute_name, item))
734
                        continue
735
                    item_value={}
736
                    for obj in results:
737
                        obj_attributes=cls.normalize_ldap_results(obj[1])
738
                        obj_attributes[dn]=obj[0]
739
                        for key in ldap_attributes_mapping:
740
                            item_value[key]=obj_attributes.get(ldap_attributes_mapping[key])
741
                            if not item_value[key]:
742
                                del item_value[key]
743
                            elif len(item_value[key])==1:
744
                                item_value[key]=item_value[key][0]
745
                    extra_attribute_values.append(item_value)
746
            else:
747
                raise ImproperlyConfigured('loop_over_attribute not defined for extra attribute %s' % extra_attribute_name)
748
            extra_attribute_serialization = extra_attribute_config.get('serialization','json')
749
            if extra_attribute_serialization == 'json':
750
                attribute_map[extra_attribute_name] = json.dumps(extra_attribute_values)
751
            else:
752
                raise ImproperlyConfigured('Invalid serialization type "%s" for extra attribute %s' % (extra_attribute_serialization, extra_attribute_name))
753

  
697 754
        return attribute_map
698 755

  
699 756
    @classmethod
......
830 887
        for block in cls.get_config():
831 888
            names.update(cls.get_ldap_attributes_names(block))
832 889
            names.update(block['mandatory_attributes_values'].keys())
890
            names.update(block.get('extra_attributes',{}).keys())
833 891
        return [(a, '%s (LDAP)' % a) for a in sorted(names)]
834 892

  
835 893
    @classmethod
836
-