14 |
14 |
import urllib
|
15 |
15 |
import six
|
16 |
16 |
import os
|
|
17 |
import json
|
17 |
18 |
|
18 |
19 |
# code originaly copied from by now merely inspired by
|
19 |
20 |
# http://www.amherst.k12.oh.us/django-ldap.html
|
... | ... | |
256 |
257 |
'mandatory_attributes_values': {},
|
257 |
258 |
# mapping from LDAP attributes name to other names
|
258 |
259 |
'attribute_mappings': [],
|
|
260 |
# extra attributes retrieve by making other LDAP search using user object informations
|
|
261 |
'extra_attributes': {},
|
259 |
262 |
# realm for selecting an ldap configuration or formatting usernames
|
260 |
263 |
'realm': 'ldap',
|
261 |
264 |
# template for building username
|
... | ... | |
733 |
736 |
external_id_tuple))
|
734 |
737 |
for from_at, to_at in block['attribute_mappings']:
|
735 |
738 |
attributes.add(to_at)
|
|
739 |
for extra_at in block.get('extra_attributes', {}):
|
|
740 |
if 'loop_over_attribute' in block['extra_attributes'][extra_at]:
|
|
741 |
attributes.add(block['extra_attributes'][extra_at]['loop_over_attribute'])
|
|
742 |
at_mapping = block['extra_attributes'][extra_at].get('mapping', {})
|
|
743 |
for key in at_mapping:
|
|
744 |
if at_mapping[key] != 'dn':
|
|
745 |
attributes.add(at_mapping[key])
|
736 |
746 |
return list(set(map(str.lower, map(str, attributes))))
|
737 |
747 |
|
738 |
748 |
@classmethod
|
... | ... | |
740 |
750 |
'''Retrieve some attributes from LDAP, add mandatory values then apply
|
741 |
751 |
defined mappings between atrribute names'''
|
742 |
752 |
attributes = cls.get_ldap_attributes_names(block)
|
|
753 |
log.debug(u'Attrs names : %s' % attributes)
|
743 |
754 |
attribute_mappings = block['attribute_mappings']
|
744 |
755 |
mandatory_attributes_values = block['mandatory_attributes_values']
|
745 |
756 |
try:
|
... | ... | |
764 |
775 |
new = set(old) | set(attribute_map[from_attribute])
|
765 |
776 |
attribute_map[to_attribute] = list(new)
|
766 |
777 |
attribute_map['dn'] = force_text(dn)
|
|
778 |
|
|
779 |
# extra attributes
|
|
780 |
attribute_map = cls.get_ldap_extra_attributes(block, conn, dn, attribute_map)
|
|
781 |
|
|
782 |
return attribute_map
|
|
783 |
|
|
784 |
@classmethod
|
|
785 |
def get_ldap_extra_attributes(cls, block, conn, dn, attribute_map):
|
|
786 |
'''Retrieve extra attributes from LDAP'''
|
|
787 |
|
|
788 |
ldap_scopes = {
|
|
789 |
'base': ldap.SCOPE_BASE,
|
|
790 |
'one': ldap.SCOPE_ONELEVEL,
|
|
791 |
'sub': ldap.SCOPE_SUBTREE,
|
|
792 |
}
|
|
793 |
log.debug(u'Attrs before extra attributes : %s' % attribute_map)
|
|
794 |
for extra_attribute_name in block.get('extra_attributes', {}):
|
|
795 |
extra_attribute_config = block['extra_attributes'][extra_attribute_name]
|
|
796 |
extra_attribute_values = []
|
|
797 |
if 'loop_over_attribute' in extra_attribute_config:
|
|
798 |
extra_attribute_config['loop_over_attribute'] = extra_attribute_config['loop_over_attribute'].lower()
|
|
799 |
if extra_attribute_config['loop_over_attribute'] not in attribute_map:
|
|
800 |
log.debug('loop_over_attribute %s not present (or empty) in user object attributes retreived. Pass.' % extra_attribute_config['loop_over_attribute'])
|
|
801 |
continue
|
|
802 |
if 'filter' not in extra_attribute_config and 'basedn' not in extra_attribute_config:
|
|
803 |
log.warning('Extra attribute %s not correctly configured : you need to defined at least one of filter or basedn parameters' % extra_attribute_name)
|
|
804 |
for item in attribute_map[extra_attribute_config['loop_over_attribute']]:
|
|
805 |
ldap_filter = unicode(extra_attribute_config.get('filter', 'objectClass=*')).format(item=item, **attribute_map)
|
|
806 |
ldap_basedn = unicode(extra_attribute_config.get('basedn', block.get('basedn'))).format(item=item, **attribute_map)
|
|
807 |
ldap_scope = ldap_scopes.get(extra_attribute_config.get('scope', 'sub'), ldap.SCOPE_SUBTREE)
|
|
808 |
ldap_attributes_mapping = extra_attribute_config.get('mapping', {})
|
|
809 |
ldap_attributes_names = filter(lambda a: a != 'dn', ldap_attributes_mapping.values())
|
|
810 |
try:
|
|
811 |
results = conn.search_s(ldap_basedn, ldap_scope, ldap_filter, ldap_attributes_names)
|
|
812 |
except ldap.LDAPError:
|
|
813 |
log.exception('unable to retrieve extra attribute %s for item %s' % (extra_attribute_name, item))
|
|
814 |
continue
|
|
815 |
item_value = {}
|
|
816 |
for obj in results:
|
|
817 |
log.debug(u'Object retrieved for extra attr %s with item %s : %s' % (extra_attribute_name, item, obj))
|
|
818 |
obj_attributes = cls.normalize_ldap_results(obj[1])
|
|
819 |
obj_attributes[dn] = obj[0]
|
|
820 |
log.debug(u'Object attributes normalized for extra attr %s with item %s : %s' % (extra_attribute_name, item, obj_attributes))
|
|
821 |
for key in ldap_attributes_mapping:
|
|
822 |
item_value[key] = obj_attributes.get(ldap_attributes_mapping[key].lower())
|
|
823 |
log.debug(u'Object attribute %s value retrieved for extra attr %s with item %s : %s' % (ldap_attributes_mapping[key], extra_attribute_name, item, item_value[key]))
|
|
824 |
if not item_value[key]:
|
|
825 |
del item_value[key]
|
|
826 |
elif len(item_value[key]) == 1:
|
|
827 |
item_value[key] = item_value[key][0]
|
|
828 |
extra_attribute_values.append(item_value)
|
|
829 |
else:
|
|
830 |
log.warning('loop_over_attribute not defined for extra attribute %s' % extra_attribute_name)
|
|
831 |
extra_attribute_serialization = extra_attribute_config.get('serialization', None)
|
|
832 |
if extra_attribute_serialization is None:
|
|
833 |
attribute_map[extra_attribute_name] = extra_attribute_values
|
|
834 |
elif extra_attribute_serialization == 'json':
|
|
835 |
attribute_map[extra_attribute_name] = json.dumps(extra_attribute_values)
|
|
836 |
else:
|
|
837 |
log.warning('Invalid serialization type "%s" for extra attribute %s' % (extra_attribute_serialization, extra_attribute_name))
|
767 |
838 |
return attribute_map
|
768 |
839 |
|
769 |
840 |
@classmethod
|
... | ... | |
906 |
977 |
for block in cls.get_config():
|
907 |
978 |
names.update(cls.get_ldap_attributes_names(block))
|
908 |
979 |
names.update(block['mandatory_attributes_values'].keys())
|
|
980 |
names.update(block.get('extra_attributes', {}).keys())
|
909 |
981 |
return [(a, '%s (LDAP)' % a) for a in sorted(names)]
|
910 |
982 |
|
911 |
983 |
@classmethod
|