server/sources/ldapuser.py
changeset 6945 28bf94d062a9
parent 6943 406a41c25e13
child 6957 ffda12be2e9f
equal deleted inserted replaced
6944:0cf10429ad39 6945:28bf94d062a9
    32 FOR A PARTICULAR PURPOSE.
    32 FOR A PARTICULAR PURPOSE.
    33 """
    33 """
    34 from __future__ import division
    34 from __future__ import division
    35 from base64 import b64decode
    35 from base64 import b64decode
    36 
    36 
    37 from logilab.common.textutils import splitstrip
       
    38 from rql.nodes import Relation, VariableRef, Constant, Function
       
    39 
       
    40 import ldap
    37 import ldap
    41 from ldap.ldapobject import ReconnectLDAPObject
    38 from ldap.ldapobject import ReconnectLDAPObject
    42 from ldap.filter import filter_format, escape_filter_chars
    39 from ldap.filter import filter_format, escape_filter_chars
    43 from ldapurl import LDAPUrl
    40 from ldapurl import LDAPUrl
    44 
    41 
    45 from logilab.common.configuration import time_validator
    42 from rql.nodes import Relation, VariableRef, Constant, Function
       
    43 
    46 from cubicweb import AuthenticationError, UnknownEid, RepositoryError
    44 from cubicweb import AuthenticationError, UnknownEid, RepositoryError
    47 from cubicweb.server.utils import cartesian_product
    45 from cubicweb.server.utils import cartesian_product
    48 from cubicweb.server.sources import (AbstractSource, TrFunc, GlobTrFunc,
    46 from cubicweb.server.sources import (AbstractSource, TrFunc, GlobTrFunc,
    49                                      ConnectionWrapper, TimedCache)
    47                                      ConnectionWrapper, TimedCache)
    50 
    48 
   166           'group': 'ldap-source', 'level': 3,
   164           'group': 'ldap-source', 'level': 3,
   167           }),
   165           }),
   168 
   166 
   169     )
   167     )
   170 
   168 
   171     def __init__(self, repo, source_config, *args, **kwargs):
   169     def __init__(self, repo, source_config, eid=None):
   172         AbstractSource.__init__(self, repo, source_config, *args, **kwargs)
   170         AbstractSource.__init__(self, repo, source_config, eid)
   173         self.host = source_config['host']
   171         self.update_config(None, self.check_conf_dict(eid, source_config))
   174         self.protocol = source_config.get('protocol', 'ldap')
   172         self._conn = None
   175         self.authmode = source_config.get('auth-mode', 'simple')
   173 
       
   174     def update_config(self, source_entity, typedconfig):
       
   175         """update configuration from source entity. `typedconfig` is config
       
   176         properly typed with defaults set
       
   177         """
       
   178         self.host = typedconfig['host']
       
   179         self.protocol = typedconfig['protocol']
       
   180         self.authmode = typedconfig['auth-mode']
   176         self._authenticate = getattr(self, '_auth_%s' % self.authmode)
   181         self._authenticate = getattr(self, '_auth_%s' % self.authmode)
   177         self.cnx_dn = source_config.get('data-cnx-dn') or ''
   182         self.cnx_dn = typedconfig['data-cnx-dn']
   178         self.cnx_pwd = source_config.get('data-cnx-password') or ''
   183         self.cnx_pwd = typedconfig['data-cnx-password']
   179         self.user_base_scope = globals()[source_config['user-scope']]
   184         self.user_base_dn = str(typedconfig['user-base-dn'])
   180         self.user_base_dn = str(source_config['user-base-dn'])
   185         self.user_base_scope = globals()[typedconfig['user-scope']]
   181         self.user_base_scope = globals()[source_config['user-scope']]
   186         self.user_login_attr = typedconfig['user-login-attr']
   182         self.user_classes = splitstrip(source_config['user-classes'])
   187         self.user_default_groups = typedconfig['user-default-group']
   183         self.user_login_attr = source_config['user-login-attr']
   188         self.user_attrs = typedconfig['user-attrs-map']
   184         self.user_default_groups = splitstrip(source_config['user-default-group'])
       
   185         self.user_attrs = dict(v.split(':', 1) for v in splitstrip(source_config['user-attrs-map']))
       
   186         self.user_filter = source_config.get('user-filter')
       
   187         self.user_rev_attrs = {'eid': 'dn'}
   189         self.user_rev_attrs = {'eid': 'dn'}
   188         for ldapattr, cwattr in self.user_attrs.items():
   190         for ldapattr, cwattr in self.user_attrs.items():
   189             self.user_rev_attrs[cwattr] = ldapattr
   191             self.user_rev_attrs[cwattr] = ldapattr
   190         self.base_filters = self._make_base_filters()
   192         self.base_filters = [filter_format('(%s=%s)', ('objectClass', o))
   191         self._conn = None
   193                              for o in typedconfig['user-classes']]
   192         self._cache = {}
   194         if typedconfig['user-filter']:
   193         # ttlm is in minutes!
   195             self.base_filters.append(typedconfig['user-filter'])
   194         self._cache_ttl = time_validator(None, None,
   196         self._interval = typedconfig['synchronization-interval']
   195                               source_config.get('cache-life-time', 2*60*60))
   197         self._cache_ttl = max(71, typedconfig['cache-life-time'])
   196         self._cache_ttl = max(71, self._cache_ttl)
   198         self.reset_caches()
   197         self._query_cache = TimedCache(self._cache_ttl)
       
   198         # interval is in seconds !
       
   199         self._interval = time_validator(None, None,
       
   200                                     source_config.get('synchronization-interval',
       
   201                                                       24*60*60))
       
   202 
       
   203     def _make_base_filters(self):
       
   204         filters =  [filter_format('(%s=%s)', ('objectClass', o))
       
   205                               for o in self.user_classes] 
       
   206         if self.user_filter:
       
   207             filters += [self.user_filter]
       
   208         return filters
       
   209 
   199 
   210     def reset_caches(self):
   200     def reset_caches(self):
   211         """method called during test to reset potential source caches"""
   201         """method called during test to reset potential source caches"""
   212         self._cache = {}
   202         self._cache = {}
   213         self._query_cache = TimedCache(self._cache_ttl)
   203         self._query_cache = TimedCache(self._cache_ttl)
   298             # On Windows + ADAM this would have succeeded (!!!)
   288             # On Windows + ADAM this would have succeeded (!!!)
   299             # You get Authenticated as: 'NT AUTHORITY\ANONYMOUS LOGON'.
   289             # You get Authenticated as: 'NT AUTHORITY\ANONYMOUS LOGON'.
   300             # we really really don't want that
   290             # we really really don't want that
   301             raise AuthenticationError()
   291             raise AuthenticationError()
   302         searchfilter = [filter_format('(%s=%s)', (self.user_login_attr, login))]
   292         searchfilter = [filter_format('(%s=%s)', (self.user_login_attr, login))]
   303         searchfilter.extend(self._make_base_filters())
   293         searchfilter.extend(self.base_filters)
   304         searchstr = '(&%s)' % ''.join(searchfilter)
   294         searchstr = '(&%s)' % ''.join(searchfilter)
   305         # first search the user
   295         # first search the user
   306         try:
   296         try:
   307             user = self._search(session, self.user_base_dn,
   297             user = self._search(session, self.user_base_dn,
   308                                 self.user_base_scope, searchstr)[0]
   298                                 self.user_base_scope, searchstr)[0]