Projet

Général

Profil

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

Benjamin Renard, 15 novembre 2017 13:56

Télécharger (6,49 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 could be serialize
(JSON is the only format available for now).
 src/authentic2/backends/ldap_backend.py | 69 +++++++++++++++++++++++++++++++++
 1 file changed, 69 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
......
253 254
        'mandatory_attributes_values': {},
254 255
        # mapping from LDAP attributes name to other names
255 256
        'attribute_mappings': [],
257
        # extra attributes retrieve by making other LDAP search using user object informations
258
        'extra_attributes': {},
256 259
        # realm for selecting an ldap configuration or formatting usernames
257 260
        'realm': 'ldap',
258 261
        # template for building username
......
683 686
                external_id_tuple))
684 687
        for from_at, to_at in block['attribute_mappings']:
685 688
            attributes.add(to_at)
689
        for extra_at in block.get('extra_attributes', {}):
690
            if 'loop_over_attribute' in block['extra_attributes'][extra_at]:
691
                attributes.add(block['extra_attributes'][extra_at]['loop_over_attribute'])
692
            at_mapping = block['extra_attributes'][extra_at].get('mapping',{})
693
            for key in at_mapping:
694
                if at_mapping[key] != 'dn':
695
                    attributes.add(at_mapping[key])
686 696
        return list(set(map(str.lower, map(str, attributes))))
687 697

  
688 698
    @classmethod
......
713 723
            old = attribute_map.setdefault(to_attribute, [])
714 724
            new = set(old) | set(attribute_map[from_attribute])
715 725
            attribute_map[to_attribute] = list(new)
726

  
716 727
        attribute_map['dn'] = dn
728

  
729
        # extra attributes
730
        attribute_map = cls.get_ldap_extra_attributes(block, conn, dn, attribute_map)
731

  
732
        return attribute_map
733

  
734
    @classmethod
735
    def get_ldap_extra_attributes(cls, block, conn, dn, attribute_map):
736
        '''Retrieve extra attributes from LDAP'''
737

  
738
        ldap_scopes = {
739
          'base': ldap.SCOPE_BASE,
740
          'one': ldap.SCOPE_ONELEVEL,
741
          'sub': ldap.SCOPE_SUBTREE,
742
        }
743
        for extra_attribute_name in block.get('extra_attributes', {}):
744
            extra_attribute_config = block['extra_attributes'][extra_attribute_name]
745
            extra_attribute_values = []
746
            if 'loop_over_attribute' in extra_attribute_config:
747
                extra_attribute_config['loop_over_attribute']=str.lower(extra_attribute_config['loop_over_attribute'])
748
                if extra_attribute_config['loop_over_attribute'] not in attribute_map:
749
                    log.debug('loop_over_attribute %s not present (or empty) in user object attributes retreived. Pass.' % extra_attribute_config['loop_over_attribute'])
750
                    continue
751
                if 'filter' not in extra_attribute_config and 'basedn' not in extra_attribute_config:
752
                    log.warning('Extra attribute %s not correctly configured : you need to defined at least one of filter or basedn parameters' % extra_attribute_name)
753
                for item in attribute_map[extra_attribute_config['loop_over_attribute']]:
754
                    ldap_filter = unicode(extra_attribute_config.get('filter','objectClass=*')).format(item=item, **attribute_map)
755
                    ldap_basedn = unicode(extra_attribute_config.get('basedn',block.get('basedn'))).format(item=item, **attribute_map)
756
                    ldap_scope = ldap_scopes.get(extra_attribute_config.get('scope','sub'), ldap.SCOPE_SUBTREE)
757
                    ldap_attributes_mapping = extra_attribute_config.get('mapping', {})
758
                    ldap_attributes_names = filter(lambda a: a != 'dn', ldap_attributes_mapping.values())
759
                    try:
760
                        results = conn.search_s(ldap_basedn, ldap_scope, ldap_filter, ldap_attributes_names)
761
                    except ldap.LDAPError:
762
                        log.exception('unable to retrieve extra attribute %s for item %s' % (extra_attribute_name, item))
763
                        continue
764
                    item_value={}
765
                    for obj in results:
766
                        obj_attributes=cls.normalize_ldap_results(obj[1])
767
                        obj_attributes[dn]=obj[0]
768
                        for key in ldap_attributes_mapping:
769
                            item_value[key]=obj_attributes.get(ldap_attributes_mapping[key].lower())
770
                            if not item_value[key]:
771
                                del item_value[key]
772
                            elif len(item_value[key])==1:
773
                                item_value[key]=item_value[key][0]
774
                    extra_attribute_values.append(item_value)
775
            else:
776
                log.warning('loop_over_attribute not defined for extra attribute %s' % extra_attribute_name)
777
            extra_attribute_serialization = extra_attribute_config.get('serialization',None)
778
            if extra_attribute_serialization is None:
779
                attribute_map[extra_attribute_name] = extra_attribute_values
780
            elif extra_attribute_serialization == 'json':
781
                attribute_map[extra_attribute_name] = json.dumps(extra_attribute_values)
782
            else:
783
                log.warning('Invalid serialization type "%s" for extra attribute %s' % (extra_attribute_serialization, extra_attribute_name))
784

  
717 785
        return attribute_map
718 786

  
719 787
    @classmethod
......
852 920
        for block in cls.get_config():
853 921
            names.update(cls.get_ldap_attributes_names(block))
854 922
            names.update(block['mandatory_attributes_values'].keys())
923
            names.update(block.get('extra_attributes',{}).keys())
855 924
        return [(a, '%s (LDAP)' % a) for a in sorted(names)]
856 925

  
857 926
    @classmethod
858
-