Ticket #2507: ldap.diff

File ldap.diff, 9.2 KB (added by spr@…, 18 years ago)

Random password, and adding defaults to global_settings.py to avoid loading errors.

  • django/conf/global_settings.py

     
    296296##################
    297297
    298298AUTHENTICATION_BACKENDS = ('django.contrib.auth.backends.ModelBackend',)
     299
     300# Defaults for LDAPBackend
     301LDAP_SERVER_URI = 'ldap://localhost'
     302LDAP_SEARCHDN = 'dc=localhost'
     303LDAP_SCOPE = 2  # ldap.SCOPE_SUBTREE
     304LDAP_SEARCH_FILTER = 'cn=%s'
     305LDAP_UPDATE_FIELDS = True
     306LDAP_BIND_DN = 'dc=localhost'
     307LDAP_BIND_ATTRIBUTE = 'dn'
  • django/contrib/auth/backends.py

     
    11from django.contrib.auth.models import User
     2from django.conf import settings
    23
    34class ModelBackend:
    45    """
     
    1920            return User.objects.get(pk=user_id)
    2021        except User.DoesNotExist:
    2122            return None
     23
     24class LDAPBackend(object):
     25    """
     26    Authenticate a user against LDAP.
     27    Requires python-ldap to be installed.
     28
     29    Requires the following things to be in settings.py:
     30    LDAP_SERVER_URI -- string, ldap uri.
     31        default: 'ldap://localhost'
     32    LDAP_SEARCHDN -- string of the LDAP dn to use for searching
     33        default: 'dc=localhost'
     34    LDAP_SCOPE -- one of: ldap.SCOPE_*, used for searching
     35        ldap.SCOPE_BASE = 0
     36        ldap.SCOPE_ONELEVEL = 1
     37        ldap.SCOPE_SUBTREE = 2
     38        see python-ldap docs for the search function
     39        default = ldap.SCOPE_SUBTREE
     40    LDAP_SEARCH_FILTER -- formated string, the filter to use for searching for a
     41        user. Used as: filterstr = LDAP_SEARCH_FILTER % username
     42        default = 'cn=%s'
     43    LDAP_UPDATE_FIELDS -- boolean, do we sync the db with ldap on each auth
     44        default = True
     45
     46    Bind Options (unused if LDAP_BIND_STRING_FUNC is set):
     47    LDAP_BINDDN -- string of the LDAP dn to use for binding
     48    LDAP_BIND_ATTRIBUTE -- string of the LDAP attribute to use in binding.
     49
     50    Required unless LDAP_FULL_NAME is set:
     51    LDAP_FIRST_NAME -- string, LDAP attribute to get the given name from
     52    LDAP_LAST_NAME -- string, LDAP attribute to get the last name from
     53
     54    Optional Settings:
     55    LDAP_FULL_NAME -- string, LDAP attribute to get name from, splits on ' '
     56    LDAP_GID -- string, LDAP attribute to get group name/number from
     57    LDAP_SU_GIDS -- list of strings, group names/numbers that are superusers
     58    LDAP_STAFF_GIDS -- list of strings, group names/numbers that are staff
     59    LDAP_EMAIL -- string, LDAP attribute to get email from
     60    LDAP_DEFAULT_EMAIL_SUFFIX -- string, appened to username if no email found
     61    LDAP_OPTIONS -- hash, python-ldap global options and their values
     62        {ldap.OPT_X_TLS_CACERTDIR: '/etc/ldap/ca/'}
     63    LDAP_BIND_STRING_FUNC -- Function to produce the string for binding the user
     64        takes two arguments: the ldap object, a string username
     65        ie: the function will be called like so: function(ldapobject, username)
     66
     67    How binding is done:
     68    LDAP_BINDDN = 'ou=people,dc=example,dc=com'
     69    LDAP_BIND_ATTRIBUTE = 'uid'
     70    # The bind would be performed via:
     71    # uid=username,ou=people,dc=example,dc=com
     72    """
     73    import ldap
     74
     75    def authenticate(self, username=None, password=None):
     76        if not username and password is not None: # we need a user/pass
     77            l.unbind_s()
     78            return None
     79
     80        if hasattr(settings, 'LDAP_OPTIONS'):
     81            for k in settings.LDAP_OPTIONS:
     82                ldap.set_option(k, settings.LDAP_OPTIONS[k])
     83
     84        l = ldap.initialize(settings.LDAP_SERVER_URI)
     85
     86        if hasattr(settings, 'LDAP_BIND_STRING_FUNC'):
     87            bind_string = settings.LDAP_BIND_STRING_FUNC(l, username)
     88            if bind_string is None:
     89                return None
     90        else:
     91            bind_string = "%s=%s,%s" % (settings.LDAP_BIND_ATTRIBUTE,
     92                    username, settings.LDAP_BINDDN)
     93        try:
     94            l.bind_s(bind_string, password)
     95        except ldap.INVALID_CREDENTIALS: # Failed user/pass
     96            l.unbind_s()
     97            return None
     98
     99        try:
     100            user = User.objects.get(username=username)
     101        except User.DoesNotExist:
     102            user = None
     103
     104        if user is not None:
     105            if settings.LDAP_UPDATE_FIELDS:
     106                LDAPBackend.update_user(l, user)
     107        else:
     108            user = LDAPBackend.get_ldap_user(l, username)
     109
     110        l.unbind_s()
     111        return user
     112
     113    def get_user(self, user_id):
     114        try:
     115            return User.objects.get(pk=user_id)
     116        except:
     117            return None
     118
     119    def get_ldap_user(l, username):
     120        """
     121        Helper method, makes a user object and call update_user to populate
     122        """
     123
     124        # Generate a random password string.
     125        from random import choice
     126        import string
     127        password = ''.join([choice(string.printable) for i in range(10)])
     128
     129        user = User(username=username, password=password)
     130        LDAPBackend.update_user(l, user)
     131        return user
     132    get_ldap_user = staticmethod(get_ldap_user)
     133
     134    def update_user(l, user):
     135        """
     136        Helper method, populates a user object with various attributes from
     137        LDAP
     138        """
     139
     140        username = user.username
     141        filter = settings.LDAP_SEARCH_FILTER % username
     142        attrs = l.search_s(settings.LDAP_SEARCHDN, settings.LDAP_SCOPE,
     143                filter)[0][1]
     144
     145        if (hasattr(settings, 'LDAP_FIRST_NAME')
     146                and hasattr(settings, 'LDAP_LAST_NAME')):
     147            if (settings.LDAP_FIRST_NAME in attrs
     148                    and settings.LDAP_LAST_NAME in attrs):
     149                user.first_name = attrs[settings.LDAP_FIRST_NAME][0]
     150                user.last_name = attrs[settings.LDAP_LAST_NAME][0]
     151            else:
     152                raise NameError('Missing needed fields %s or %s in LDAP'
     153                        % (settings.LDAP_FIRST_NAME, settings.LDAP_LAST_NAME))
     154        elif hasattr(settings, 'LDAP_FULL_NAME'):
     155                if settings.LDAP_FULL_NAME in attrs:
     156                    tmp = attrs[settings.FULL_NAME_FIELD][0]
     157                    user.first_name = tmp.split(' ')[0]
     158                    user.last_name = ' '.join(tmp.split(' ')[1:])
     159                else:
     160                    raise NameError('Required field %s missing in LDAP'
     161                            % (settings.LDAP_FULL_NAME))
     162        else:
     163            raise NameError('Name fields not defined in settings.py')
     164
     165        if hasattr(settings, 'LDAP_EMAIL') and settings.LDAP_EMAIL in attrs:
     166            user.email = attrs[settings.EMAIL_FIELD][0]
     167        elif hasattr(settings, 'LDAP_DEFAULT_EMAIL_SUFFIX'):
     168            user.email = username + settings.LDAP_DEFAULT_EMAIL_SUFFIX
     169
     170        if (hasattr(settings, 'LDAP_GID')
     171                and settings.LDAP_GID in attrs
     172                and hasattr(settings, 'LDAP_SU_GIDS')
     173                and attrs[settings.LDAP_GID][0] in settings.LDAP_SU_GIDS):
     174            user.is_superuser = True
     175            user.is_staff = True
     176        elif (hasattr(settings, 'LDAP_GID')
     177                and settings.LDAP_GID in attrs
     178                and hasattr(settings, 'LDAP_STAFF_GIDS')
     179                and attrs[settings.LDAP_GID][0] in settings.LDAP_STAFF_GIDS):
     180            user.is_superuser = False
     181            user.is_staff = True
     182        else:
     183            user.is_superuser = False
     184            user.is_staff = False
     185
     186        user.save()
     187    update_user = staticmethod(update_user)
     188
     189    def mk_pre_auth_bind(auth_user, auth_pass,
     190            search_base=settings.LDAP_SEARCHDN,
     191            search_scope=settings.LDAP_SCOPE,
     192            search_filter=settings.LDAP_SEARCH_FILTER):
     193        """
     194        Returns a function, to be used for LDAP_BIND_STRING_FUNC.
     195        auth_user -- string, ldap formatted user to bind with
     196        auth_pass -- string, password for auth_user
     197        search_base -- string, base for the ldap search
     198        search_filter -- string, ldap search filter to use, %'d with username
     199        search_scope -- ldap.SCOPE_* value, search scope
     200
     201        set LDAP_BIND_STRING_FUNC like so:
     202        LDAP_BIND_STRING_FUNC = mk_pre_auth_bind('dn=Me,dc=example,dc=com,
     203                'pass')
     204        which defaults to:
     205        LDAP_BIND_STRING_FUNC = mk_pre_auth_bind('dn=Me,dc=example,dc=com,
     206                'pass', LDAP_SEARCHDN, LDAP_SCOPE, LDAP_SEARCH_FILTER)
     207        or you can customize it:
     208        LDAP_BIND_STRING_FUNC = mk_pre_auth_bind('dn=Me,dc=example,dc=com',
     209                'pass', 'ou=people,dc=example,dc=com'', ldap.SCOPE_SUBTREE,
     210                '(&(objectclass=person) (cn=%s))')
     211        """
     212        def pre_auth_bind(l, user):
     213            try:
     214                l.simple_bind_s(auth_user, auth_pass)
     215            except ldap.LDAPError:
     216                return None
     217
     218            filter = search_filter % user
     219
     220            result = l.search_s(search_base, search_scope, filter,
     221                    attrsonly=1)
     222
     223            if len(result) != 1:
     224                return None
     225
     226            return result[0][0]
     227        return pre_auth_bind
     228    mk_pre_auth_bind = staticmethod(mk_pre_auth_bind)
Back to Top