django-auth-ldap-1.4.0/0000755000076600000240000000000013255035702015425 5ustar psagersstaff00000000000000django-auth-ldap-1.4.0/PKG-INFO0000644000076600000240000001027213255035702016524 0ustar psagersstaff00000000000000Metadata-Version: 1.2 Name: django-auth-ldap Version: 1.4.0 Summary: Django LDAP authentication backend Home-page: https://bitbucket.org/illocution/django-auth-ldap Author: Peter Sagerson Author-email: psagers@ignorare.net License: BSD Description: This is a Django authentication backend that authenticates against an LDAP service. Configuration can be as simple as a single distinguished name template, but there are many rich configuration options for working with users, groups, and permissions. This version is supported on Python 2.7 and 3.4+; and Django 1.11+. It requires `python-ldap `_ >= 3.0. * Repository: https://bitbucket.org/illocution/django-auth-ldap * Documentation: https://django-auth-ldap.readthedocs.io/ * Mailing list: https://groups.google.com/group/django-auth-ldap Following is an example configuration, just to whet your appetite:: import ldap from django_auth_ldap.config import LDAPSearch, GroupOfNamesType # Baseline configuration. AUTH_LDAP_SERVER_URI = "ldap://ldap.example.com" AUTH_LDAP_BIND_DN = "cn=django-agent,dc=example,dc=com" AUTH_LDAP_BIND_PASSWORD = "phlebotinum" AUTH_LDAP_USER_SEARCH = LDAPSearch("ou=users,dc=example,dc=com", ldap.SCOPE_SUBTREE, "(uid=%(user)s)") # or perhaps: # AUTH_LDAP_USER_DN_TEMPLATE = "uid=%(user)s,ou=users,dc=example,dc=com" # Set up the basic group parameters. AUTH_LDAP_GROUP_SEARCH = LDAPSearch("ou=django,ou=groups,dc=example,dc=com", ldap.SCOPE_SUBTREE, "(objectClass=groupOfNames)" ) AUTH_LDAP_GROUP_TYPE = GroupOfNamesType() # Simple group restrictions AUTH_LDAP_REQUIRE_GROUP = "cn=enabled,ou=django,ou=groups,dc=example,dc=com" AUTH_LDAP_DENY_GROUP = "cn=disabled,ou=django,ou=groups,dc=example,dc=com" # Populate the Django user from the LDAP directory. AUTH_LDAP_USER_ATTR_MAP = { "first_name": "givenName", "last_name": "sn", "email": "mail" } AUTH_LDAP_USER_FLAGS_BY_GROUP = { "is_active": "cn=active,ou=django,ou=groups,dc=example,dc=com", "is_staff": "cn=staff,ou=django,ou=groups,dc=example,dc=com", "is_superuser": "cn=superuser,ou=django,ou=groups,dc=example,dc=com" } # Use LDAP group membership to calculate group permissions. AUTH_LDAP_FIND_GROUP_PERMS = True # Cache group memberships for an hour to minimize LDAP traffic AUTH_LDAP_CACHE_GROUPS = True AUTH_LDAP_GROUP_CACHE_TIMEOUT = 3600 # Keep ModelBackend around for per-user permissions and maybe a local # superuser. AUTHENTICATION_BACKENDS = ( 'django_auth_ldap.backend.LDAPBackend', 'django.contrib.auth.backends.ModelBackend', ) Keywords: django,ldap,authentication,auth Platform: UNKNOWN Classifier: Development Status :: 5 - Production/Stable Classifier: Environment :: Web Environment Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.4 Classifier: Programming Language :: Python :: 3.5 Classifier: Programming Language :: Python :: 3.6 Classifier: Framework :: Django Classifier: Framework :: Django :: 1.11 Classifier: Framework :: Django :: 2.0 Classifier: Intended Audience :: Developers Classifier: Intended Audience :: System Administrators Classifier: License :: OSI Approved :: BSD License Classifier: Topic :: Internet :: WWW/HTTP Classifier: Topic :: System :: Systems Administration :: Authentication/Directory :: LDAP Classifier: Topic :: Software Development :: Libraries :: Python Modules Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.* django-auth-ldap-1.4.0/django_auth_ldap/0000755000076600000240000000000013255035702020710 5ustar psagersstaff00000000000000django-auth-ldap-1.4.0/django_auth_ldap/backend.py0000644000076600000240000010415213253750522022656 0ustar psagersstaff00000000000000# Copyright (c) 2009, Peter Sagerson # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # # - Redistributions of source code must retain the above copyright notice, this # list of conditions and the following disclaimer. # # - Redistributions in binary form must reproduce the above copyright notice, # this list of conditions and the following disclaimer in the documentation # and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. """ LDAP authentication backend Complete documentation can be found in docs/howto/auth-ldap.txt (or the thing it compiles to). Use of this backend requires the python-ldap module. To support unit tests, we import ldap in a single centralized place (config._LDAPConfig) so that the test harness can insert a mock object. A few notes on naming conventions. If an identifier ends in _dn, it is a string representation of a distinguished name. If it ends in _info, it is a 2-tuple containing a DN and a dictionary of lists of attributes. ldap.search_s returns a list of such structures. An identifier that ends in _attrs is the dictionary of attributes from the _info structure. A connection is an LDAPObject that has been successfully bound with a DN and password. The identifier 'user' always refers to a User model object; LDAP user information will be user_dn or user_info. Additional classes can be found in the config module next to this one. """ from __future__ import absolute_import, division, print_function, unicode_literals import copy from functools import reduce import operator import pprint import re import warnings import ldap import django.conf from django.contrib.auth import get_user_model from django.contrib.auth.models import Group, Permission from django.core.cache import cache from django.core.exceptions import ImproperlyConfigured, ObjectDoesNotExist import django.dispatch from django.utils import six from django.utils.encoding import force_str from django_auth_ldap.config import ConfigurationWarning, LDAPGroupQuery, LDAPSearch, _LDAPConfig logger = _LDAPConfig.get_logger() # Exported signals # Allows clients to perform custom user population. populate_user = django.dispatch.Signal(providing_args=["user", "ldap_user"]) # Allows clients to inspect and perform special handling of LDAPError # exceptions. Exceptions raised by handlers will be propagated out. ldap_error = django.dispatch.Signal(providing_args=['context', 'user', 'exception']) class LDAPBackend(object): """ The main backend class. This implements the auth backend API, although it actually delegates most of its work to _LDAPUser, which is defined next. """ supports_anonymous_user = False supports_object_permissions = True supports_inactive_user = False _settings = None _ldap = None # The cached ldap module (or mock object) # This is prepended to our internal setting names to produce the names we # expect in Django's settings file. Subclasses can change this in order to # support multiple collections of settings. settings_prefix = 'AUTH_LDAP_' # Default settings to override the built-in defaults. default_settings = {} def __getstate__(self): """ Exclude certain cached properties from pickling. """ return {k: v for k, v in self.__dict__.items() if k not in ['_settings', '_ldap']} @property def settings(self): if self._settings is None: self._settings = LDAPSettings(self.settings_prefix, self.default_settings) return self._settings @settings.setter def settings(self, settings): self._settings = settings @property def ldap(self): if self._ldap is None: options = getattr(django.conf.settings, 'AUTH_LDAP_GLOBAL_OPTIONS', None) self._ldap = _LDAPConfig.get_ldap(options) return self._ldap def get_user_model(self): """ By default, this will return the model class configured by AUTH_USER_MODEL. Subclasses may wish to override it and return a proxy model. """ return get_user_model() # # The Django auth backend API # def authenticate(self, request=None, username=None, password=None, **kwargs): if bool(password) or self.settings.PERMIT_EMPTY_PASSWORD: ldap_user = _LDAPUser(self, username=username.strip()) user = self.authenticate_ldap_user(ldap_user, password) else: logger.debug('Rejecting empty password for {}'.format(username)) user = None return user def get_user(self, user_id): user = None try: user = self.get_user_model().objects.get(pk=user_id) _LDAPUser(self, user=user) # This sets user.ldap_user except ObjectDoesNotExist: pass return user def has_perm(self, user, perm, obj=None): return perm in self.get_all_permissions(user, obj) def has_module_perms(self, user, app_label): for perm in self.get_all_permissions(user): if perm[:perm.index('.')] == app_label: return True return False def get_all_permissions(self, user, obj=None): return self.get_group_permissions(user, obj) def get_group_permissions(self, user, obj=None): if not hasattr(user, 'ldap_user') and self.settings.AUTHORIZE_ALL_USERS: _LDAPUser(self, user=user) # This sets user.ldap_user if hasattr(user, 'ldap_user'): permissions = user.ldap_user.get_group_permissions() else: permissions = set() return permissions # # Bonus API: populate the Django user from LDAP without authenticating. # def populate_user(self, username): ldap_user = _LDAPUser(self, username=username) user = ldap_user.populate_user() return user # # Hooks for subclasses # def authenticate_ldap_user(self, ldap_user, password): """ Returns an authenticated Django user or None. """ return ldap_user.authenticate(password) def get_or_build_user(self, username, ldap_user): """ This must return a (User, built) 2-tuple for the given LDAP user. username is the Django-friendly username of the user. ldap_user.dn is the user's DN and ldap_user.attrs contains all of their LDAP attributes. The returned User object may be an unsaved model instance. """ if self.__class__.get_or_create_user != LDAPBackend.get_or_create_user: # Older deprecated method overridden, defer to it instead. warnings.warn( "Method LDAPBacked.get_or_create_user() is deprecated and will " "be removed in a future version. Override " "LDAPBacked.get_or_build_user() instead. " "LDAPBacked.get_or_build_user() does not need to save newly " "built users.", DeprecationWarning, ) return self.get_or_create_user(username, ldap_user) model = self.get_user_model() if self.settings.USER_QUERY_FIELD: query_field = self.settings.USER_QUERY_FIELD query_value = ldap_user.attrs[self.settings.USER_ATTR_MAP[query_field]][0] lookup = query_field else: query_field = model.USERNAME_FIELD query_value = username.lower() lookup = '{}__iexact'.format(query_field) try: user = model.objects.get(**{lookup: query_value}) except model.DoesNotExist: user = model(**{query_field: query_value}) built = True else: built = False return (user, built) def get_or_create_user(self, username, ldap_user): """ This must return a (User, created) 2-tuple for the given LDAP user. username is the Django-friendly username of the user. ldap_user.dn is the user's DN and ldap_user.attrs contains all of their LDAP attributes. Deprecated, but remains for backwards compatibility. Remove in a future release. """ model = self.get_user_model() username_field = model.USERNAME_FIELD kwargs = { username_field + '__iexact': username, 'defaults': {username_field: username.lower()} } return model.objects.get_or_create(**kwargs) def ldap_to_django_username(self, username): return username def django_to_ldap_username(self, username): return username class _LDAPUser(object): """ Represents an LDAP user and ultimately fields all requests that the backend receives. This class exists for two reasons. First, it's convenient to have a separate object for each request so that we can use object attributes without running into threading problems. Second, these objects get attached to the User objects, which allows us to cache expensive LDAP information, especially around groups and permissions. self.backend is a reference back to the LDAPBackend instance, which we need to access the ldap module and any hooks that a subclass has overridden. """ class AuthenticationFailed(Exception): pass # Defaults _user = None _user_dn = None _user_attrs = None _groups = None _group_permissions = None _connection = None _connection_bound = False # # Initialization # def __init__(self, backend, username=None, user=None): """ A new LDAPUser must be initialized with either a username or an authenticated User object. If a user is given, the username will be ignored. """ self.backend = backend self._username = username if user is not None: self._set_authenticated_user(user) if username is None and user is None: raise Exception("Internal error: _LDAPUser improperly initialized.") def __deepcopy__(self, memo): obj = object.__new__(self.__class__) obj.backend = self.backend obj._user = copy.deepcopy(self._user, memo) # This is all just cached immutable data. There's no point copying it. obj._username = self._username obj._user_dn = self._user_dn obj._user_attrs = self._user_attrs obj._groups = self._groups obj._group_permissions = self._group_permissions # The connection couldn't be copied even if we wanted to obj._connection = self._connection obj._connection_bound = self._connection_bound return obj def __getstate__(self): """ Most of our properties are cached from the LDAP server. We only want to pickle a few crucial things. """ return {k: v for k, v in self.__dict__.items() if k in ['backend', '_username', '_user']} def _set_authenticated_user(self, user): self._user = user self._username = self.backend.django_to_ldap_username(user.get_username()) user.ldap_user = self user.ldap_username = self._username @property def ldap(self): return self.backend.ldap @property def settings(self): return self.backend.settings # # Entry points # def authenticate(self, password): """ Authenticates against the LDAP directory and returns the corresponding User object if successful. Returns None on failure. """ user = None try: self._authenticate_user_dn(password) self._check_requirements() self._get_or_create_user() user = self._user except self.AuthenticationFailed as e: logger.debug("Authentication failed for {}: {}".format(self._username, e)) except ldap.LDAPError as e: results = ldap_error.send(self.backend.__class__, context='authenticate', user=self._user, exception=e) if len(results) == 0: logger.warning( "Caught LDAPError while authenticating {}: {}".format( self._username, pprint.pformat(e) ) ) except Exception as e: logger.warning( "{} while authenticating {}".format(e, self._username) ) raise return user def get_group_permissions(self): """ If allowed by the configuration, this returns the set of permissions defined by the user's LDAP group memberships. """ if self._group_permissions is None: self._group_permissions = set() if self.settings.FIND_GROUP_PERMS: try: if self.dn is not None: self._load_group_permissions() except ldap.LDAPError as e: results = ldap_error.send(self.backend.__class__, context='get_group_permissions', user=self._user, exception=e) if len(results) == 0: logger.warning( "Caught LDAPError loading group permissions: {}".format( pprint.pformat(e) ) ) return self._group_permissions def populate_user(self): """ Populates the Django user object using the default bind credentials. """ user = None try: # self.attrs will only be non-None if we were able to load this user # from the LDAP directory, so this filters out nonexistent users. if self.attrs is not None: self._get_or_create_user(force_populate=True) user = self._user except ldap.LDAPError as e: results = ldap_error.send(self.backend.__class__, context='populate_user', user=self._user, exception=e) if len(results) == 0: logger.warning( "Caught LDAPError while authenticating {}: {}".format( self._username, pprint.pformat(e) ) ) except Exception as e: logger.warning( "{} while authenticating {}".format(e, self._username) ) raise return user # # Public properties (callbacks). These are all lazy for performance reasons. # @property def dn(self): if self._user_dn is None: self._load_user_dn() return self._user_dn @property def attrs(self): if self._user_attrs is None: self._load_user_attrs() return self._user_attrs @property def group_dns(self): return self._get_groups().get_group_dns() @property def group_names(self): return self._get_groups().get_group_names() @property def connection(self): if not self._connection_bound: self._bind() return self._get_connection() # # Authentication # def _authenticate_user_dn(self, password): """ Binds to the LDAP server with the user's DN and password. Raises AuthenticationFailed on failure. """ if self.dn is None: raise self.AuthenticationFailed("failed to map the username to a DN.") try: sticky = self.settings.BIND_AS_AUTHENTICATING_USER self._bind_as(self.dn, password, sticky=sticky) except ldap.INVALID_CREDENTIALS: raise self.AuthenticationFailed("user DN/password rejected by LDAP server.") def _load_user_attrs(self): if self.dn is not None: search = LDAPSearch(self.dn, ldap.SCOPE_BASE, attrlist=self.settings.USER_ATTRLIST) results = search.execute(self.connection) if results is not None and len(results) > 0: self._user_attrs = results[0][1] def _load_user_dn(self): """ Populates self._user_dn with the distinguished name of our user. This will either construct the DN from a template in AUTH_LDAP_USER_DN_TEMPLATE or connect to the server and search for it. If we have to search, we'll cache the DN. """ if self._using_simple_bind_mode(): self._user_dn = self._construct_simple_user_dn() else: cache_key = valid_cache_key('django_auth_ldap.user_dn.{}'.format(self._username)) self._user_dn = cache.get_or_set( cache_key, self._search_for_user_dn, self.settings.GROUP_CACHE_TIMEOUT ) def _using_simple_bind_mode(self): return (self.settings.USER_DN_TEMPLATE is not None) def _construct_simple_user_dn(self): template = self.settings.USER_DN_TEMPLATE username = ldap.dn.escape_dn_chars(self._username) user_dn = template % {'user': username} return user_dn def _search_for_user_dn(self): """ Searches the directory for a user matching AUTH_LDAP_USER_SEARCH. Populates self._user_dn and self._user_attrs. """ search = self.settings.USER_SEARCH if search is None: raise ImproperlyConfigured('AUTH_LDAP_USER_SEARCH must be an LDAPSearch instance.') results = search.execute(self.connection, {'user': self._username}) if (results is not None) and (len(results) == 1): (user_dn, self._user_attrs) = next(iter(results)) else: user_dn = None return user_dn def _check_requirements(self): """ Checks all authentication requirements beyond credentials. Raises AuthenticationFailed on failure. """ self._check_required_group() self._check_denied_group() def _check_required_group(self): """ Returns True if the group requirement (AUTH_LDAP_REQUIRE_GROUP) is met. Always returns True if AUTH_LDAP_REQUIRE_GROUP is None. """ required_group_dn = self.settings.REQUIRE_GROUP if required_group_dn is not None: if not isinstance(required_group_dn, LDAPGroupQuery): required_group_dn = LDAPGroupQuery(required_group_dn) result = required_group_dn.resolve(self) if not result: raise self.AuthenticationFailed("user does not satisfy AUTH_LDAP_REQUIRE_GROUP") return True def _check_denied_group(self): """ Returns True if the negative group requirement (AUTH_LDAP_DENY_GROUP) is met. Always returns True if AUTH_LDAP_DENY_GROUP is None. """ denied_group_dn = self.settings.DENY_GROUP if denied_group_dn is not None: is_member = self._get_groups().is_member_of(denied_group_dn) if is_member: raise self.AuthenticationFailed("user does not satisfy AUTH_LDAP_DENY_GROUP") return True # # User management # def _get_or_create_user(self, force_populate=False): """ Loads the User model object from the database or creates it if it doesn't exist. Also populates the fields, subject to AUTH_LDAP_ALWAYS_UPDATE_USER. """ save_user = False username = self.backend.ldap_to_django_username(self._username) self._user, built = self.backend.get_or_build_user(username, self) self._user.ldap_user = self self._user.ldap_username = self._username should_populate = force_populate or self.settings.ALWAYS_UPDATE_USER or built if built: logger.debug("Creating Django user {}".format(username)) self._user.set_unusable_password() save_user = True if should_populate: logger.debug("Populating Django user {}".format(username)) self._populate_user() save_user = True # Give the client a chance to finish populating the user just # before saving. populate_user.send(self.backend.__class__, user=self._user, ldap_user=self) if save_user: self._user.save() # This has to wait until we're sure the user has a pk. if bool(self.settings.MIRROR_GROUPS) or bool(self.settings.MIRROR_GROUPS_EXCEPT): self._normalize_mirror_settings() self._mirror_groups() def _populate_user(self): """ Populates our User object with information from the LDAP directory. """ self._populate_user_from_attributes() self._populate_user_from_group_memberships() def _populate_user_from_attributes(self): for field, attr in self.settings.USER_ATTR_MAP.items(): try: value = self.attrs[attr][0] except LookupError: logger.warning("{} does not have a value for the attribute {}".format(self.dn, attr)) else: setattr(self._user, field, value) def _populate_user_from_group_memberships(self): for field, group_dns in self.settings.USER_FLAGS_BY_GROUP.items(): try: query = self._normalize_group_dns(group_dns) except ValueError as e: raise ImproperlyConfigured("{}: {}", self.settings._name('USER_FLAGS_BY_GROUP'), e) value = query.resolve(self) setattr(self._user, field, value) def _normalize_group_dns(self, group_dns): """ Converts one or more group DNs to an LDAPGroupQuery. group_dns may be a string, a non-empty list or tuple of strings, or an LDAPGroupQuery. The result will be an LDAPGroupQuery. A list or tuple will be joined with the | operator. """ if isinstance(group_dns, LDAPGroupQuery): query = group_dns elif isinstance(group_dns, six.string_types): query = LDAPGroupQuery(group_dns) elif isinstance(group_dns, (list, tuple)) and len(group_dns) > 0: query = reduce(operator.or_, map(LDAPGroupQuery, group_dns)) else: raise ValueError(group_dns) return query def _normalize_mirror_settings(self): """ Validates the group mirroring settings and converts them as necessary. """ def malformed_mirror_groups_except(): return ImproperlyConfigured( "{} must be a collection of group names".format( self.settings._name('MIRROR_GROUPS_EXCEPT') ) ) def malformed_mirror_groups(): return ImproperlyConfigured( "{} must be True or a collection of group names".format( self.settings._name('MIRROR_GROUPS') ) ) mge = self.settings.MIRROR_GROUPS_EXCEPT mg = self.settings.MIRROR_GROUPS if mge is not None: if isinstance(mge, (set, frozenset)): pass elif isinstance(mge, (list, tuple)): mge = self.settings.MIRROR_GROUPS_EXCEPT = frozenset(mge) else: raise malformed_mirror_groups_except() if not all(isinstance(value, six.string_types) for value in mge): raise malformed_mirror_groups_except() elif bool(mg): warnings.warn(ConfigurationWarning("Ignoring {} in favor of {}".format( self.settings._name("MIRROR_GROUPS"), self.settings._name("MIRROR_GROUPS_EXCEPT") ))) mg = self.settings.MIRROR_GROUPS = None if mg is not None: if isinstance(mg, (bool, set, frozenset)): pass elif isinstance(mg, (list, tuple)): mg = self.settings.MIRROR_GROUPS = frozenset(mg) else: raise malformed_mirror_groups() if isinstance(mg, (set, frozenset)) and (not all(isinstance(value, six.string_types) for value in mg)): raise malformed_mirror_groups() def _mirror_groups(self): """ Mirrors the user's LDAP groups in the Django database and updates the user's membership. """ target_group_names = frozenset(self._get_groups().get_group_names()) current_group_names = frozenset(self._user.groups.values_list('name', flat=True).iterator()) # These were normalized to sets above. MIRROR_GROUPS_EXCEPT = self.settings.MIRROR_GROUPS_EXCEPT MIRROR_GROUPS = self.settings.MIRROR_GROUPS # If the settings are white- or black-listing groups, we'll update # target_group_names such that we won't modify the membership of groups # beyond our purview. if isinstance(MIRROR_GROUPS_EXCEPT, (set, frozenset)): target_group_names = ( (target_group_names - MIRROR_GROUPS_EXCEPT) | (current_group_names & MIRROR_GROUPS_EXCEPT) ) elif isinstance(MIRROR_GROUPS, (set, frozenset)): target_group_names = ( (target_group_names & MIRROR_GROUPS) | (current_group_names - MIRROR_GROUPS) ) if target_group_names != current_group_names: existing_groups = list(Group.objects.filter(name__in=target_group_names).iterator()) existing_group_names = frozenset(group.name for group in existing_groups) new_groups = [Group.objects.get_or_create(name=name)[0] for name in target_group_names if name not in existing_group_names] self._user.groups.set(existing_groups + new_groups) # # Group information # def _load_group_permissions(self): """ Populates self._group_permissions based on LDAP group membership and Django group permissions. """ group_names = self._get_groups().get_group_names() perms = Permission.objects.filter(group__name__in=group_names) perms = perms.values_list('content_type__app_label', 'codename') perms = perms.order_by() self._group_permissions = {"{}.{}".format(ct, name) for ct, name in perms} def _get_groups(self): """ Returns an _LDAPUserGroups object, which can determine group membership. """ if self._groups is None: self._groups = _LDAPUserGroups(self) return self._groups # # LDAP connection # def _bind(self): """ Binds to the LDAP server with AUTH_LDAP_BIND_DN and AUTH_LDAP_BIND_PASSWORD. """ self._bind_as(self.settings.BIND_DN, self.settings.BIND_PASSWORD, sticky=True) def _bind_as(self, bind_dn, bind_password, sticky=False): """ Binds to the LDAP server with the given credentials. This does not trap exceptions. If sticky is True, then we will consider the connection to be bound for the life of this object. If False, then the caller only wishes to test the credentials, after which the connection will be considered unbound. """ self._get_connection().simple_bind_s(force_str(bind_dn), force_str(bind_password)) self._connection_bound = sticky def _get_connection(self): """ Returns our cached LDAPObject, which may or may not be bound. """ if self._connection is None: uri = self.settings.SERVER_URI if callable(uri): uri = uri() self._connection = self.backend.ldap.initialize(uri) for opt, value in self.settings.CONNECTION_OPTIONS.items(): self._connection.set_option(opt, value) if self.settings.START_TLS: logger.debug("Initiating TLS") self._connection.start_tls_s() return self._connection class _LDAPUserGroups(object): """ Represents the set of groups that a user belongs to. """ def __init__(self, ldap_user): self.settings = ldap_user.settings self._ldap_user = ldap_user self._group_type = None self._group_search = None self._group_infos = None self._group_dns = None self._group_names = None self._init_group_settings() def _init_group_settings(self): """ Loads the settings we need to deal with groups. Raises ImproperlyConfigured if anything's not right. """ self._group_type = self.settings.GROUP_TYPE if self._group_type is None: raise ImproperlyConfigured("AUTH_LDAP_GROUP_TYPE must be an LDAPGroupType instance.") self._group_search = self.settings.GROUP_SEARCH if self._group_search is None: raise ImproperlyConfigured("AUTH_LDAP_GROUP_SEARCH must be an LDAPSearch instance.") def get_group_names(self): """ Returns the set of Django group names that this user belongs to by virtue of LDAP group memberships. """ if self._group_names is None: self._load_cached_attr("_group_names") if self._group_names is None: group_infos = self._get_group_infos() self._group_names = { self._group_type.group_name_from_info(group_info) for group_info in group_infos } self._cache_attr("_group_names") return self._group_names def is_member_of(self, group_dn): """ Returns true if our user is a member of the given group. """ is_member = None # Normalize the DN group_dn = group_dn.lower() # If we have self._group_dns, we'll use it. Otherwise, we'll try to # avoid the cost of loading it. if self._group_dns is None: is_member = self._group_type.is_member(self._ldap_user, group_dn) if is_member is None: is_member = (group_dn in self.get_group_dns()) logger.debug("{} is{}a member of {}".format( self._ldap_user.dn, is_member and " " or " not ", group_dn )) return is_member def get_group_dns(self): """ Returns a (cached) set of the distinguished names in self._group_infos. """ if self._group_dns is None: group_infos = self._get_group_infos() self._group_dns = {group_info[0] for group_info in group_infos} return self._group_dns def _get_group_infos(self): """ Returns a (cached) list of group_info structures for the groups that our user is a member of. """ if self._group_infos is None: self._group_infos = self._group_type.user_groups(self._ldap_user, self._group_search) return self._group_infos def _load_cached_attr(self, attr_name): if self.settings.CACHE_GROUPS: key = self._cache_key(attr_name) value = cache.get(key) setattr(self, attr_name, value) def _cache_attr(self, attr_name): if self.settings.CACHE_GROUPS: key = self._cache_key(attr_name) value = getattr(self, attr_name, None) cache.set(key, value, self.settings.GROUP_CACHE_TIMEOUT) def _cache_key(self, attr_name): """ Memcache keys can't have spaces in them, so we'll remove them from the DN for maximum compatibility. """ dn = self._ldap_user.dn key = valid_cache_key( 'auth_ldap.{}.{}.{}'.format(self.__class__.__name__, attr_name, dn) ) return key class LDAPSettings(object): """ This is a simple class to take the place of the global settings object. An instance will contain all of our settings as attributes, with default values if they are not specified by the configuration. """ _prefix = 'AUTH_LDAP_' defaults = { 'ALWAYS_UPDATE_USER': True, 'AUTHORIZE_ALL_USERS': False, 'BIND_AS_AUTHENTICATING_USER': False, 'BIND_DN': '', 'BIND_PASSWORD': '', 'CACHE_GROUPS': False, 'CONNECTION_OPTIONS': {}, 'DENY_GROUP': None, 'FIND_GROUP_PERMS': False, 'GROUP_CACHE_TIMEOUT': None, 'GROUP_SEARCH': None, 'GROUP_TYPE': None, 'MIRROR_GROUPS': None, 'MIRROR_GROUPS_EXCEPT': None, 'PERMIT_EMPTY_PASSWORD': False, 'REQUIRE_GROUP': None, 'SERVER_URI': 'ldap://localhost', 'START_TLS': False, 'USER_QUERY_FIELD': None, 'USER_ATTRLIST': None, 'USER_ATTR_MAP': {}, 'USER_DN_TEMPLATE': None, 'USER_FLAGS_BY_GROUP': {}, 'USER_SEARCH': None, } def __init__(self, prefix='AUTH_LDAP_', defaults={}): """ Loads our settings from django.conf.settings, applying defaults for any that are omitted. """ self._prefix = prefix defaults = dict(self.defaults, **defaults) for name, default in defaults.items(): value = getattr(django.conf.settings, prefix + name, default) setattr(self, name, value) def _name(self, suffix): return (self._prefix + suffix) def valid_cache_key(key): """ Sanitizes a cache key for memcached. """ key = re.sub(r'\s+', '+', key)[:250] return key django-auth-ldap-1.4.0/django_auth_ldap/config.py0000644000076600000240000006144113255034514022535 0ustar psagersstaff00000000000000# Copyright (c) 2009, Peter Sagerson # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # # - Redistributions of source code must retain the above copyright notice, this # list of conditions and the following disclaimer. # # - Redistributions in binary form must reproduce the above copyright notice, # this list of conditions and the following disclaimer in the documentation # and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. """ This module contains classes that will be needed for configuration of LDAP authentication. Unlike backend.py, this is safe to import into settings.py. Please see the docstring on the backend module for more information, including notes on naming conventions. """ from __future__ import absolute_import, division, print_function, unicode_literals import logging import pprint import ldap from django.utils.encoding import force_str from django.utils.tree import Node class ConfigurationWarning(UserWarning): pass class _LDAPConfig(object): """ A private class that loads and caches some global objects. """ ldap = None logger = None _ldap_configured = False @classmethod def get_ldap(cls, global_options=None): """ Returns the ldap module. The unit test harness will assign a mock object to _LDAPConfig.ldap. It is imperative that the ldap module not be imported anywhere else so that the unit tests will pass in the absence of python-ldap. """ if cls.ldap is None: import ldap.filter cls.ldap = ldap # Apply global LDAP options once if (not cls._ldap_configured) and (global_options is not None): for opt, value in global_options.items(): cls.ldap.set_option(opt, value) cls._ldap_configured = True return cls.ldap @classmethod def get_logger(cls): """ Initializes and returns our logger instance. """ if cls.logger is None: cls.logger = logging.getLogger('django_auth_ldap') cls.logger.addHandler(logging.NullHandler()) return cls.logger # Our global logger logger = _LDAPConfig.get_logger() class LDAPSearch(object): """ Public class that holds a set of LDAP search parameters. Objects of this class should be considered immutable. Only the initialization method is documented for configuration purposes. Internal clients may use the other methods to refine and execute the search. """ def __init__(self, base_dn, scope, filterstr='(objectClass=*)', attrlist=None): """ These parameters are the same as the first three parameters to ldap.search_s. """ self.base_dn = base_dn self.scope = scope self.filterstr = filterstr self.attrlist = attrlist self.ldap = _LDAPConfig.get_ldap() def __repr__(self): return "<{}: {}>".format(self.__class__.__name__, self.base_dn) def search_with_additional_terms(self, term_dict, escape=True): """ Returns a new search object with additional search terms and-ed to the filter string. term_dict maps attribute names to assertion values. If you don't want the values escaped, pass escape=False. """ term_strings = [self.filterstr] for name, value in term_dict.items(): if escape: value = self.ldap.filter.escape_filter_chars(value) term_strings.append('({}={})'.format(name, value)) filterstr = '(&{})'.format(''.join(term_strings)) return self.__class__(self.base_dn, self.scope, filterstr, attrlist=self.attrlist) def search_with_additional_term_string(self, filterstr): """ Returns a new search object with filterstr and-ed to the original filter string. The caller is responsible for passing in a properly escaped string. """ filterstr = '(&{}{})'.format(self.filterstr, filterstr) return self.__class__(self.base_dn, self.scope, filterstr, attrlist=self.attrlist) def execute(self, connection, filterargs=(), escape=True): """ Executes the search on the given connection (an LDAPObject). filterargs is an object that will be used for expansion of the filter string. If escape is True, values in filterargs will be escaped. The python-ldap library returns utf8-encoded strings. For the sake of sanity, this method will decode all result strings and return them as Unicode. """ if escape: filterargs = self._escape_filterargs(filterargs) try: filterstr = self.filterstr % filterargs results = connection.search_s(force_str(self.base_dn), self.scope, force_str(filterstr), self.attrlist) except ldap.LDAPError as e: results = [] logger.error( "search_s('{}', {}, '{}') raised {}".format( self.base_dn, self.scope, filterstr, pprint.pformat(e) ) ) return self._process_results(results) def _begin(self, connection, filterargs=(), escape=True): """ Begins an asynchronous search and returns the message id to retrieve the results. filterargs is an object that will be used for expansion of the filter string. If escape is True, values in filterargs will be escaped. """ if escape: filterargs = self._escape_filterargs(filterargs) try: filterstr = self.filterstr % filterargs msgid = connection.search( force_str(self.base_dn), self.scope, force_str(filterstr), self.attrlist ) except ldap.LDAPError as e: msgid = None logger.error( "search('{}', {}, '{}') raised {}".format( self.base_dn, self.scope, filterstr, pprint.pformat(e) ) ) return msgid def _results(self, connection, msgid): """ Returns the result of a previous asynchronous query. """ try: kind, results = connection.result(msgid) if kind not in (ldap.RES_SEARCH_ENTRY, ldap.RES_SEARCH_RESULT): results = [] except ldap.LDAPError as e: results = [] logger.error("result({}) raised {}".format(msgid, pprint.pformat(e))) return self._process_results(results) def _escape_filterargs(self, filterargs): """ Escapes values in filterargs. filterargs is a value suitable for Django's string formatting operator (%), which means it's either a tuple or a dict. This return a new tuple or dict with all values escaped for use in filter strings. """ if isinstance(filterargs, tuple): filterargs = tuple(self.ldap.filter.escape_filter_chars(value) for value in filterargs) elif isinstance(filterargs, dict): filterargs = {key: self.ldap.filter.escape_filter_chars(value) for key, value in filterargs.items()} else: raise TypeError("filterargs must be a tuple or dict.") return filterargs def _process_results(self, results): """ Returns a sanitized copy of raw LDAP results. This scrubs out references, decodes utf8, normalizes DNs, etc. """ results = [r for r in results if r[0] is not None] results = _DeepStringCoder('utf-8').decode(results) # The normal form of a DN is lower case. results = [(r[0].lower(), r[1]) for r in results] result_dns = [result[0] for result in results] logger.debug( "search_s('{}', {}, '{}') returned {} objects: {}".format( self.base_dn, self.scope, self.filterstr, len(result_dns), "; ".join(result_dns) ) ) return results class LDAPSearchUnion(object): """ A compound search object that returns the union of the results. Instantiate it with one or more LDAPSearch objects. """ def __init__(self, *args): self.searches = args self.ldap = _LDAPConfig.get_ldap() def search_with_additional_terms(self, term_dict, escape=True): searches = [s.search_with_additional_terms(term_dict, escape) for s in self.searches] return self.__class__(*searches) def search_with_additional_term_string(self, filterstr): searches = [s.search_with_additional_term_string(filterstr) for s in self.searches] return self.__class__(*searches) def execute(self, connection, filterargs=()): msgids = [search._begin(connection, filterargs) for search in self.searches] results = {} for search, msgid in zip(self.searches, msgids): if msgid is not None: result = search._results(connection, msgid) results.update(dict(result)) return results.items() class _DeepStringCoder(object): """ Encodes and decodes strings in a nested structure of lists, tuples, and dicts. This is helpful when interacting with the Unicode-unaware python-ldap. """ def __init__(self, encoding): self.encoding = encoding self.ldap = _LDAPConfig.get_ldap() def decode(self, value): try: if isinstance(value, bytes): value = value.decode(self.encoding) elif isinstance(value, list): value = self._decode_list(value) elif isinstance(value, tuple): value = tuple(self._decode_list(value)) elif isinstance(value, dict): value = self._decode_dict(value) except UnicodeDecodeError: pass return value def _decode_list(self, value): return [self.decode(v) for v in value] def _decode_dict(self, value): # Attribute dictionaries should be case-insensitive. python-ldap # defines this, although for some reason, it doesn't appear to use it # for search results. decoded = self.ldap.cidict.cidict() for k, v in value.items(): decoded[self.decode(k)] = self.decode(v) return decoded class LDAPGroupType(object): """ This is an abstract base class for classes that determine LDAP group membership. A group can mean many different things in LDAP, so we will need a concrete subclass for each grouping mechanism. Clients may subclass this if they have a group mechanism that is not handled by a built-in implementation. name_attr is the name of the LDAP attribute from which we will take the Django group name. Subclasses in this file must use self.ldap to access the python-ldap module. This will be a mock object during unit tests. """ def __init__(self, name_attr="cn"): self.name_attr = name_attr self.ldap = _LDAPConfig.get_ldap() def user_groups(self, ldap_user, group_search): """ Returns a list of group_info structures, each one a group to which ldap_user belongs. group_search is an LDAPSearch object that returns all of the groups that the user might belong to. Typical implementations will apply additional filters to group_search and return the results of the search. ldap_user represents the user and has the following three properties: dn: the distinguished name attrs: a dictionary of LDAP attributes (with lists of values) connection: an LDAPObject that has been bound with credentials This is the primitive method in the API and must be implemented. """ return [] def is_member(self, ldap_user, group_dn): """ This method is an optimization for determining group membership without loading all of the user's groups. Subclasses that are able to do this may return True or False. ldap_user is as above. group_dn is the distinguished name of the group in question. The base implementation returns None, which means we don't have enough information. The caller will have to call user_groups() instead and look for group_dn in the results. """ return None def group_name_from_info(self, group_info): """ Given the (DN, attrs) 2-tuple of an LDAP group, this returns the name of the Django group. This may return None to indicate that a particular LDAP group has no corresponding Django group. The base implementation returns the value of the cn attribute, or whichever attribute was given to __init__ in the name_attr parameter. """ try: name = group_info[1][self.name_attr][0] except (KeyError, IndexError): name = None return name class PosixGroupType(LDAPGroupType): """ An LDAPGroupType subclass that handles groups of class posixGroup. """ def user_groups(self, ldap_user, group_search): """ Searches for any group that is either the user's primary or contains the user as a member. """ groups = [] try: user_uid = ldap_user.attrs['uid'][0] if 'gidNumber' in ldap_user.attrs: user_gid = ldap_user.attrs['gidNumber'][0] filterstr = '(|(gidNumber={})(memberUid={}))'.format( self.ldap.filter.escape_filter_chars(user_gid), self.ldap.filter.escape_filter_chars(user_uid) ) else: filterstr = '(memberUid={})'.format( self.ldap.filter.escape_filter_chars(user_uid), ) search = group_search.search_with_additional_term_string(filterstr) groups = search.execute(ldap_user.connection) except (KeyError, IndexError): pass return groups def is_member(self, ldap_user, group_dn): """ Returns True if the group is the user's primary group or if the user is listed in the group's memberUid attribute. """ try: user_uid = ldap_user.attrs['uid'][0] try: is_member = ldap_user.connection.compare_s(force_str(group_dn), 'memberUid', force_str(user_uid)) except (ldap.UNDEFINED_TYPE, ldap.NO_SUCH_ATTRIBUTE): is_member = False if not is_member: try: user_gid = ldap_user.attrs['gidNumber'][0] is_member = ldap_user.connection.compare_s(force_str(group_dn), 'gidNumber', force_str(user_gid)) except (ldap.UNDEFINED_TYPE, ldap.NO_SUCH_ATTRIBUTE): is_member = False except (KeyError, IndexError): is_member = False return is_member class MemberDNGroupType(LDAPGroupType): """ A group type that stores lists of members as distinguished names. """ def __init__(self, member_attr, name_attr='cn'): """ member_attr is the attribute on the group object that holds the list of member DNs. """ self.member_attr = member_attr super(MemberDNGroupType, self).__init__(name_attr) def __repr__(self): return "<{}: {}>".format(self.__class__.__name__, self.member_attr) def user_groups(self, ldap_user, group_search): search = group_search.search_with_additional_terms({self.member_attr: ldap_user.dn}) groups = search.execute(ldap_user.connection) return groups def is_member(self, ldap_user, group_dn): try: result = ldap_user.connection.compare_s( force_str(group_dn), force_str(self.member_attr), force_str(ldap_user.dn) ) except (ldap.UNDEFINED_TYPE, ldap.NO_SUCH_ATTRIBUTE): result = 0 return result class NISGroupType(LDAPGroupType): """ A group type that handles nisNetgroup. """ def user_groups(self, ldap_user, group_search): try: user_uid = ldap_user.attrs['uid'][0] filterstr = '(|(nisNetgroupTriple={})(nisNetgroupTriple={}))'.format( self.ldap.filter.escape_filter_chars('(,{},)'.format(user_uid)), self.ldap.filter.escape_filter_chars('(-,{},-)'.format(user_uid)) ) search = group_search.search_with_additional_term_string(filterstr) groups = search.execute(ldap_user.connection) except (KeyError, IndexError): pass return groups def is_member(self, ldap_user, group_dn): try: user_uid = ldap_user.attrs['uid'][0] result = ldap_user.connection.compare_s( force_str(group_dn), force_str('nisNetgroupTriple'), force_str('(,{},)'.format(user_uid)) ) if result == 0: result = ldap_user.connection.compare_s( force_str(group_dn), force_str('nisNetgroupTriple'), force_str('(-,{},-)'.format(user_uid)) ) except (ldap.UNDEFINED_TYPE, ldap.NO_SUCH_ATTRIBUTE, KeyError, IndexError): result = 0 return result class NestedMemberDNGroupType(LDAPGroupType): """ A group type that stores lists of members as distinguished names and supports nested groups. There is no shortcut for is_member in this case, so it's left unimplemented. """ def __init__(self, member_attr, name_attr='cn'): """ member_attr is the attribute on the group object that holds the list of member DNs. """ self.member_attr = member_attr super(NestedMemberDNGroupType, self).__init__(name_attr) def user_groups(self, ldap_user, group_search): """ This searches for all of a user's groups from the bottom up. In other words, it returns the groups that the user belongs to, the groups that those groups belong to, etc. Circular references will be detected and pruned. """ group_info_map = {} # Maps group_dn to group_info of groups we've found member_dn_set = {ldap_user.dn} # Member DNs to search with next handled_dn_set = set() # Member DNs that we've already searched with while len(member_dn_set) > 0: group_infos = self.find_groups_with_any_member(member_dn_set, group_search, ldap_user.connection) new_group_info_map = {info[0]: info for info in group_infos} group_info_map.update(new_group_info_map) handled_dn_set.update(member_dn_set) # Get ready for the next iteration. To avoid cycles, we make sure # never to search with the same member DN twice. member_dn_set = set(new_group_info_map.keys()) - handled_dn_set return group_info_map.values() def find_groups_with_any_member(self, member_dn_set, group_search, connection): terms = [ "({0}={1})".format(self.member_attr, self.ldap.filter.escape_filter_chars(dn)) for dn in member_dn_set ] filterstr = "(|{0})".format("".join(terms)) search = group_search.search_with_additional_term_string(filterstr) return search.execute(connection) class GroupOfNamesType(MemberDNGroupType): """ An LDAPGroupType subclass that handles groups of class groupOfNames. """ def __init__(self, name_attr='cn'): super(GroupOfNamesType, self).__init__('member', name_attr) class NestedGroupOfNamesType(NestedMemberDNGroupType): """ An LDAPGroupType subclass that handles groups of class groupOfNames with nested group references. """ def __init__(self, name_attr='cn'): super(NestedGroupOfNamesType, self).__init__('member', name_attr) class GroupOfUniqueNamesType(MemberDNGroupType): """ An LDAPGroupType subclass that handles groups of class groupOfUniqueNames. """ def __init__(self, name_attr='cn'): super(GroupOfUniqueNamesType, self).__init__('uniqueMember', name_attr) class NestedGroupOfUniqueNamesType(NestedMemberDNGroupType): """ An LDAPGroupType subclass that handles groups of class groupOfUniqueNames with nested group references. """ def __init__(self, name_attr='cn'): super(NestedGroupOfUniqueNamesType, self).__init__('uniqueMember', name_attr) class ActiveDirectoryGroupType(MemberDNGroupType): """ An LDAPGroupType subclass that handles Active Directory groups. """ def __init__(self, name_attr='cn'): super(ActiveDirectoryGroupType, self).__init__('member', name_attr) class NestedActiveDirectoryGroupType(NestedMemberDNGroupType): """ An LDAPGroupType subclass that handles Active Directory groups with nested group references. """ def __init__(self, name_attr='cn'): super(NestedActiveDirectoryGroupType, self).__init__('member', name_attr) class OrganizationalRoleGroupType(MemberDNGroupType): """ An LDAPGroupType subclass that handles groups of class organizationalRole. """ def __init__(self, name_attr='cn'): super(OrganizationalRoleGroupType, self).__init__('roleOccupant', name_attr) class NestedOrganizationalRoleGroupType(NestedMemberDNGroupType): """ An LDAPGroupType subclass that handles groups of class OrganizationalRoleGroupType with nested group references. """ def __init__(self, name_attr='cn'): super(NestedOrganizationalRoleGroupType, self).__init__('roleOccupant', name_attr) class LDAPGroupQuery(Node): """ Represents a compound query for group membership. This can be used to construct an arbitrarily complex group membership query with AND, OR, and NOT logical operators. Construct primitive queries with a group DN as the only argument. These queries can then be combined with the ``&``, ``|``, and ``~`` operators. :param str group_dn: The DN of a group to test for membership. """ # Connection types AND = 'AND' OR = 'OR' default = AND _CONNECTORS = [AND, OR] def __init__(self, *args, **kwargs): super(LDAPGroupQuery, self).__init__(children=list(args) + list(kwargs.items())) def __and__(self, other): return self._combine(other, self.AND) def __or__(self, other): return self._combine(other, self.OR) def __invert__(self): obj = type(self)() obj.add(self, self.AND) obj.negate() return obj def _combine(self, other, conn): if not isinstance(other, LDAPGroupQuery): raise TypeError(other) if conn not in self._CONNECTORS: raise ValueError(conn) obj = type(self)() obj.connector = conn obj.add(self, conn) obj.add(other, conn) return obj def resolve(self, ldap_user, groups=None): if groups is None: groups = ldap_user._get_groups() result = self.aggregator(self._resolve_children(ldap_user, groups)) if self.negated: result = not result return result @property def aggregator(self): """ Returns a function for aggregating a sequence of sub-results. """ if self.connector == self.AND: aggregator = all elif self.connector == self.OR: aggregator = any else: raise ValueError(self.connector) return aggregator def _resolve_children(self, ldap_user, groups): """ Generates the query result for each child. """ for child in self.children: if isinstance(child, LDAPGroupQuery): yield child.resolve(ldap_user, groups) else: yield groups.is_member_of(child) django-auth-ldap-1.4.0/django_auth_ldap/__init__.py0000644000076600000240000000030113255035541023014 0ustar psagersstaff00000000000000from __future__ import absolute_import, division, print_function, unicode_literals version = (1, 4, 0) version_qualifier = '' version_string = '.'.join(map(str, version)) + version_qualifier django-auth-ldap-1.4.0/LICENSE0000644000076600000240000000242113151036722016427 0ustar psagersstaff00000000000000Copyright (c) 2009, Peter Sagerson All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. django-auth-ldap-1.4.0/CHANGES0000644000076600000240000001516413255035541016430 0ustar psagersstaff000000000000001.4.0 - 2018-03-22 - Cleanup and fixes -------------------------------------- - Fix `#61`_: Honor the attrlist argument to :setting:`AUTH_LDAP_GROUP_SEARCH` - **Backwards incompatible**: Removed support for Django < 1.11. - Support for Python 2.7 and 3.4+ now handled by the same dependency, `python-ldap >= 3.0 `_. .. _#61: https://bitbucket.org/illocution/django-auth-ldap/issues/58/ 1.3.0 - 2017-11-20 - Various improvements and cleanup ----------------------------------------------------- This release driven primarily by https://bitbucket.org/jdufresne/. - **Backwards incompatible**: Removed support for obsolete versions of Django (<=1.7, plus 1.9). - Fix `#58`_: Delay saving new users as long as possible. This will allow :setting:`AUTH_LDAP_USER_ATTR_MAP` to populate required fields before creating a new Django user. ``LDAPBackend.get_or_create_user()`` is now :meth:`~django_auth_ldap.backend.LDAPBackend.get_or_build_user` to avoid confusion. The old name may still be overridden for now. - Support querying by a field other than the username field with :setting:`AUTH_LDAP_USER_QUERY_FIELD`. - New method :meth:`~django_auth_ldap.backend.LDAPBackend.authenticate_ldap_user()` to provide pre- and post-authenication hooks. - Add support for Django 2.0. .. _#58: https://bitbucket.org/illocution/django-auth-ldap/issues/58/ 1.2.16 - 2017-09-30 - Minor fixes --------------------------------- - Fix `#86`_: Better cache key sanitizing. - Improved handling of LDAPError. A case existed where the error would not get caught while loading group permissions. .. _#86: https://bitbucket.org/illocution/django-auth-ldap/issues/86/ 1.2.15 - 2017-08-17 - Documentation improvements ------------------------------------------------ - Fix `#83`_: Improved documentation for finding the official repository and contributing. .. _#83: https://bitbucket.org/illocution/django-auth-ldap/issues/83/ 1.2.14 - 2017-07-24 - User DN caching ------------------------------------- - Fix `#82`_: Under search/bind mode, the user's DN will now be cached for performance. .. _#82: https://bitbucket.org/illocution/django-auth-ldap/issues/82/ 1.2.13 - 2017-06-19 - Selective group mirroring ----------------------------------------------- - Support selective group mirroring with :setting:`AUTH_LDAP_MIRROR_GROUPS` and :setting:`AUTH_LDAP_MIRROR_GROUPS_EXCEPT`. - Fix `#73`_: Work around Django 1.11 bug with multiple authentication backends. .. _#73: https://bitbucket.org/illocution/django-auth-ldap/issues/73/ 1.2.12 - 2017-05-20 - Complex group queries ------------------------------------------- - Support for complex group queries via :class:`~django_auth_ldap.config.LDAPGroupQuery`. 1.2.11 - 2017-04-22 - Testing and debugging cleanup --------------------------------------------------- - Some more descriptive object representations. - Improved tox.ini organization. 1.2.9 - 2017-02-14 - Fix python-ldap incompatibility ---------------------------------------------------- - Fix `#65`_: Ignore python-ldap documentation and accept :data:`ldap.RES_SEARCH_ENTRY` from :meth:`ldap.LDAPObject.result`. .. _#65: https://bitbucket.org/illocution/django-auth-ldap/issues/65/ 1.2.8 - 2016-04-18 - AUTH_LDAP_USER_ATTRLIST -------------------------------------------- - Add :setting:`AUTH_LDAP_USER_ATTRLIST` to override the set of attributes requested from the LDAP server. 1.2.7 - 2015-09-29 - Python 3 ----------------------------- - Support Python 3 with `pyldap `_. 1.2.6 - 2015-03-29 ------------------ - Performance improvements to group mirroring (from `Denver Janke `_). - Add :data:`django_auth_ldap.backend.ldap_error` signal for custom handling of :exc:`~ldap.LDAPError` exceptions. - Add :data:`django_auth_ldap.backend.LDAPBackend.default_settings` for per-subclass default settings. 1.2.5 - 2015-01-30 ------------------ - Fix interaction between :setting:`AUTH_LDAP_AUTHORIZE_ALL_USERS` and :setting:`AUTH_LDAP_USER_SEARCH`. 1.2.4 - 2014-12-28 - nisNetgroup support ---------------------------------------- - Add support for nisNetgroup groups (thanks to Christopher Bartz). 1.2.3 - 2014-11-18 ------------------ - Fix `#50`_: Improved escaping for filter strings. - Accept (and ignore) arbitrary keyword arguments to :meth:`~django_auth_ldap.backend.LDAPBackend.authenticate`. .. _#50: https://bitbucket.org/illocution/django-auth-ldap/issue/50/ 1.2.2 - 2014-09-22 ------------------ - Include test harness in source distribution. Some package maintainers find this helpful. 1.2.1 - 2014-08-24 ------------------ - More verbose log messages for authentication failures. 1.2 - 2014-04-10 ---------------- - django-auth-ldap now provides experimental Python 3 support. Python 2.5 was dropped. To sum up, django-auth-ldap works with Python 2.6, 2.7, 3.3 and 3.4. Since python-ldap isn't making progress toward Python 3, if you're using Python 3, you need to install a fork:: $ pip install git+https://github.com/rbarrois/python-ldap.git@py3 Thanks to `Aymeric Augustin `_ for making this happen. 1.1.8 - 2014-02-01 ------------------ * Fix `#43`_: Update :class:`~django_auth_ldap.config.LDAPSearchUnion` to work for group searches in addition to user searches. * Tox no longer supports Python 2.5, so our tests now run on 2.6 and 2.7 only. .. _#43: https://bitbucket.org/illocution/django-auth-ldap/issue/43/ 1.1.7 - 2013-11-19 ------------------ * Bug fix: :setting:`AUTH_LDAP_GLOBAL_OPTIONS` could be ignored in some cases (such as :func:`~django_auth_ldap.backend.LDAPBackend.populate_user`). 1.1.5 - 2013-10-25 ------------------ * Fix `#41`_: Support POSIX group permissions with no gidNumber attribute. * Support multiple group DNs for \*_FLAGS_BY_GROUP. .. _#41: https://bitbucket.org/illocution/django-auth-ldap/issue/41/ 1.1.4 - 2013-03-09 ------------------ * Add support for Django 1.5's custom user models. 1.1.3 - 2013-01-05 ------------------ * Fix `#33`_: Reject empty passwords by default. Unless :setting:`AUTH_LDAP_PERMIT_EMPTY_PASSWORD` is set to True, LDAPBackend.authenticate() will immediately return None if the password is empty. This is technically backwards-incompatible, but it's a more secure default for those LDAP servers that are configured such that binds without passwords always succeed. * Fix `#39`_: Add support for pickling LDAP-authenticated users. .. _#33: https://bitbucket.org/illocution/django-auth-ldap/issue/33/ .. _#39: https://bitbucket.org/illocution/django-auth-ldap/issue/39/ .. vim: ft=rst nospell tw=80 django-auth-ldap-1.4.0/tests/0000755000076600000240000000000013255035702016567 5ustar psagersstaff00000000000000django-auth-ldap-1.4.0/tests/settings.pyc0000644000076600000240000000200613255034615021144 0ustar psagersstaff00000000000000ó ČĂËYc@`s‚ddlmZmZmZmZiidd6d6ZgZdZdZe Z e Z e Z dZdZgZd Zd dgZdS(i(tabsolute_importtdivisiontprint_functiontunicode_literalsudjango.db.backends.sqlite3uENGINEudefaultuUTCuen-usu2nt56v8)moa)37ta5z7dd=if-@y#k@l7+t8lct*c8m730lpd=soudjango.contrib.authudjango.contrib.contenttypesudjango.contrib.sessionsutestsu auth.Useru$django_auth_ldap.backend.LDAPBackendu)django.contrib.auth.backends.ModelBackendN(udjango.contrib.authudjango.contrib.contenttypesudjango.contrib.sessionsutests(t __future__RRRRt DATABASESt ALLOWED_HOSTSt TIME_ZONEt LANGUAGE_CODEtFalsetUSE_I18NtUSE_L10NtTruetUSE_TZt SECRET_KEYtINSTALLED_APPStMIDDLEWARE_CLASSEStAUTH_USER_MODELtAUTHENTICATION_BACKENDS(((stests/settings.pyts$"django-auth-ldap-1.4.0/tests/models.pyc0000644000076600000240000000252713255034615020577 0ustar psagersstaff00000000000000ó |`NZc@`s\ddlmZmZmZmZddlmZddlmZdefd„ƒYZ dS(i(tabsolute_importtdivisiontprint_functiontunicode_literals(tAbstractBaseUser(tmodelstTestUsercB`skeZejdddedeƒZejƒZdZd„Z d„Z d„Z d„Z e e e ƒZRS( t max_lengthi(tuniquetdb_indexu identifiercC`s|jS(N(t identifier(tself((stests/models.pyt get_full_name scC`s|jS(N(R (R ((stests/models.pytget_short_namescC`sdS(NuAlice((R ((stests/models.pytget_first_namescC`stdƒ‚dS(NuOops...(t Exception(R tvalue((stests/models.pytset_first_names(t__name__t __module__Rt CharFieldtTrueR t IntegerFieldt uid_numbertUSERNAME_FIELDR R RRtpropertyt first_name(((stests/models.pyRs     N( t __future__RRRRtdjango.contrib.auth.modelsRt django.dbRR(((stests/models.pyts"django-auth-ldap-1.4.0/tests/models.py0000644000076600000240000000123113223460174020421 0ustar psagersstaff00000000000000from __future__ import absolute_import, division, print_function, unicode_literals from django.contrib.auth.models import AbstractBaseUser from django.db import models class TestUser(AbstractBaseUser): identifier = models.CharField(max_length=40, unique=True, db_index=True) uid_number = models.IntegerField() USERNAME_FIELD = 'identifier' def get_full_name(self): return self.identifier def get_short_name(self): return self.identifier def get_first_name(self): return 'Alice' def set_first_name(self, value): raise Exception('Oops...') first_name = property(get_first_name, set_first_name) django-auth-ldap-1.4.0/tests/tests.pyc0000644000076600000240000016013313255034615020454 0ustar psagersstaff00000000000000ó RŃŻZc@`sŠddlmZmZmZmZddlmZddlZddlZddl Z ddl Z ddl Z ddl m Z mZmZddlmZddlmZddlmZddlmZdd lmZdd lmZdd lmZmZmZm Z m!Z!m"Z"m#Z#m$Z$d d l%m&Z&yddl'Z'Wne(k r]dZ'nXdej*fd„ƒYZ+e j,e'dkdƒdefd„ƒYƒZ-dS(i(tabsolute_importtdivisiontprint_functiontunicode_literals(tdeepcopyN(tGroupt PermissiontUser(tcache(tImproperlyConfigured(tTestCase(toverride_settings(t force_str(tbackend(tGroupOfNamesTypetLDAPGroupQueryt LDAPSearchtLDAPSearchUniontMemberDNGroupTypetNestedMemberDNGroupTypet NISGroupTypetPosixGroupTypei(tTestUsert TestSettingscB`seZdZd„ZRS(ub A replacement for backend.LDAPSettings that does not load settings from django.conf. cK`sFx?|jjƒD].\}}|j||ƒ}t|||ƒqWdS(N(tdefaultstitemstgettsetattr(tselftkwargstnametdefaulttvalue((stests/tests.pyt__init__@s(t__name__t __module__t__doc__R!(((stests/tests.pyR;su4django_auth_ldap tests require the mockldap package.tLDAPTestcB`s• eZdidd6fZdidd6fZdidd6fZdid d6fZd id d6fZd id gd6ddddgd6dgd6dgd6dgd6dgd6dgd6fZdidgd6ddddgd6dgd6dgd6d gd6d!gd6d"gd6fZe d#ƒie d$ƒgd6ddddgd6dgd6d%gd6d gd6d&gd6e d'ƒgd6fZ d(id)gd6ddddgd6dgd6d*gd+6fZ d,id-gd.6d/gd6dgd6gd06fZ d1id2gd.6d/gd6dgd6d gd06fZ d3id4gd.6d/gd6d%gd6d gd06fZd5id6gd.6d7gd6gd86fZd9id:gd.6d7gd6d gd86fZd;id<gd.6d7gd6d gd86fZd=id>gd.6d7gd6d gd86fZd?id@gd.6d7gd6dgd86fZdAidBgd.6d7gd6d gd86fZdCidDgd.6d7gd6d dgd86fZdEidFgd.6d7gd6dgd86fZdGidHgd.6d7gd6d gd86fZdIidJgd.6d7gd6gd86fZdKidLgd.6d7gd6d gd86fZdMidNgd.6d7gd6gd86fZdOidPgd.6dQgd6dRgdS6fZdTidUgd.6dQgd6dRgdS6fZdVidWgd.6dQgd6dRgdS6fZdXidYgd.6d7gd6dZgd86fZd[id\gd.6d7gd6d d]gd86fZd]id^gd.6d7gd6dXgd86fZ e!eeeeeeee e e e eeeeeeeeeeeeeeeeeee gƒZ"e#d_„ƒZ$e#d`„ƒZ%e#da„ƒZ&db„Z'dc„Z(dd„Z)de„Z*df„Z+dg„Z,e-dhe.de/j0diƒƒdj„ƒZ1e-dhe.de/j0diƒƒdk„ƒZ2dl„Z3dm„Z4dn„Z5e-dodpƒdq„ƒZ6e-dodpƒdr„ƒZ7e-dodpƒds„ƒZ8dt„Z9du„Z:dv„Z;dw„Z<dx„Z=dy„Z>dz„Z?d{„Z@d|„ZAd}„ZBd~„ZCd„ZDd€„ZEd„ZFd‚„ZGdƒ„ZHd„„ZIe-dodpƒd…„ƒZJe-dodpƒd†„ƒZKd‡„ZLdˆ„ZMd‰„ZNdŠ„ZOd‹„ZPdŒ„ZQd„ZRdŽ„ZSd„ZTd„ZUd‘„ZVd’„ZWd“„ZXd”„ZYd•„ZZd–„Z[d—„Z\d˜„Z]d™„Z^dš„Z_d›„Z`dœ„Zad„Zbdž„ZcdŸ„Zdd „ZedĄ„Zfd˘„ZgdŁ„Zhd¤„ZidĽ„ZjdŚ„Zkd§„Zld¨„ZmdŠ„ZndŞ„ZodŤ„ZpdŹ„Zqd­„ZrdŽ„ZsdŻ„Ztd°„Zudą„Zvd˛„Zwdł„Zxd´„Zydľ„Zzdś„Z{dˇ„Z|d¸„Z}dš„Z~dş„Zdť„Z€dź„Zd˝„Z‚dž„Zƒdż„Z„RS(Ŕuo=testutestuouou=people,o=testupeopleuouuou=groups,o=testugroupsuou=moregroups,o=testu moregroupsuou=mirror_groups,o=testu mirror_groupsuuid=alice,ou=people,o=testualiceuuidupersonuorganizationalPersonu inetOrgPersonu posixAccountu objectClassupasswordu userPasswordu1000u uidNumberu gidNumberuAliceu givenNameuAdamsusnuuid=bob,ou=people,o=testubobu1001u50uRobertuBarkeruuid=dreßler,ou=people,o=testudreßleru1002uWolfganguDreßleruuid=nobody,ou=people,o=testunobodyu²u binaryAttrucn=active_px,ou=groups,o=testu active_pxucnu posixGroupu memberUiducn=staff_px,ou=groups,o=testustaff_pxu cn=superuser_px,ou=groups,o=testu superuser_pxucn=empty_gon,ou=groups,o=testu empty_gonu groupOfNamesumemberucn=active_gon,ou=groups,o=testu active_gonucn=staff_gon,ou=groups,o=testu staff_gonu!cn=superuser_gon,ou=groups,o=testu superuser_gonu!cn=other_gon,ou=moregroups,o=testu other_gonu#cn=alice_gon,ou=query_groups,o=testu alice_gonu$cn=mutual_gon,ou=query_groups,o=testu mutual_gonu!cn=bob_gon,ou=query_groups,o=testubob_gonu"cn=mirror1,ou=mirror_groups,o=testumirror1u"cn=mirror2,ou=mirror_groups,o=testumirror2u"cn=mirror3,ou=mirror_groups,o=testumirror3u"cn=mirror4,ou=mirror_groups,o=testumirror4ucn=active_nis,ou=groups,o=testu active_nisu nisNetgroupu (,alice,)unisNetgroupTripleucn=staff_nis,ou=groups,o=testu staff_nisu!cn=superuser_nis,ou=groups,o=testu superuser_nisucn=parent_gon,ou=groups,o=testu parent_gonucn=nested_gon,ou=groups,o=testuCN=nested_gon,ou=groups,o=testu nested_gonu cn=circular_gon,ou=groups,o=testu circular_goncC`shtjdƒ}tjdƒ}tjƒ}|jtjƒ|j|ƒ|j|ƒ|jtjƒdS(Nudjango_auth_ldapu'LDAP auth - %(levelname)s - %(message)s( tloggingt getLoggert Formattert StreamHandlertsetLeveltDEBUGt setFormattert addHandlertCRITICAL(tclstloggert formatterthandler((stests/tests.pytconfigure_loggerós   cC`s<|jƒtj|jƒ|_tjddtddƒdS(Nuignoretcategorytmoduleumockldap.ldapobject(R3tmockldaptMockLdapt directorytwarningstfilterwarningstUnicodeWarning(R/((stests/tests.pyt setUpClass˙s cC`s |`dS(N(R6(R/((stests/tests.pyt tearDownClassscC`sDtjƒ|jjƒ|jd|_tjƒ|_|jjdS(Nuldap://localhost(RtclearR6tstarttldapobjR t LDAPBackendtldap(R((stests/tests.pytsetUp s   cC`s|jjƒ|`dS(N(R6tstopR@(R((stests/tests.pyttearDowns cC`sV|jdddidd6ƒ|jjdddd ƒ|j|jjdƒdƒdS( NtUSER_DN_TEMPLATEuuid=%(user)s,ou=people,o=testtCONNECTION_OPTIONSuvalue1uopt1tusernameualicetpasswordupassword(t_init_settingsR t authenticatet assertEqualR@t get_option(R((stests/tests.pyt test_optionss  cC`sw|jdd„ddƒ|jjddddƒ|jd }|j|jd tƒd difd difgƒdS(Nt SERVER_URIcS`sdS(Nuldap://ldap.example.com((((stests/tests.pyt(sRFuuid=%(user)s,ou=people,o=testRHualiceRIupassworduldap://ldap.example.comt with_argsu initializeu simple_bind_suuid=alice,ou=people,o=test(uldap://ldap.example.com(uuid=alice,ou=people,o=testupassword(RJR RKR6RLtmethods_calledtTrue(RR@((stests/tests.pyttest_callable_server_uri&s    cC`s |jddƒtjjƒ}|jjddddƒ}|j|jƒƒ|j|j dƒ|jtjjƒ|dƒ|j|j j ƒdd gƒdS( NRFuuid=%(user)s,ou=people,o=testRHualiceRIupasswordiu initializeu simple_bind_s( RJRtobjectstcountR RKt assertFalsethas_usable_passwordRLRHR@RR(Rt user_counttuser((stests/tests.pyttest_simple_bind5s  cC`sľdtjfd„ƒY}|ƒ|_tjjƒ}|jjddddƒ}|j|jƒƒ|j|j dƒ|jtjjƒ|dƒ|j|j j ƒdd gƒdS( Nt MyBackendcB`seZidd6ZRS(uuid=%(user)s,ou=people,o=testuUSER_DN_TEMPLATE(R"R#tdefault_settings(((stests/tests.pyR\FsRHualiceRIupasswordiu initializeu simple_bind_s( R RARRURVRKRWRXRLRHR@RR(RR\RYRZ((stests/tests.pyttest_default_settingsEs  tAUTH_LDAP_USER_SEARCHu(uid=%(user)s)cC`s,|jjddddƒ}|j|ƒdS(NRHualiceRIupassword(tclienttlogint assertTrue(Rtauth((stests/tests.pyt&test_login_with_multiple_auth_backendsXscC`s,|jjddddƒ}|j|ƒdS(NRHuinvalidRIui_do_not_exist(R`RaRW(RRc((stests/tests.pyt*test_bad_login_with_multiple_auth_backends]scC`ss|jddƒ|jjddddƒ}|j|ƒ|j|jjdtƒdd ifd difgƒd S(u. Bind with a username that requires escaping. RFuuid=%(user)s,ou=people,o=testRHualice,1RIupasswordRQu initializeuldap://localhostu simple_bind_suuid=alice\,1,ou=people,o=testN(uldap://localhost(uuid=alice\,1,ou=people,o=testupassword(RJR RKt assertIsNoneRLR@RRRS(RRZ((stests/tests.pyttest_simple_bind_escapedbs   cC`s |jddƒtjjƒ}|jjddddƒ}|j|jƒƒ|j|j dƒ|jtjjƒ|dƒ|j|j j ƒd d gƒdS( NRFuuid=%(user)s,ou=people,o=testRHuAliceRIupasswordualiceiu initializeu simple_bind_s( RJRRURVR RKRWRXRLRHR@RR(RRYRZ((stests/tests.pyttest_new_user_lowercaseqs  cC`s;|jddƒ|jjddddƒ}t|ƒ}dS(NRFuuid=%(user)s,ou=people,o=testRHuAliceRIupassword(RJR RKR(RRZ((stests/tests.pyt test_deepcopys tAUTH_USER_MODELutests.TestUsercC`sL|jdddidd6ƒ|jjdddd ƒ}|j|tƒdS( NRFuuid=%(user)s,ou=people,o=testt USER_ATTR_MAPu uidNumberu uid_numberRHuAliceRIupassword(RJR RKtassertIsInstanceR(RRZ((stests/tests.pyttest_auth_custom_user‰s  cC`sa|jdddidd6ƒ|jjdddd ƒ}|jj|jƒ}|j|tƒdS( NRFuuid=%(user)s,ou=people,o=testRku uidNumberu uid_numberRHuAliceRIupassword(RJR RKtget_usertidRlR(RRZ((stests/tests.pyttest_get_custom_user”s  cC`sƒ|jdddidd6ddƒtjjddd d ƒ}|jjd d d dƒ}|j|tƒ|j|j|jƒdS(NRFuuid=%(user)s,ou=people,o=testRku uidNumberu uid_numbertUSER_QUERY_FIELDt identifieruabcdeft uid_numberičRHuAliceRIupassword( RJRRUtcreateR RKRlRLtpk(RtaliceRZ((stests/tests.pyttest_get_custom_field s  cC`sœ|jddƒtjjƒ}|jjddddƒ}|jjddddƒ}|j|jƒƒ|j|j dƒ|jtjjƒ|d ƒdS( NRFuuid=%(user)s,ou=people,o=testRHu aliceRIupasswordualice ualicei( RJRRURVR RKRWRXRLRH(RRYRZ((stests/tests.pyttest_new_user_whitespaceŽs cC`sƒ|jddƒtjjƒ}|jjddddƒ}|j|ƒ|jtjjƒ|ƒ|j|jj ƒddgƒdS( NRFuuid=%(user)s,ou=people,o=testRHu evil_aliceRIupasswordu initializeu simple_bind_s( RJRRURVR RKRfRLR@RR(RRYRZ((stests/tests.pyttest_simple_bind_bad_userťs   cC`sƒ|jddƒtjjƒ}|jjddddƒ}|j|ƒ|jtjjƒ|ƒ|j|jj ƒddgƒdS( NRFuuid=%(user)s,ou=people,o=testRHualiceRIubogusu initializeu simple_bind_s( RJRRURVR RKRfRLR@RR(RRYRZ((stests/tests.pyttest_simple_bind_bad_passwordĘs   cC`sw|jddƒtjjddƒtjjƒ}|jjddddƒ}|j|ƒ|jtjjƒ|ƒdS(NRFuuid=%(user)s,ou=people,o=testRHualiceRIupassword( RJRRURtRVR RKtassertIsNotNoneRL(RRYRZ((stests/tests.pyttest_existing_userŮs  cC`sľ|jdtdtjdƒƒ|jjjdtjddƒ|jgƒt j j ddƒ|j j dddd ƒ}|j|ƒ|j|jdƒ|jt j jƒd ƒdS( Nt USER_SEARCHuou=people,o=testu(uid=%(user)s)u (uid=Alice)RHualiceuAliceRIupasswordi(RJRRBt SCOPE_SUBTREER@tsearch_stseedtNoneRvRRURtR RKR{RLRHRV(RRZ((stests/tests.pyttest_existing_user_insensitivećs  cC`s dtjfd„ƒY}|ƒ|_|jddƒtjjƒ}|jjddddƒ}|jj|jƒ}|j tjjƒ|d ƒ|j |j d ƒ|j |j j dƒ|j |j dƒ|j |j d ƒ|j |j j dƒ|j |j dƒdS( NR\cB`seZd„Zd„ZRS(cS`sd|S(Nuldap_%s((RRH((stests/tests.pytldap_to_django_usernameůscS`s|dS(Ni((RRH((stests/tests.pytdjango_to_ldap_usernameüs(R"R#RƒR„(((stests/tests.pyR\řs RFuuid=%(user)s,ou=people,o=testRHualiceRIupasswordiu ldap_alice(R RARJRRURVRKRnRuRLRHt ldap_usert _usernamet ldap_username(RR\RYtuser1tuser2((stests/tests.pyttest_convert_username÷s  cC`sœ|jdtdtjdƒƒtjjƒ}|jjddddƒ}|j |ƒ|j tjjƒ|dƒ|j |j j ƒd d d d gƒdS( NR}uou=people,o=testu(uid=%(user)s)RHualiceRIupasswordiu initializeu simple_bind_susearch_s( RJRRBR~RRURVR RKR{RLR@RR(RRYRZ((stests/tests.pyttest_search_binds   c C`s|jdtdtjdƒƒ|jjddddƒ}|j|ƒ|j|jj dt ƒd difd difd dtjddfifgƒdS(u/ Search for a username that requires escaping. R}uou=people,o=testu(uid=%(user)s)RHualice*RIupasswordRQu initializeuldap://localhostu simple_bind_suusearch_su(uid=alice\2a)N(uldap://localhost(uu( RJRRBR~R RKRfRLR@RRRSR(RRZ((stests/tests.pyttest_search_bind_escaped!s    cC`sm|jdtdtjdƒƒ|jjddddƒ}|j|ƒ|j|jj ƒdd d gƒdS( NR}uou=people,o=testu (cn=%(user)s)RHualiceRIupasswordu initializeu simple_bind_susearch_s( RJRRBR~R RKRfRLR@RR(RRZ((stests/tests.pyttest_search_bind_no_user3s   cC`sm|jdtdtjdƒƒ|jjddddƒ}|j|ƒ|j|jj ƒdd d gƒdS( NR}uou=people,o=testu(uid=*)RHualiceRIupasswordu initializeu simple_bind_susearch_s( RJRRBR~R RKRfRLR@RR(RRZ((stests/tests.pyttest_search_bind_multiple_usersBs   cC`sp|jdtdtjdƒƒ|jjddddƒ}|j|ƒ|j|jj ƒdd d d gƒdS( NR}uou=people,o=testu(uid=%(user)s)RHualiceRIubogusu initializeu simple_bind_susearch_s( RJRRBR~R RKRfRLR@RR(RRZ((stests/tests.pyttest_search_bind_bad_passwordQs   c C`sŇ|jdddddtdtjdƒƒ|jjdd d dƒ}|j|ƒ|j|jƒ|j|jj |j d ƒ|j|jj tj j |j d ƒƒ|j|j jƒd dddgƒdS(NtBIND_DNuuid=bob,ou=people,o=testt BIND_PASSWORDupasswordR}uou=people,o=testu(uid=%(user)s)RHualiceRIiiu initializeu simple_bind_susearch_s(RJRRBR~R RKR{R…RLtdnRvtattrstcidictR@RR(RRZ((stests/tests.pyt!test_search_bind_with_credentials`s  ) c C`sv|jdddddtdtjdƒƒ|jjdd d d ƒ}|j|ƒ|j|jj ƒd d gƒdS(NRuuid=bob,ou=people,o=testR‘ubogusR}uou=people,o=testu(uid=%(user)s)RHualiceRIupasswordu initializeu simple_bind_s( RJRRBR~R RKRfRLR@RR(RRZ((stests/tests.pyt%test_search_bind_with_bad_credentialsts   cC`sv|jdddidd6dd6ƒ|jjdd d d ƒ}|j|ƒ|j|jd ƒ|j|jd ƒdS( NRFuuid=%(user)s,ou=people,o=testRku givenNameu first_nameusnu last_nameRHudreßlerRIupassworduDreßler(RJR RKR{RLRHt last_name(RRZ((stests/tests.pyttest_unicode_user…s  cC`sK|jddƒ|jjddddƒ}|j|jjtjjƒdS(NRFuuid=%(user)s,ou=people,o=testRHualiceRIupassword(RJR RKRlR…R“RBR”(RRZ((stests/tests.pyt test_cidicts cC`sĄ|jdddidd6dd6ƒ|jjdd d d ƒ}|j|jd ƒ|j|jd ƒ|j|jd ƒ|j|jjƒddddgƒdS(NRFuuid=%(user)s,ou=people,o=testRku givenNameu first_nameusnu last_nameRHualiceRIupassworduAliceuAdamsu initializeu simple_bind_susearch_s( RJR RKRLRHt first_nameR—R@RR(RRZ((stests/tests.pyttest_populate_user™s  cC`s–|jdddidd6dd6dd 6ƒ|jjd d d d ƒ}|j|jd ƒ|j|jdƒ|j|jdƒ|j|jdƒdS(NRFuuid=%(user)s,ou=people,o=testRku givenNameu first_nameusnu last_nameumailuemailRHualiceRIupassworduAliceuAdamsu(RJR RKRLRHRšR—temail(RRZ((stests/tests.pyt)test_populate_user_with_missing_attributeŤs  c C`sZ|jdddidd6dd6ƒ|jtdƒ|jjd d d d ƒWdQXdS( NRFuuid=%(user)s,ou=people,o=testRku givenNameu first_nameu uidNumberu uid_numberuOops...RHualiceRIupassword(RJtassertRaisesMessaget ExceptionR RK(R((stests/tests.pyt4test_authenticate_with_buggy_setter_raises_exceptionťs  cC`sQ|jdddidd6dd6ƒ|jtdƒ|jjd ƒWdQXdS( NRFuuid=%(user)s,ou=people,o=testRku givenNameu first_nameu uidNumberu uid_numberuOops...ualice(RJRžRŸR t populate_user(R((stests/tests.pyt5test_populate_user_with_buggy_setter_raises_exceptionČs  cC`sČ|jdddidd6dd6dd d gƒ|jjd d d dƒ}|j|jd ƒ|j|jjƒddddgƒ|j|jjdtƒdddtj dd d gfifƒdS(NRFuuid=%(user)s,ou=people,o=testRku givenNameu first_nameusnu last_namet USER_ATTRLISTu*u+RHualiceRIupasswordu initializeu simple_bind_susearch_sRQiuuid=alice,ou=people,o=testu(objectClass=*)( RJR RKRLRHR@RRRSRBt SCOPE_BASE(RRZ((stests/tests.pyttest_populate_with_attrlistŐs   cC`s¤|jdddidd6dd6dtƒ|jjd d d d ƒ}|j|jd ƒ|j|jd ƒ|j|jdƒ|j|jj ƒdddgƒdS(NRFuuid=%(user)s,ou=people,o=testRku givenNameu first_nameusnu last_nametBIND_AS_AUTHENTICATING_USERRHualiceRIupassworduAliceuAdamsu initializeu simple_bind_susearch_s( RJRSR RKRLRHRšR—R@RR(RRZ((stests/tests.pyttest_bind_as_useręs  c`snˆjddƒ‡fd†}tjj|ƒˆjjddddƒ}ˆj|jƒtjj|ƒdS(NRFuuid=%(user)s,ou=people,o=testc`s1ˆjd|ƒˆjd|ƒt|d_dS(Nuuseru ldap_user(tassertInRStpopulate_user_handled(tsenderR(R(stests/tests.pythandle_populate_usersRHualiceRIupassword(RJR RĄtconnectRKRbRŠt disconnect(RRŤRZ((Rstests/tests.pyttest_signal_populate_userýs c `sˆjdddddtdtjdƒƒ‡fd†}tjj|ƒˆjtjƒˆjj d d d d ƒWdQXtjj |ƒdS( NRuuid=bob,ou=people,o=testR‘ubogusR}uou=people,o=testu(uid=%(user)s)c`s"ˆj|ddƒ|d‚dS(Nucontextu authenticateu exception(RL(RŞR(R(stests/tests.pythandle_ldap_errorsRHualiceRIupassword( RJRRBR~R t ldap_errorRŹt assertRaisest LDAPErrorRKR­(RRŻ((Rstests/tests.pyttest_auth_signal_ldap_errors c C`sN|jdddddtdtjdƒƒ|jjdƒ}|j|ƒdS( NRuuid=bob,ou=people,o=testR‘ubogusR}uou=people,o=testu(uid=%(user)s)ualice(RJRRBR~R RĄRf(RRZ((stests/tests.pyttest_populate_signal_ldap_error s cC`sĎ|jdddidd6dd6dtƒtjjd d d d d dƒ|jjd d ddƒ}|jjd dddƒ}|j|jd ƒ|j|j dƒ|j|jdƒ|j|j dƒdS(NRFuuid=%(user)s,ou=people,o=testRku givenNameu first_nameusnu last_nametALWAYS_UPDATE_USERRHualiceRšuAliciaR—uAstroRIupasswordubobuRobertuBarker( RJtFalseRRURtR RKRLRšR—(RRvtbob((stests/tests.pyttest_no_update_existing-s c C`sż|jdddtdtjdƒdtddƒd d ƒ|jjd d d dƒ}|jjd dd dƒ}|j|ƒ|j|ƒ|j |j j ƒddddddddgƒdS(NRFuuid=%(user)s,ou=people,o=testt GROUP_SEARCHuou=groups,o=testu(objectClass=groupOfNames)t GROUP_TYPEt member_attrumembert REQUIRE_GROUPucn=active_gon,ou=groups,o=testRHualiceRIupasswordubobu initializeu simple_bind_su compare_s( RJRRBR~RR RKR{RfRLR@RR(RRvRˇ((stests/tests.pyttest_require_group=s     c C`sx|jdddtdtjdƒdtddƒƒ|jjd d d d ƒ}td ƒ}|j|j |j ƒƒdS(NRFuuid=%(user)s,ou=people,o=testRšuou=query_groups,o=testu(objectClass=groupOfNames)RşRťumemberRHualiceRIupasswordu#cn=alice_gon,ou=query_groups,o=test( RJRRBR~RR RKRRbtresolveR…(RRvtquery((stests/tests.pyttest_simple_group_queryPs  c C`sy|jdddtdtjdƒdtddƒƒ|jjd d d d ƒ}td ƒ}|j|j |j ƒƒdS(NRFuuid=%(user)s,ou=people,o=testRšuou=query_groups,o=testu(objectClass=groupOfNames)RşRťumemberRHualiceRIupasswordu#cn=alice_gon,ou=query_groups,o=test( RJRRBR~RR RKRRWRžR…(RRvRż((stests/tests.pyttest_negated_group_queryZs  c C`sś|jdddtdtjdƒdtddƒƒ|jjd d d d ƒ}|jjd d d d ƒ}tdƒtdƒB}|j|j |j ƒƒ|j|j |j ƒƒdS(NRFuuid=%(user)s,ou=people,o=testRšuou=query_groups,o=testu(objectClass=groupOfNames)RşRťumemberRHualiceRIupasswordubobu#cn=alice_gon,ou=query_groups,o=testu!cn=bob_gon,ou=query_groups,o=test( RJRRBR~RR RKRRbRžR…(RRvRˇRż((stests/tests.pyttest_or_group_queryds   c C`sś|jdddtdtjdƒdtddƒƒ|jjd d d d ƒ}|jjd d d d ƒ}tdƒtdƒ@}|j|j |j ƒƒ|j |j |j ƒƒdS(NRFuuid=%(user)s,ou=people,o=testRšuou=query_groups,o=testu(objectClass=groupOfNames)RşRťumemberRHualiceRIupasswordubobu#cn=alice_gon,ou=query_groups,o=testu$cn=mutual_gon,ou=query_groups,o=test( RJRRBR~RR RKRRbRžR…RW(RRvRˇRż((stests/tests.pyttest_and_group_queryts   c C`sŔ|jdddtdtjdƒdtddƒƒ|jjd d d d ƒ}|jjd d d d ƒ}tdƒtdƒ@tdƒB}|j|j |j ƒƒ|j|j |j ƒƒdS(NRFuuid=%(user)s,ou=people,o=testRšuou=query_groups,o=testu(objectClass=groupOfNames)RşRťumemberRHualiceRIupasswordubobu#cn=alice_gon,ou=query_groups,o=testu$cn=mutual_gon,ou=query_groups,o=testu!cn=bob_gon,ou=query_groups,o=test( RJRRBR~RR RKRRbRžR…(RRvRˇRż((stests/tests.pyttest_nested_group_query„s    c C`s¤tdƒtdƒ@}|jdddtdtjdƒdtd d ƒd |ƒ|jjd d ddƒ}|jjd dddƒ}|j|ƒ|j |ƒdS(Nu#cn=alice_gon,ou=query_groups,o=testu$cn=mutual_gon,ou=query_groups,o=testRFuuid=%(user)s,ou=people,o=testRšuou=query_groups,o=testu(objectClass=groupOfNames)RşRťumemberRźRHualiceRIupasswordubob( RRJRRBR~RR RKR{Rf(RRżRvRˇ((stests/tests.pyt!test_require_group_as_group_query—s    c C`sż|jdddttdtjdƒtdtjdƒƒdtdd ƒd d ƒ|jjd d ddƒ}|jjd dddƒ}|j|ƒ|j |ƒ|j |j j dhƒdS(NRFuuid=%(user)s,ou=people,o=testRšuou=groups,o=testu(objectClass=groupOfNames)uou=moregroups,o=testRşRťumemberRźu!cn=other_gon,ou=moregroups,o=testRHualiceRIupasswordubobu other_gon( RJRRRBR~RR RKRfR{RLR…t group_names(RRvRˇ((stests/tests.pyttest_group_unionŠs   c C`sż|jdddttdtjdƒtdtjdƒƒdtdd ƒd d ƒ|jjd d ddƒ}|jjd dddƒ}|j|ƒ|j |ƒ|j |j j dhƒdS(NRFuuid=%(user)s,ou=people,o=testRšuou=groups,o=testu(objectClass=groupOfNames)uou=moregroups,o=testRşRťumemberRźu!cn=other_gon,ou=moregroups,o=testRHualiceRIupasswordubobu other_gon( RJRRRBR~RR RKRfR{RLR…RĆ(RRvRˇ((stests/tests.pyttest_nested_group_unionťs   c C`sź|jdddtdtjƒdtddƒdd ƒ|jjd d d d ƒ}|jjd dd d ƒ}|j|ƒ|j|ƒ|j |j j ƒddddddddgƒdS(NRFuuid=%(user)s,ou=people,o=testRšuou=groups,o=testRşRťumembert DENY_GROUPucn=active_gon,ou=groups,o=testRHualiceRIupasswordubobu initializeu simple_bind_su compare_s( RJRRBR~RR RKRfR{RLR@RR(RRvRˇ((stests/tests.pyttest_denied_groupÍs     c C`sˆ|jdddtdtjƒdtddƒƒ|jjdd d d ƒ}|j|jj d „|j |j |j |j gDƒƒdS( NRFuuid=%(user)s,ou=people,o=testRšuou=groups,o=testRşRťumemberRHualiceRIupasswordcS`s h|]}|djƒ’qS(i(tlower(t.0tg((stests/tests.pys ęs (RJRRBR~RR RKRLR…t group_dnst active_gont staff_gont superuser_gont nested_gon(RRv((stests/tests.pyttest_group_dnsŕs  c C`sr|jdddtdtjƒdtddƒƒ|jjdd d d ƒ}|j|jj d d ddhƒdS(NRFuuid=%(user)s,ou=people,o=testRšuou=groups,o=testRşRťumemberRHualiceRIupasswordu active_gonu staff_gonu superuser_gonu nested_gon( RJRRBR~RR RKRLR…RĆ(RRv((stests/tests.pyttest_group_namesís  c C`sň|jdddtdtjƒdtddƒditd ƒd 6d d gd 6dd6ƒ|jjddddƒ}|jjddddƒ}|j|j ƒ|j|j ƒ|j|j ƒ|j |j ƒ|j |j ƒ|j |j ƒdS(NRFuuid=%(user)s,ou=people,o=testRšuou=groups,o=testRşRťumembertUSER_FLAGS_BY_GROUPucn=active_gon,ou=groups,o=testu is_activeucn=empty_gon,ou=groups,o=testucn=staff_gon,ou=groups,o=testuis_staffu!cn=superuser_gon,ou=groups,o=testu is_superuserRHualiceRIupasswordubob( RJRRBR~RRR RKRbt is_activetis_stafft is_superuserRW(RRvRˇ((stests/tests.pyttest_dn_group_membership÷s"    c C`s…|jdddtdtjƒdtddƒditd ƒd 6gd 6d d 6ƒ|jtƒ|jj ddddƒWdQXdS(NRFuuid=%(user)s,ou=people,o=testRšuou=groups,o=testRşRťumemberRŐucn=active_gon,ou=groups,o=testu is_activeuis_staffu!cn=superuser_gon,ou=groups,o=testu is_superuserRHualiceRIupassword( RJRRBR~RRRąR R RK(R((stests/tests.pyttest_user_flags_misconfigureds   c C`sŕ|jdddtdtjƒdtƒdidd6d d 6d d 6ƒ|jjd dddƒ}|jjd dddƒ}|j|jƒ|j|j ƒ|j|j ƒ|j |jƒ|j |j ƒ|j |j ƒdS(NRFuuid=%(user)s,ou=people,o=testRšuou=groups,o=testRşRŐucn=active_px,ou=groups,o=testu is_activeucn=staff_px,ou=groups,o=testuis_staffu cn=superuser_px,ou=groups,o=testu is_superuserRHualiceRIupasswordubob( RJRRBR~RR RKRbRÖR×RŘRW(RRvRˇ((stests/tests.pyttest_posix_memberships    c C`sŕ|jdddtdtjƒdtƒdidd6d d 6d d 6ƒ|jjd dddƒ}|jjd dddƒ}|j|jƒ|j|j ƒ|j|j ƒ|j |jƒ|j |j ƒ|j |j ƒdS(NRFuuid=%(user)s,ou=people,o=testRšuou=groups,o=testRşRŐucn=active_nis,ou=groups,o=testu is_activeucn=staff_nis,ou=groups,o=testuis_staffu!cn=superuser_nis,ou=groups,o=testu is_superuserRHualiceRIupasswordubob( RJRRBR~RR RKRbRÖR×RŘRW(RRvRˇ((stests/tests.pyttest_nis_membership3s    c C`sż|jdddtdtjƒdtddƒdid d 6d d 6ƒ|jjd d ddƒ}|jjd dddƒ}|j|jƒ|j|j ƒ|j |jƒ|j |j ƒdS(NRFuuid=%(user)s,ou=people,o=testRšuou=groups,o=testRşRťumemberRŐucn=parent_gon,ou=groups,o=testu is_activeuis_staffRHualiceRIupasswordubob( RJRRBR~RR RKRbRÖR×RW(RRvRˇ((stests/tests.pyttest_nested_dn_group_membershipIs  c C`sg|jdddtdtjƒdtƒdidd6ƒ|jjd d d d ƒ}|j|jƒdS( NRFuuid=%(user)s,ou=people,o=testRšuou=groups,o=testRşRŐucn=active_px,ou=groups,o=testu is_activeRHunobodyRIupassword( RJRRBR~RR RKRWRÖ(Rtnobody((stests/tests.pyttest_posix_missing_attributes[s   c C`së|jdddtdtjƒdtddƒdtƒ|jƒtjj d d ƒ}|j j |j ƒ}|j |j j|ƒd d hƒ|j |j j|ƒd d hƒ|j|j j|d ƒƒ|j|j j|d ƒƒdS(NRFuuid=%(user)s,ou=people,o=testRšuou=groups,o=testRşRťumembertFIND_GROUP_PERMSRHualiceu auth.add_useruauth.change_useruauth(RJRRBR~RRSt _init_groupsRRURtR RnRuRLtget_group_permissionstget_all_permissionsRbthas_permthas_module_perms(RRv((stests/tests.pyttest_dn_group_permissionsis  ""c C`sš|jdddddddtdtjƒd td d ƒd tƒ|jƒtjj d dƒ}|j j |j ƒ}|j |j j|ƒtƒƒdS(NRuuid=bob,ou=people,o=testR‘ubogusRFuuid=%(user)s,ou=people,o=testRšuou=groups,o=testRşRťumemberRŕRHualice(RJRRBR~RRSRáRRURtR RnRuRLRâtset(RRv((stests/tests.pyt!test_group_permissions_ldap_errorzs  c C`sĺ|jdddtdtjƒdtddƒdtƒ|jƒtjj d d ƒ}|j j |j ƒ}|j |j j|ƒtƒƒ|j |j j|ƒtƒƒ|j|j j|d ƒƒ|j|j j|d ƒƒdS( NRFuuid=%(user)s,ou=people,o=testRšuou=groups,o=testRşRťumemberRŕRHubobu auth.add_useruauth(RJRRBR~RRSRáRRURtR RnRuRLRâRçRăRWRäRĺ(RRˇ((stests/tests.pyttest_empty_group_permissionsŠs  c C`sč|jdddtdtjdƒdtƒdtƒ|jƒtjj dd ƒ}|j j |j ƒ}|j |j j|ƒd d hƒ|j |j j|ƒd d hƒ|j|j j|d ƒƒ|j|j j|d ƒƒdS( NRFuuid=%(user)s,ou=people,o=testRšuou=groups,o=testu(objectClass=posixGroup)RşRŕRHualiceu auth.add_useruauth.change_useruauth(RJRRBR~RRSRáRRURtR RnRuRLRâRăRbRäRĺ(RRv((stests/tests.pyttest_posix_group_permissions›s     ""c C`s=|jdddtdtjdƒdtƒdtƒ|jƒ|jj|j dtj d dfgƒ|jj|j dtj d d gfgƒtjjd d ƒ}|jj|jƒ}|j|jj|ƒd dhƒ|j|jj|ƒd dhƒ|j|jj|d ƒƒ|j|jj|dƒƒdS(NRFuuid=%(user)s,ou=people,o=testRšuou=groups,o=testu(objectClass=posixGroup)RşRŕiu gidNumberu memberUidualiceRHu auth.add_useruauth.change_useruauth(RJRRBR~RRSRáR@tmodify_sRvt MOD_DELETERt active_pxtMOD_ADDRRURtR RnRuRLRâRăRbRäRĺ(RRv((stests/tests.pyt#test_posix_group_permissions_no_gid­s     ),""c C`sč|jdddtdtjdƒdtƒdtƒ|jƒtjj dd ƒ}|j j |j ƒ}|j |j j|ƒd d hƒ|j |j j|ƒd d hƒ|j|j j|d ƒƒ|j|j j|d ƒƒdS( NRFuuid=%(user)s,ou=people,o=testRšuou=groups,o=testu(objectClass=nisNetgroup)RşRŕRHualiceu auth.add_useruauth.change_useruauth(RJRRBR~RRSRáRRURtR RnRuRLRâRăRbRäRĺ(RRv((stests/tests.pyttest_nis_group_permissionsÁs     ""c C`sy|jdddtdtjƒdtddƒdtƒ|jƒtjj d d ƒ}|j |j j |ƒt ƒƒdS( NRFuuid=%(user)s,ou=people,o=testRšuou=groups,o=testRşRťumemberRŕRHualice(RJRRBR~RRSRáRRURtRLR RâRç(RRv((stests/tests.pyttest_foreign_user_permissionsÓs  c C`s"|jdddtdtjƒdtddƒdtd tƒ|jƒtjj d d ƒj }tjj d d ƒj }xyt d ƒD]k}|j j |ƒ}|j|j j|ƒddhƒ|j j |ƒ}|j|j j|ƒtƒƒq„W|j|jjƒddddddgƒdS(NRFuuid=%(user)s,ou=people,o=testRšuou=groups,o=testRşRťumemberRŕt CACHE_GROUPSRHualiceubobiu auth.add_useruauth.change_useru initializeu simple_bind_susearch_s(RJRRBR~RRSRáRRURtRutrangeR RnRLRâRçR@RR(Rtalice_idtbob_idtiRvRˇ((stests/tests.pyttest_group_cacheŕs(   #  c C`sł|jdddtdtjdƒdtƒdtƒ|jtjj ƒdƒ|j j d d d d ƒ}|jtjj ƒd ƒ|jt |j jƒƒt tjjƒƒƒdS(NRFuuid=%(user)s,ou=people,o=testRšuou=groups,o=testu(objectClass=posixGroup)Rşt MIRROR_GROUPSiRHualiceRIupasswordi(RJRRBR~RRSRLRRURVR RKRçtgroupstall(RRv((stests/tests.pyttest_group_mirroring˙s    c C`sÇ|jdddtdtjdƒdtddƒd tƒ|jjd d d d ƒ}|jt t j j ƒj ddtƒƒddddddhƒ|jt |jj ƒƒt t j j ƒƒƒdS(NRFuuid=%(user)s,ou=people,o=testRšuou=groups,o=testu(objectClass=groupOfNames)RşRťumemberRřRHualiceRIupasswordunametflatu active_gonu staff_gonu superuser_gonu nested_gonu parent_gonu circular_gon(RJRRBR~RRSR RKRLRçRRURút values_listRů(RRv((stests/tests.pyttest_nested_group_mirrorings   !  c C`sú|jdddtdtjdƒdtƒddd gƒi}x:d „td d ƒDƒD]}tjjd |ƒ||6siiRualiceumirror4RHRIupasswordunameRü(RJRRBR~RRóRRURtR RĄRůRçRKRLRýRS(RRůRRv((stests/tests.pyt%test_group_mirroring_whitelist_update,s      c C`sú|jdddtdtjdƒdtƒddd gƒi}x:d „td d ƒDƒD]}tjjd |ƒ||LsiiRualiceumirror3RHRIupasswordunameRü(RJRRBR~RRóRRURtR RĄRůRçRKRLRýRS(RRůRRv((stests/tests.pyt#test_group_mirroring_whitelist_noopBs      c C`sú|jdddtdtjdƒdtƒddd gƒi}x:d „td d ƒDƒD]}tjjd |ƒ||bsiiRualiceumirror4RHRIupasswordunameRüumirror3(RJRRBR~RRóRRURtR RĄRůRçRKRLRýRS(RRůRRv((stests/tests.pyt%test_group_mirroring_blacklist_updateXs      c C`sú|jdddtdtjdƒdtƒddd gƒi}x:d „td d ƒDƒD]}tjjd |ƒ||xsiiRualiceumirror3RHRIupasswordunameRü(RJRRBR~RRóRRURtR RĄRůRçRKRLRýRS(RRůRRv((stests/tests.pyt#test_group_mirroring_blacklist_noopns      c C`s‚|jdddtdtjƒdtddƒdtd tƒ|jƒtjj d d ƒ}|j |j j |ƒd d hƒdS(NRFuuid=%(user)s,ou=people,o=testRšuou=groups,o=testRşRťumemberRŕtAUTHORIZE_ALL_USERSRHualiceu auth.add_useruauth.change_user( RJRRBR~RRSRáRRURtRLR Râ(RRv((stests/tests.pyttest_authorize_external_users„s  c C`sŽ|jdtdtjdƒdtdtjƒdtddƒd td tƒ|jƒtjj d d ƒ}|j |j j |ƒt ƒƒdS( NR}uou=people,o=testu(uid=%(user)s)Ršuou=groups,o=testRşRťumemberRŕRRHu not-in-ldap(RJRRBR~RRSRáRRURtRLR RâRç(RRv((stests/tests.pyttest_authorize_external_unknown’s  cC`sţ|jddƒ|jjdƒ}|jjdƒ}|j|ƒ|j|jdƒ|j|jdƒ|j|jƒ|j |j ƒ|j |j ƒ|j|ƒ|j|jdƒ|j|jdƒ|j|jƒ|j |j ƒ|j |j ƒdS(NRFuuid=%(user)s,ou=people,o=testualiceubobu( RJR RĄR{RLRšR—RbRÖRWR×RŘ(RRvRˇ((stests/tests.pyttest_create_without_auth˘s    cC`st|jdddtdidd6dd6d td tjƒd tƒd id d6dd6dd6ƒtjjddƒtjjddƒ|j j dƒ}|j j dƒ}|j |ƒ|j |j dƒ|j |jdƒ|j|jƒ|j|jƒ|j|jƒ|j |ƒ|j |j dƒ|j |jdƒ|j|jƒ|j|jƒ|j|jƒdS(NRFuuid=%(user)s,ou=people,o=testRľRku givenNameu first_nameusnu last_nameRšuou=groups,o=testRşRŐucn=active_gon,ou=groups,o=testu is_activeucn=staff_gon,ou=groups,o=testuis_staffu!cn=superuser_gon,ou=groups,o=testu is_superuserRHualiceubobuAliceuAdamsuRobertuBarker(RJRśRRBR~RRRURtR RĄR{RLRšR—RbRÖR×RŘRW(RRvRˇ((stests/tests.pyttest_populate_without_authˇs4     cC`s3|jddƒ|jjdƒ}|j|ƒdS(NRFuuid=%(user)s,ou=people,o=testubogus(RJR RĄRf(Rtbogus((stests/tests.pyttest_populate_bogus_userŘs cC`sY|jdddtƒ|j|jjƒ|jjddddƒ|j|jjƒdS(NRFuuid=%(user)s,ou=people,o=testt START_TLSRHualiceRIupassword(RJRśRWR@t tls_enabledR RK(R((stests/tests.pyttest_start_tls_missingás  cC`sY|jdddtƒ|j|jjƒ|jjddddƒ|j|jjƒdS(NRFuuid=%(user)s,ou=people,o=testR RHualiceRIupassword(RJRSRWR@R R RKRb(R((stests/tests.pyttest_start_tlsës  cC`s<|jdtdtjdƒƒ|jjddddƒdS( u: Make sure we're not phased by referrals. R}uou=people,o=testu(uid=%(user)s)RHualiceRIupasswordN(RJRRBR~R RK(R((stests/tests.pyttest_null_search_resultsős c C`s‘|jdttdtjdƒtdtjdƒƒƒ|jjddddƒ}|j|ƒ|j|j j ƒd d d d d d d gƒdS( NR}uou=groups,o=testu(uid=%(user)s)uou=people,o=testRHualiceRIupasswordu initializeu simple_bind_susearchuresult( RJRRRBR~R RKR{RLR@RR(RRv((stests/tests.pyttest_union_searchs   cC`sU|jddƒ|jjddddƒ}|j|ƒ|j|jjƒgƒdS(NRFuuid=%(user)s,ou=people,o=testRHualiceRIu(RJR RKRfRLR@RR(RRv((stests/tests.pyttest_deny_empty_passwords   cC`sa|jdddtƒ|jjddddƒ}|j|ƒ|j|jjƒdd gƒdS( NRFuuid=%(user)s,ou=people,o=testtPERMIT_EMPTY_PASSWORDRHualiceRIuu initializeu simple_bind_s(RJRSR RKRfRLR@RR(RRv((stests/tests.pyttest_permit_empty_passwords   cC`sa|jdddtƒ|jjddddƒ}|j|ƒ|j|jjƒddgƒdS( NRFuuid=%(user)s,ou=people,o=testRRHualiceRIu initializeu simple_bind_s( RJRSR RKRRfRLR@RR(RRv((stests/tests.pyttest_permit_null_password)s   c C`s%|jdddtdtjƒdtddƒdtƒ|jƒ|jjd d d d ƒ}t j |t j ƒ}t j |ƒ}|j jj|j j_|j|ƒ|j|jj|ƒd dhƒ|j|jj|ƒd dhƒ|j|jj|d ƒƒ|j|jj|dƒƒdS(NRFuuid=%(user)s,ou=people,o=testRšuou=groups,o=testRşRťumemberRŕRHualiceRIupasswordu auth.add_useruauth.change_useruauth(RJRRBR~RRSRáR RKtpickletdumpstHIGHEST_PROTOCOLtloadsR…tsettingsR{RLRâRăRbRäRĺ(Rtalice0tpickledRv((stests/tests.pyt test_pickle7s   ""cC`srtdtjdddgƒ}|j|jƒ|j|jjdtƒddtjdddgfifgƒdS(Nuou=people,o=testu (uid=alice)u*u+RQusearch_s(RRBR~texecuteR@RLRRRS(Rtsearch((stests/tests.pyttest_search_attrlistLs c `s­dtjf‡fd†ƒY‰ˆƒ|_|jddƒtjdtƒ-}tjdƒ|jjddd d ƒWdQX|jt |ƒd ƒ|j|d j t ƒdS( NR\c`seZ‡fd†ZRS(c`stˆ|ƒj||ƒS(N(tsupertget_or_create_user(RRHR…(R\(stests/tests.pyR"Vs(R"R#R"((R\(stests/tests.pyR\UsRFuuid=%(user)s,ou=people,o=testtrecordualwaysRHualiceRIupasswordii( R RARJR9tcatch_warningsRSt simplefilterRKRLtlenR4tDeprecationWarning(Rtw((R\stests/tests.pyt"test_get_or_create_user_deprecatedTs  c C`sdtjfd„ƒY}|ƒ|_|jddƒtjdtƒ-}tjdƒ|jjddd d ƒWdQX|jt |ƒd ƒdS( NR\cB`seZRS((R"R#(((stests/tests.pyR\csRFuuid=%(user)s,ou=people,o=testR#ualwaysRHualiceRIupasswordi( R RARJR9R$RSR%RKRLR&(RR\R(((stests/tests.pyt4test_custom_backend_without_get_or_create_no_warningbs  c`spdtjf‡fd†ƒY‰ˆƒ|_|jddƒ|jjddddƒ}|j|jjd ƒdS( NR\c`seZ‡fd†ZRS(c`s"d|_tˆ|ƒj||ƒS(Nubar(tfooR!tauthenticate_ldap_user(RR…RI(R\(stests/tests.pyR,ps (R"R#R,((R\(stests/tests.pyR\osRFuuid=%(user)s,ou=people,o=testRHualiceRIupasswordubar(R RARJRKRLR…R+(RRZ((R\stests/tests.pyt+test_override_authenticate_access_ldap_userns  cK`st||j_dS(N(RR R(RR((stests/tests.pyRJ}scC`stjjddƒtjjddƒg}tjjddƒ}|jj|Œtjjddƒ}|jj|Œtjjddƒ}|jj|ŒdS(Ntcodenameuadd_useru change_userRu active_gonu active_pxu active_nis(RRURRRtt permissionstadd(RR/RĎRít active_nis((stests/tests.pyRá€s(…R"R#ttoptpeopleRůt moregroupst mirror_groupsRvRˇR tdresslerRŢRítstaff_pxt superuser_pxt empty_gonRĎRĐRŃt other_gont alice_gont mutual_gontbob_gontmirror1tmirror2tmirror3tmirror4R1t staff_nist superuser_nist parent_gonRŇt circular_gontdictR8t classmethodR3R<R=RCRERNRTR[R^R RRBR~RdReRgRhRiRmRpRwRxRyRzR|R‚RŠR‹RŒRRŽRR•R–R˜R™R›RR R˘RĽR§RŽRłR´R¸R˝RŔRÁRÂRĂRÄRĹRÇRČRĘRÓRÔRŮRÚRŰRÜRÝRßRćRčRéRęRďRđRńR÷RűRţRRRRRRRR R RRRRRRRRR R)R*R-RJRá(((stests/tests.pyR%Fsş                                                                        ''                                                       !        (.t __future__RRRRtcopyRR&RtunittestR9RBtdjango.contrib.auth.modelsRRRtdjango.core.cacheRtdjango.core.exceptionsR t django.testR tdjango.test.utilsR tdjango.utils.encodingR tdjango_auth_ldapR tdjango_auth_ldap.configRRRRRRRRtmodelsRR6t ImportErrorRt LDAPSettingsRtskipIfR%(((stests/tests.pyts,"     :   django-auth-ldap-1.4.0/tests/__init__.py0000644000076600000240000000000013151037627020671 0ustar psagersstaff00000000000000django-auth-ldap-1.4.0/tests/__pycache__/0000755000076600000240000000000013255035702020777 5ustar psagersstaff00000000000000django-auth-ldap-1.4.0/tests/__pycache__/models.cpython-36.pyc0000644000076600000240000000234513255034721024714 0ustar psagersstaff000000000000003 |`NZ™ă@sDddlmZmZmZmZddlmZddlmZGdd„deƒZ dS)é)Úabsolute_importÚdivisionÚprint_functionÚunicode_literals)ÚAbstractBaseUser)Úmodelsc@sReZdZejddddZejƒZdZdd„Z dd„Z d d „Z d d „Z e e e ƒZd S)ÚTestUseré(T)Ú max_lengthÚuniqueÚdb_indexÚ identifiercCs|jS)N)r )ÚselfŠrú8/Users/psagers/Projects/django-auth-ldap/tests/models.pyÚ get_full_name szTestUser.get_full_namecCs|jS)N)r )rrrrÚget_short_nameszTestUser.get_short_namecCsdS)NZAlicer)rrrrÚget_first_nameszTestUser.get_first_namecCs tdƒ‚dS)NzOops...)Ú Exception)rÚvaluerrrÚset_first_nameszTestUser.set_first_nameN)Ú__name__Ú __module__Ú __qualname__rÚ CharFieldr Ú IntegerFieldZ uid_numberÚUSERNAME_FIELDrrrrÚpropertyÚ first_namerrrrrsrN) Ú __future__rrrrÚdjango.contrib.auth.modelsrÚ django.dbrrrrrrÚs  django-auth-ldap-1.4.0/tests/__pycache__/tests.cpython-36.pyc0000644000076600000240000014101613255034721024572 0ustar psagersstaff000000000000003 RŃŻZűůă @s:ddlmZmZmZmZddlmZddlZddlZddl Z ddl Z ddl Z ddl m Z mZmZddlmZddlmZddlmZddlmZdd lmZdd lmZdd lmZmZmZm Z m!Z!m"Z"m#Z#m$Z$d d l%m&Z&y ddl'Z'Wne(k rdZ'YnXGdd„dej)ƒZ*e j+e'dkdƒGdd„deƒƒZ,dS)é)Úabsolute_importÚdivisionÚprint_functionÚunicode_literals)ÚdeepcopyN)ÚGroupÚ PermissionÚUser)Úcache)ÚImproperlyConfigured)ÚTestCase)Úoverride_settings)Ú force_str)Úbackend)ÚGroupOfNamesTypeÚLDAPGroupQueryÚ LDAPSearchÚLDAPSearchUnionÚMemberDNGroupTypeÚNestedMemberDNGroupTypeÚ NISGroupTypeÚPosixGroupTypeé)ÚTestUserc@seZdZdZdd„ZdS)Ú TestSettingszb A replacement for backend.LDAPSettings that does not load settings from django.conf. cKs4x.|jjƒD] \}}|j||ƒ}t|||ƒq WdS)N)ÚdefaultsÚitemsÚgetÚsetattr)ÚselfÚkwargsÚnameÚdefaultÚvalueŠr$ú7/Users/psagers/Projects/django-auth-ldap/tests/tests.pyÚ__init__@s zTestSettings.__init__N)Ú__name__Ú __module__Ú __qualname__Ú__doc__r&r$r$r$r%r;srz4django_auth_ldap tests require the mockldap package.c@seZdZdddifZdddifZdddifZd dd ifZd dd ifZd dgddddgdgdgdgdgdgdœfZddgddddgdgdgdgdgdgdœfZ e dƒe dƒgddddgdgd gdgd!ge d"ƒgdœfZ d#d$gddddgdgd%gd&œfZ d'd(gd)gdggd*œfZ d+d,gd)gdgdgd*œfZd-d.gd)gd gdgd*œfZd/d0gd1ggd2œfZd3d4gd1gd gd2œfZd5d6gd1gd gd2œfZd7d8gd1gd gd2œfZd9d:gd1gdgd2œfZd;dgd1gd dgd2œfZd?d@gd1gdgd2œfZdAdBgd1gd gd2œfZdCdDgd1ggd2œfZdEdFgd1gd gd2œfZdGdHgd1ggd2œfZdIdJgdKgdLgdMœfZdNdOgdKgdLgdMœfZdPdQgdKgdLgdMœfZdRdSgd1gdTgd2œfZdUdVgd1gd dWgd2œfZ dWdXgd1gdRgd2œfZ!e"eeeeeee e e e eeeeeeeeeeeeeeeeeee e!gƒZ#e$dYdZ„ƒZ%e$d[d\„ƒZ&e$d]d^„ƒZ'd_d`„Z(dadb„Z)dcdd„Z*dedf„Z+dgdh„Z,didj„Z-e.e/de0j1dkƒdldmdn„ƒZ2e.e/de0j1dkƒdldodp„ƒZ3dqdr„Z4dsdt„Z5dudv„Z6e.dwdxdydz„ƒZ7e.dwdxd{d|„ƒZ8e.dwdxd}d~„ƒZ9dd€„Z:dd‚„Z;dƒd„„Zd‰dŠ„Z?d‹dŒ„Z@ddŽ„ZAdd„ZBd‘d’„ZCd“d”„ZDd•d–„ZEd—d˜„ZFd™dš„ZGd›dœ„ZHddž„ZIdŸd „ZJe.dwdxdĄd˘„ƒZKe.dwdxdŁd¤„ƒZLdĽdŚ„ZMd§d¨„ZNdŠdŞ„ZOdŤdŹ„ZPd­dŽ„ZQdŻd°„ZRdąd˛„ZSdłd´„ZTdľdś„ZUdˇd¸„ZVdšdş„ZWdťdź„ZXd˝dž„ZYdżdŔ„ZZdÁd„Z[dĂdĄZ\dĹdƄZ]dÇdȄZ^dÉdʄZ_dËd̄Z`dÍd΄ZadĎdЄZbdŃd҄ZcdÓdԄZddŐdքZed×d؄ZfdŮdڄZgdŰd܄ZhdÝdބZidßdŕ„Zjdádâ„Zkdădä„Zldĺdć„Zmdçdč„Zndédę„Zodëdě„Zpdídî„Zqdďdđ„Zrdńdň„Zsdódô„Ztdődö„Zud÷dř„Zvdůdú„Zwdűdü„Zxdýdţ„Zyd˙d„Zzdd„Z{dd„Z|dd„Z}dd„Z~d d „Zd d „Z€d d„Zdd„Z‚dd„Zƒdd„Z„dd„Z…dS(ÚLDAPTestzo=testÚoÚtestzou=people,o=testZouÚpeoplezou=groups,o=testÚgroupszou=moregroups,o=testÚ moregroupszou=mirror_groups,o=testÚ mirror_groupszuid=alice,ou=people,o=testÚaliceZpersonZorganizationalPersonZ inetOrgPersonZ posixAccountÚpasswordZ1000ÚAliceÚAdams)ÚuidÚ objectClassÚ userPasswordÚ uidNumberÚ gidNumberÚ givenNameÚsnzuid=bob,ou=people,o=testÚbobZ1001Z50ÚRobertÚBarkeruuid=dreßler,ou=people,o=testudreßlerZ1002ZWolfganguDreßlerzuid=nobody,ou=people,o=testÚnobodyő²)r6r7r8Z binaryAttrzcn=active_px,ou=groups,o=testÚ active_pxZ posixGroup)Úcnr7r:Ú memberUidzcn=staff_px,ou=groups,o=testÚstaff_pxz cn=superuser_px,ou=groups,o=testÚ superuser_pxzcn=empty_gon,ou=groups,o=testÚ empty_gonZ groupOfNames)rCr7Úmemberzcn=active_gon,ou=groups,o=testÚ active_gonzcn=staff_gon,ou=groups,o=testÚ staff_gonz!cn=superuser_gon,ou=groups,o=testÚ superuser_gonz!cn=other_gon,ou=moregroups,o=testÚ other_gonz#cn=alice_gon,ou=query_groups,o=testÚ alice_gonz$cn=mutual_gon,ou=query_groups,o=testÚ mutual_gonz!cn=bob_gon,ou=query_groups,o=testÚbob_gonz"cn=mirror1,ou=mirror_groups,o=testÚmirror1z"cn=mirror2,ou=mirror_groups,o=testÚmirror2z"cn=mirror3,ou=mirror_groups,o=testÚmirror3z"cn=mirror4,ou=mirror_groups,o=testÚmirror4zcn=active_nis,ou=groups,o=testÚ active_nisZ nisNetgroupz (,alice,))rCr7ZnisNetgroupTriplezcn=staff_nis,ou=groups,o=testÚ staff_nisz!cn=superuser_nis,ou=groups,o=testÚ superuser_niszcn=parent_gon,ou=groups,o=testÚ parent_gonzcn=nested_gon,ou=groups,o=testzCN=nested_gon,ou=groups,o=testÚ nested_gonz cn=circular_gon,ou=groups,o=testÚ circular_goncCsLtjdƒ}tjdƒ}tjƒ}|jtjƒ|j|ƒ|j|ƒ|jtjƒdS)NÚdjango_auth_ldapz'LDAP auth - %(levelname)s - %(message)s) ÚloggingÚ getLoggerÚ FormatterÚ StreamHandlerÚsetLevelÚDEBUGÚ setFormatterÚ addHandlerÚCRITICAL)ÚclsÚloggerÚ formatterÚhandlerr$r$r%Úconfigure_loggerós     zLDAPTest.configure_loggercCs*|jƒtj|jƒ|_tjdtdddS)NÚignorezmockldap.ldapobject)ÚcategoryÚmodule)rhÚmockldapZMockLdapÚ directoryÚwarningsÚfilterwarningsÚUnicodeWarning)rdr$r$r%Ú setUpClass˙szLDAPTest.setUpClasscCs|`dS)N)rl)rdr$r$r%Ú tearDownClassszLDAPTest.tearDownClasscCs4tjƒ|jjƒ|jd|_tjƒ|_|jjdS)Nzldap://localhost)r ÚclearrlÚstartÚldapobjrÚ LDAPBackendÚldap)rr$r$r%ÚsetUp s    zLDAPTest.setUpcCs|jjƒ|`dS)N)rlÚstopru)rr$r$r%ÚtearDowns zLDAPTest.tearDowncCs:|jdddid|jjddd|j|jjdƒdƒdS)Nzuid=%(user)s,ou=people,o=testZopt1Zvalue1)ÚUSER_DN_TEMPLATEZCONNECTION_OPTIONSr2r3)Úusernamer3)Ú_init_settingsrÚ authenticateÚ assertEqualruZ get_option)rr$r$r%Ú test_optionss  zLDAPTest.test_optionscCsT|jdd„dd|jjddd|jd}|j|jd d d difd difgƒdS)NcSsdS)Nzldap://ldap.example.comr$r$r$r$r%Ú(sz3LDAPTest.test_callable_server_uri..zuid=%(user)s,ou=people,o=test)Z SERVER_URIr{r2r3)r|r3úldap://ldap.example.comT)Ú with_argsÚ initializeÚ simple_bind_súuid=alice,ou=people,o=test)r‚)r†r3)r}rr~rlrÚmethods_called)rrur$r$r%Útest_callable_server_uri&s  z!LDAPTest.test_callable_server_uricCsr|jddtjjƒ}|jjddd}|j|jƒƒ|j|j dƒ|jtjjƒ|dƒ|j|j j ƒddgƒdS) Nzuid=%(user)s,ou=people,o=test)r{r2r3)r|r3rr„r…) r}r ÚobjectsÚcountrr~Ú assertFalseÚhas_usable_passwordrr|rur‡)rÚ user_countÚuserr$r$r%Útest_simple_bind5s zLDAPTest.test_simple_bindcCs€Gdd„dtjƒ}|ƒ|_tjjƒ}|jjddd}|j|jƒƒ|j|j dƒ|jtjjƒ|dƒ|j|j j ƒddgƒdS) Nc@seZdZddiZdS)z1LDAPTest.test_default_settings..MyBackendr{zuid=%(user)s,ou=people,o=testN)r'r(r)Údefault_settingsr$r$r$r%Ú MyBackendFsr‘r2r3)r|r3rr„r…) rrvr r‰rŠr~r‹rŒrr|rur‡)rr‘rrŽr$r$r%Útest_default_settingsEs zLDAPTest.test_default_settingsz(uid=%(user)s))ZAUTH_LDAP_USER_SEARCHcCs|jjddd}|j|ƒdS)Nr2r3)r|r3)ÚclientÚloginÚ assertTrue)rÚauthr$r$r%Ú&test_login_with_multiple_auth_backendsXsz/LDAPTest.test_login_with_multiple_auth_backendscCs|jjddd}|j|ƒdS)NÚinvalidZi_do_not_exist)r|r3)r“r”r‹)rr–r$r$r%Ú*test_bad_login_with_multiple_auth_backends]sz3LDAPTest.test_bad_login_with_multiple_auth_backendscCsP|jdd|jjddd}|j|ƒ|j|jjdddd ifd difgƒd S)z. Bind with a username that requires escaping. zuid=%(user)s,ou=people,o=test)r{zalice,1r3)r|r3T)rƒr„úldap://localhostr…úuid=alice\,1,ou=people,o=testN)rš)r›r3)r}rr~Ú assertIsNonerrur‡)rrŽr$r$r%Útest_simple_bind_escapedbs  z!LDAPTest.test_simple_bind_escapedcCsr|jddtjjƒ}|jjddd}|j|jƒƒ|j|j dƒ|jtjjƒ|dƒ|j|j j ƒdd gƒdS) Nzuid=%(user)s,ou=people,o=test)r{r4r3)r|r3r2rr„r…) r}r r‰rŠrr~r‹rŒrr|rur‡)rrrŽr$r$r%Útest_new_user_lowercaseqs z LDAPTest.test_new_user_lowercasecCs(|jdd|jjddd}t|ƒ}dS)Nzuid=%(user)s,ou=people,o=test)r{r4r3)r|r3)r}rr~r)rrŽr$r$r%Ú test_deepcopyszLDAPTest.test_deepcopyztests.TestUser)ÚAUTH_USER_MODELcCs2|jdddid|jjddd}|j|tƒdS)Nzuid=%(user)s,ou=people,o=testÚ uid_numberr9)r{Ú USER_ATTR_MAPr4r3)r|r3)r}rr~ÚassertIsInstancer)rrŽr$r$r%Útest_auth_custom_user‰s  zLDAPTest.test_auth_custom_usercCs@|jdddid|jjddd}|jj|jƒ}|j|tƒdS)Nzuid=%(user)s,ou=people,o=testrĄr9)r{r˘r4r3)r|r3)r}rr~Úget_userÚidrŁr)rrŽr$r$r%Útest_get_custom_user”s  zLDAPTest.test_get_custom_usercCsT|jdddiddtjjddd}|jjdd d }|j|tƒ|j|j|jƒdS) Nzuid=%(user)s,ou=people,o=testrĄr9)r{r˘ZUSER_QUERY_FIELDZabcdefič)Ú identifierrĄr4r3)r|r3) r}rr‰Úcreaterr~rŁrÚpk)rr2rŽr$r$r%Útest_get_custom_field s zLDAPTest.test_get_custom_fieldcCsl|jddtjjƒ}|jjddd}|jjddd}|j|jƒƒ|j|j dƒ|jtjjƒ|dƒdS) Nzuid=%(user)s,ou=people,o=test)r{z alicer3)r|r3zalice r2r) r}r r‰rŠrr~r‹rŒrr|)rrrŽr$r$r%Útest_new_user_whitespaceŽs z!LDAPTest.test_new_user_whitespacecCs\|jddtjjƒ}|jjddd}|j|ƒ|jtjjƒ|ƒ|j|jj ƒddgƒdS)Nzuid=%(user)s,ou=people,o=test)r{Z evil_alicer3)r|r3r„r…) r}r r‰rŠrr~rœrrur‡)rrrŽr$r$r%Útest_simple_bind_bad_userťs  z"LDAPTest.test_simple_bind_bad_usercCs\|jddtjjƒ}|jjddd}|j|ƒ|jtjjƒ|ƒ|j|jj ƒddgƒdS)Nzuid=%(user)s,ou=people,o=test)r{r2Úbogus)r|r3r„r…) r}r r‰rŠrr~rœrrur‡)rrrŽr$r$r%Útest_simple_bind_bad_passwordĘs  z&LDAPTest.test_simple_bind_bad_passwordcCsT|jddtjjddtjjƒ}|jjddd}|j|ƒ|jtjjƒ|ƒdS)Nzuid=%(user)s,ou=people,o=test)r{r2)r|r3)r|r3) r}r r‰rŠrŠrr~ÚassertIsNotNoner)rrrŽr$r$r%Útest_existing_userŮs  zLDAPTest.test_existing_usercCs€|jtdtjdƒd|jjjdtjddƒ|jgƒtj j dd|j j ddd }|j |ƒ|j|jdƒ|jtj jƒd ƒdS) Nzou=people,o=testz(uid=%(user)s))Ú USER_SEARCHz (uid=Alice)r2)r|r4r3)r|r3r)r}rrwÚ SCOPE_SUBTREEruÚsearch_sÚseedr2r r‰rŠrr~r°rr|rŠ)rrŽr$r$r%Útest_existing_user_insensitivećs z'LDAPTest.test_existing_user_insensitivecCsŔGdd„dtjƒ}|ƒ|_|jddtjjƒ}|jjddd}|jj|jƒ}|j tjjƒ|dƒ|j |j d ƒ|j |j j dƒ|j |j dƒ|j |j d ƒ|j |j j dƒ|j |j dƒdS) Nc@seZdZdd„Zdd„ZdS)z1LDAPTest.test_convert_username..MyBackendcSsd|S)Nzldap_%sr$)rr|r$r$r%Úldap_to_django_usernameůszILDAPTest.test_convert_username..MyBackend.ldap_to_django_usernamecSs |dd…S)Nér$)rr|r$r$r%Údjango_to_ldap_usernameüszILDAPTest.test_convert_username..MyBackend.django_to_ldap_usernameN)r'r(r)rˇršr$r$r$r%r‘řsr‘zuid=%(user)s,ou=people,o=test)r{r2r3)r|r3rZ ldap_alice)rrvr}r r‰rŠr~rĽrŞrr|Ú ldap_userÚ _usernameZ ldap_username)rr‘rZuser1Zuser2r$r$r%Útest_convert_username÷s zLDAPTest.test_convert_usernamecCsn|jtdtjdƒdtjjƒ}|jjddd}|j |ƒ|j tjjƒ|dƒ|j |j j ƒdd d d gƒdS) Nzou=people,o=testz(uid=%(user)s))r˛r2r3)r|r3rr„r…r´) r}rrwrłr r‰rŠrr~r°rrur‡)rrrŽr$r$r%Útest_search_binds  zLDAPTest.test_search_bindc Csl|jtdtjdƒd|jjddd}|j|ƒ|j|jj ddd difd difd dtjddfifgƒdS)z/ Search for a username that requires escaping. zou=people,o=testz(uid=%(user)s))r˛zalice*r3)r|r3T)rƒr„úldap://localhostr…Úr´z(uid=alice\2a)N)rž)rżrż) r}rrwrłrr~rœrrur‡)rrŽr$r$r%Útest_search_bind_escaped!s  z!LDAPTest.test_search_bind_escapedcCsL|jtdtjdƒd|jjddd}|j|ƒ|j|jj ƒddd gƒdS) Nzou=people,o=testz (cn=%(user)s))r˛r2r3)r|r3r„r…r´) r}rrwrłrr~rœrrur‡)rrŽr$r$r%Útest_search_bind_no_user3s z!LDAPTest.test_search_bind_no_usercCsL|jtdtjdƒd|jjddd}|j|ƒ|j|jj ƒddd gƒdS) Nzou=people,o=testz(uid=*))r˛r2r3)r|r3r„r…r´) r}rrwrłrr~rœrrur‡)rrŽr$r$r%Útest_search_bind_multiple_usersBs z(LDAPTest.test_search_bind_multiple_userscCsN|jtdtjdƒd|jjddd}|j|ƒ|j|jj ƒddd dgƒdS) Nzou=people,o=testz(uid=%(user)s))r˛r2rŽ)r|r3r„r…r´) r}rrwrłrr~rœrrur‡)rrŽr$r$r%Útest_search_bind_bad_passwordQs z&LDAPTest.test_search_bind_bad_passwordcCs’|jddtdtjdƒd|jjddd}|j|ƒ|j|jƒ|j|jj |j dƒ|j|jj tj j |j d ƒƒ|j|j jƒd d d d gƒdS) Nzuid=bob,ou=people,o=testr3zou=people,o=testz(uid=%(user)s))ÚBIND_DNÚ BIND_PASSWORDr˛r2)r|r3rrr„r…r´)r}rrwrłrr~r°rşrÚdnr2ÚattrsÚcidictrur‡)rrŽr$r$r%Ú!test_search_bind_with_credentials`s  z*LDAPTest.test_search_bind_with_credentialscCsN|jddtdtjdƒd|jjddd}|j|ƒ|j|jj ƒd d gƒdS) Nzuid=bob,ou=people,o=testrŽzou=people,o=testz(uid=%(user)s))rÄrĹr˛r2r3)r|r3r„r…) r}rrwrłrr~rœrrur‡)rrŽr$r$r%Ú%test_search_bind_with_bad_credentialsts z.LDAPTest.test_search_bind_with_bad_credentialscCsN|jddddœd|jjddd}|j|ƒ|j|jdƒ|j|jd ƒdS) Nzuid=%(user)s,ou=people,o=testr;r<)Ú first_nameÚ last_name)r{r˘udreßlerr3)r|r3uDreßler)r}rr~r°rr|rĚ)rrŽr$r$r%Útest_unicode_user…s zLDAPTest.test_unicode_usercCs4|jdd|jjddd}|j|jjtjjƒdS)Nzuid=%(user)s,ou=people,o=test)r{r2r3)r|r3)r}rr~rŁrşrÇrwrČ)rrŽr$r$r%Ú test_cidictszLDAPTest.test_cidictcCsl|jddddœd|jjddd}|j|jdƒ|j|jd ƒ|j|jd ƒ|j|jjƒd d d d gƒdS)Nzuid=%(user)s,ou=people,o=testr;r<)rËrĚ)r{r˘r2r3)r|r3r4r5r„r…r´) r}rr~rr|rËrĚrur‡)rrŽr$r$r%Útest_populate_user™szLDAPTest.test_populate_usercCsb|jdddddœd|jjddd }|j|jdƒ|j|jd ƒ|j|jd ƒ|j|jd ƒdS) Nzuid=%(user)s,ou=people,o=testr;r<Úmail)rËrĚÚemail)r{r˘r2r3)r|r3r4r5rż)r}rr~rr|rËrĚrŃ)rrŽr$r$r%Ú)test_populate_user_with_missing_attributeŤs z2LDAPTest.test_populate_user_with_missing_attributec Cs@|jddddœd|jtdƒ|jjddd WdQRXdS) Nzuid=%(user)s,ou=people,o=testr;r9)rËrĄ)r{r˘zOops...r2r3)r|r3)r}ÚassertRaisesMessageÚ Exceptionrr~)rr$r$r%Ú4test_authenticate_with_buggy_setter_raises_exceptionťs  z=LDAPTest.test_authenticate_with_buggy_setter_raises_exceptionc Cs<|jddddœd|jtdƒ|jjdƒWdQRXdS)Nzuid=%(user)s,ou=people,o=testr;r9)rËrĄ)r{r˘zOops...r2)r}rÓrÔrÚ populate_user)rr$r$r%Ú5test_populate_user_with_buggy_setter_raises_exceptionČs  z>LDAPTest.test_populate_user_with_buggy_setter_raises_exceptioncCs„|jddddœddgd|jjdd d }|j|jdƒ|j|jjƒd d d d gƒ|j|jjdddd dtjdddgfifƒdS)Nzuid=%(user)s,ou=people,o=testr;r<)rËrĚÚ*ú+)r{r˘Z USER_ATTRLISTr2r3)r|r3r„r…r´T)rƒézuid=alice,ou=people,o=testz(objectClass=*)) r}rr~rr|rur‡rwZ SCOPE_BASE)rrŽr$r$r%Útest_populate_with_attrlistŐs z$LDAPTest.test_populate_with_attrlistcCsl|jddddœdd|jjddd }|j|jdƒ|j|jd ƒ|j|jd ƒ|j|jjƒd d dgƒdS)Nzuid=%(user)s,ou=people,o=testr;r<)rËrĚT)r{r˘ZBIND_AS_AUTHENTICATING_USERr2r3)r|r3r4r5r„r…r´) r}rr~rr|rËrĚrur‡)rrŽr$r$r%Útest_bind_as_useręszLDAPTest.test_bind_as_usercsPˆjdd‡fdd„}tjj|ƒˆjjddd}ˆj|jƒtjj|ƒdS)Nzuid=%(user)s,ou=people,o=test)r{cs&ˆjd|ƒˆjd|ƒd|d_dS)NrŽrşT)ÚassertInÚpopulate_user_handled)Úsenderr )rr$r%Úhandle_populate_users  z@LDAPTest.test_signal_populate_user..handle_populate_userr2r3)r|r3)r}rrÖÚconnectr~r•rŢÚ disconnect)rrŕrŽr$)rr%Útest_signal_populate_userýs   z"LDAPTest.test_signal_populate_userc sjˆjddtdtjdƒd‡fdd„}tjj|ƒˆjtjƒˆjj dd d WdQRXtjj |ƒdS) Nzuid=bob,ou=people,o=testrŽzou=people,o=testz(uid=%(user)s))rÄrĹr˛csˆj|ddƒ|d‚dS)NÚcontextr~Ú exception)r)rßr )rr$r%Úhandle_ldap_errorsz?LDAPTest.test_auth_signal_ldap_error..handle_ldap_errorr2r3)r|r3) r}rrwrłrZ ldap_errorráÚ assertRaisesZ LDAPErrorr~râ)rrćr$)rr%Útest_auth_signal_ldap_errors  z$LDAPTest.test_auth_signal_ldap_errorcCs4|jddtdtjdƒd|jjdƒ}|j|ƒdS)Nzuid=bob,ou=people,o=testrŽzou=people,o=testz(uid=%(user)s))rÄrĹr˛r2)r}rrwrłrrÖrœ)rrŽr$r$r%Útest_populate_signal_ldap_error s z(LDAPTest.test_populate_signal_ldap_errorcCs„|jddddœddtjjddd d |jjdd d }|jjd d d }|j|jdƒ|j|jd ƒ|j|jdƒ|j|jdƒdS)Nzuid=%(user)s,ou=people,o=testr;r<)rËrĚF)r{r˘ÚALWAYS_UPDATE_USERr2ZAliciaZAstro)r|rËrĚr3)r|r3r=r>r?) r}r r‰rŠrr~rrËrĚ)rr2r=r$r$r%Útest_no_update_existing-sz LDAPTest.test_no_update_existingc Cs||jdtdtjdƒtdddd|jjdd d }|jjd d d }|j|ƒ|j|ƒ|j |j j ƒd d d dd d d dgƒdS)Nzuid=%(user)s,ou=people,o=testzou=groups,o=testz(objectClass=groupOfNames)rH)Ú member_attrzcn=active_gon,ou=groups,o=test)r{Ú GROUP_SEARCHÚ GROUP_TYPEÚ REQUIRE_GROUPr2r3)r|r3r=r„r…Ú compare_s) r}rrwrłrrr~r°rœrrur‡)rr2r=r$r$r%Útest_require_group=s   zLDAPTest.test_require_groupcCsN|jdtdtjdƒtddd|jjddd }td ƒ}|j|j |j ƒƒdS) Nzuid=%(user)s,ou=people,o=testzou=query_groups,o=testz(objectClass=groupOfNames)rH)rě)r{rírîr2r3)r|r3z#cn=alice_gon,ou=query_groups,o=test) r}rrwrłrrr~rr•Úresolverş)rr2Úqueryr$r$r%Útest_simple_group_queryPs z LDAPTest.test_simple_group_querycCsP|jdtdtjdƒtddd|jjddd }td ƒ}|j|j |j ƒƒdS) Nzuid=%(user)s,ou=people,o=testzou=query_groups,o=testz(objectClass=groupOfNames)rH)rě)r{rírîr2r3)r|r3z#cn=alice_gon,ou=query_groups,o=test) r}rrwrłrrr~rr‹rňrş)rr2rór$r$r%Útest_negated_group_queryZs  z!LDAPTest.test_negated_group_querycCsx|jdtdtjdƒtddd|jjddd }|jjd dd }td ƒtd ƒB}|j|j |j ƒƒ|j|j |j ƒƒdS) Nzuid=%(user)s,ou=people,o=testzou=query_groups,o=testz(objectClass=groupOfNames)rH)rě)r{rírîr2r3)r|r3r=z#cn=alice_gon,ou=query_groups,o=testz!cn=bob_gon,ou=query_groups,o=test) r}rrwrłrrr~rr•rňrş)rr2r=rór$r$r%Útest_or_group_queryds  zLDAPTest.test_or_group_querycCsx|jdtdtjdƒtddd|jjddd }|jjd dd }td ƒtd ƒ@}|j|j |j ƒƒ|j |j |j ƒƒdS) Nzuid=%(user)s,ou=people,o=testzou=query_groups,o=testz(objectClass=groupOfNames)rH)rě)r{rírîr2r3)r|r3r=z#cn=alice_gon,ou=query_groups,o=testz$cn=mutual_gon,ou=query_groups,o=test) r}rrwrłrrr~rr•rňrşr‹)rr2r=rór$r$r%Útest_and_group_queryts  zLDAPTest.test_and_group_querycCs€|jdtdtjdƒtddd|jjddd }|jjd dd }td ƒtd ƒ@td ƒB}|j|j |j ƒƒ|j|j |j ƒƒdS)Nzuid=%(user)s,ou=people,o=testzou=query_groups,o=testz(objectClass=groupOfNames)rH)rě)r{rírîr2r3)r|r3r=z#cn=alice_gon,ou=query_groups,o=testz$cn=mutual_gon,ou=query_groups,o=testz!cn=bob_gon,ou=query_groups,o=test) r}rrwrłrrr~rr•rňrş)rr2r=rór$r$r%Útest_nested_group_query„s  z LDAPTest.test_nested_group_querycCsjtdƒtdƒ@}|jdtdtjdƒtdd|d|jjd d d }|jjd d d }|j|ƒ|j |ƒdS) Nz#cn=alice_gon,ou=query_groups,o=testz$cn=mutual_gon,ou=query_groups,o=testzuid=%(user)s,ou=people,o=testzou=query_groups,o=testz(objectClass=groupOfNames)rH)rě)r{rírîrďr2r3)r|r3r=) rr}rrwrłrrr~r°rœ)rrór2r=r$r$r%Ú!test_require_group_as_group_query—s   z*LDAPTest.test_require_group_as_group_querycCs||jdttdtjdƒtdtjdƒƒtdddd|jjd d d }|jjd d d }|j|ƒ|j |ƒ|j |j j d hƒdS)Nzuid=%(user)s,ou=people,o=testzou=groups,o=testz(objectClass=groupOfNames)zou=moregroups,o=testrH)rěz!cn=other_gon,ou=moregroups,o=test)r{rírîrďr2r3)r|r3r=rL) r}rrrwrłrrr~rœr°rrşÚ group_names)rr2r=r$r$r%Útest_group_unionŠs   zLDAPTest.test_group_unioncCs||jdttdtjdƒtdtjdƒƒtdddd|jjd d d }|jjd d d }|j|ƒ|j |ƒ|j |j j d hƒdS)Nzuid=%(user)s,ou=people,o=testzou=groups,o=testz(objectClass=groupOfNames)zou=moregroups,o=testrH)rěz!cn=other_gon,ou=moregroups,o=test)r{rírîrďr2r3)r|r3r=rL) r}rrrwrłrrr~rœr°rrşrú)rr2r=r$r$r%Útest_nested_group_unionťs   z LDAPTest.test_nested_group_unionc Csz|jdtdtjƒtdddd|jjddd }|jjd dd }|j|ƒ|j|ƒ|j |j j ƒd d d d d d d d gƒdS)Nzuid=%(user)s,ou=people,o=testzou=groups,o=testrH)rězcn=active_gon,ou=groups,o=test)r{rírîZ DENY_GROUPr2r3)r|r3r=r„r…rđ) r}rrwrłrrr~rœr°rrur‡)rr2r=r$r$r%Útest_denied_groupÍs   zLDAPTest.test_denied_groupcCs\|jdtdtjƒtddd|jjddd}|j|jj d d „|j |j |j |j gDƒƒdS) Nzuid=%(user)s,ou=people,o=testzou=groups,o=testrH)rě)r{rírîr2r3)r|r3cSsh|]}|djƒ’qS)r)Úlower)Ú.0Úgr$r$r%ú ęsz*LDAPTest.test_group_dns..)r}rrwrłrrr~rrşZ group_dnsrIrJrKrX)rr2r$r$r%Útest_group_dnsŕs zLDAPTest.test_group_dnscCsJ|jdtdtjƒtddd|jjddd}|j|jj d d d d hƒdS) Nzuid=%(user)s,ou=people,o=testzou=groups,o=testrH)rě)r{rírîr2r3)r|r3rIrJrKrX) r}rrwrłrrr~rrşrú)rr2r$r$r%Útest_group_namesís  zLDAPTest.test_group_namescCsœ|jdtdtjƒtddtdƒddgdd œd |jjd d d }|jjdd d }|j|j ƒ|j|j ƒ|j|j ƒ|j |j ƒ|j |j ƒ|j |j ƒdS)Nzuid=%(user)s,ou=people,o=testzou=groups,o=testrH)rězcn=active_gon,ou=groups,o=testzcn=empty_gon,ou=groups,o=testzcn=staff_gon,ou=groups,o=testz!cn=superuser_gon,ou=groups,o=test)Ú is_activeÚis_staffÚ is_superuser)r{rírîÚUSER_FLAGS_BY_GROUPr2r3)r|r3r=) r}rrwrłrrrr~r•rrrr‹)rr2r=r$r$r%Útest_dn_group_membership÷s        z!LDAPTest.test_dn_group_membershipc CsV|jdtdtjƒtddtdƒgddœd|jtƒ|jj d d d WdQRXdS) Nzuid=%(user)s,ou=people,o=testzou=groups,o=testrH)rězcn=active_gon,ou=groups,o=testz!cn=superuser_gon,ou=groups,o=test)rrr)r{rírîrr2r3)r|r3) r}rrwrłrrrçr rr~)rr$r$r%Útest_user_flags_misconfigureds   z&LDAPTest.test_user_flags_misconfiguredcCs|jdtdtjƒtƒddddœd|jjdd d }|jjd d d }|j|jƒ|j|j ƒ|j|j ƒ|j |jƒ|j |j ƒ|j |j ƒdS) Nzuid=%(user)s,ou=people,o=testzou=groups,o=testzcn=active_px,ou=groups,o=testzcn=staff_px,ou=groups,o=testz cn=superuser_px,ou=groups,o=test)rrr)r{rírîrr2r3)r|r3r=) r}rrwrłrrr~r•rrrr‹)rr2r=r$r$r%Útest_posix_memberships       zLDAPTest.test_posix_membershipcCs|jdtdtjƒtƒddddœd|jjdd d }|jjd d d }|j|jƒ|j|j ƒ|j|j ƒ|j |jƒ|j |j ƒ|j |j ƒdS) Nzuid=%(user)s,ou=people,o=testzou=groups,o=testzcn=active_nis,ou=groups,o=testzcn=staff_nis,ou=groups,o=testz!cn=superuser_nis,ou=groups,o=test)rrr)r{rírîrr2r3)r|r3r=) r}rrwrłrrr~r•rrrr‹)rr2r=r$r$r%Útest_nis_membership3s       zLDAPTest.test_nis_membershipcCsz|jdtdtjƒtdddddœd|jjdd d }|jjd d d }|j|jƒ|j|j ƒ|j |jƒ|j |j ƒdS) Nzuid=%(user)s,ou=people,o=testzou=groups,o=testrH)rězcn=parent_gon,ou=groups,o=test)rr)r{rírîrr2r3)r|r3r=) r}rrwrłrrr~r•rrr‹)rr2r=r$r$r%Útest_nested_dn_group_membershipIs     z(LDAPTest.test_nested_dn_group_membershipcCs@|jdtdtjƒtƒddid|jjddd}|j|jƒdS) Nzuid=%(user)s,ou=people,o=testzou=groups,o=testrzcn=active_px,ou=groups,o=test)r{rírîrr@r3)r|r3) r}rrwrłrrr~r‹r)rr@r$r$r%Útest_posix_missing_attributes[s  z&LDAPTest.test_posix_missing_attributescCs |jdtdtjƒtdddd|jƒtjjdd}|j j |j ƒ}|j |j j |ƒd d hƒ|j |j j|ƒd d hƒ|j|j j|d ƒƒ|j|j j|d ƒƒdS) Nzuid=%(user)s,ou=people,o=testzou=groups,o=testrH)rěT)r{rírîÚFIND_GROUP_PERMSr2)r|z auth.add_userzauth.change_userr–)r}rrwrłrÚ _init_groupsr r‰rŠrrĽrŞrÚget_group_permissionsÚget_all_permissionsr•Úhas_permÚhas_module_perms)rr2r$r$r%Útest_dn_group_permissionsis z"LDAPTest.test_dn_group_permissionscCsb|jdddtdtjƒtdddd|jƒtjjd d }|j j |j ƒ}|j |j j |ƒtƒƒdS) Nzuid=bob,ou=people,o=testrŽzuid=%(user)s,ou=people,o=testzou=groups,o=testrH)rěT)rÄrĹr{rírîrr2)r|)r}rrwrłrrr r‰rŠrrĽrŞrrÚset)rr2r$r$r%Ú!test_group_permissions_ldap_errorzs z*LDAPTest.test_group_permissions_ldap_errorcCsœ|jdtdtjƒtdddd|jƒtjjdd}|j j |j ƒ}|j |j j |ƒtƒƒ|j |j j|ƒtƒƒ|j|j j|d ƒƒ|j|j j|d ƒƒdS) Nzuid=%(user)s,ou=people,o=testzou=groups,o=testrH)rěT)r{rírîrr=)r|z auth.add_userr–)r}rrwrłrrr r‰rŠrrĽrŞrrrrr‹rr)rr=r$r$r%Útest_empty_group_permissionsŠs z%LDAPTest.test_empty_group_permissionscCsž|jdtdtjdƒtƒdd|jƒtjjdd}|j j |j ƒ}|j |j j |ƒdd hƒ|j |j j|ƒdd hƒ|j|j j|dƒƒ|j|j j|d ƒƒdS) Nzuid=%(user)s,ou=people,o=testzou=groups,o=testz(objectClass=posixGroup)T)r{rírîrr2)r|z auth.add_userzauth.change_userr–)r}rrwrłrrr r‰rŠrrĽrŞrrrr•rr)rr2r$r$r%Útest_posix_group_permissions›sz%LDAPTest.test_posix_group_permissionscCsÜ|jdtdtjdƒtƒdd|jƒ|jj|jdtj ddfgƒ|jj|j dtj dd gfgƒt j jd d }|jj|jƒ}|j|jj|ƒd d hƒ|j|jj|ƒd d hƒ|j|jj|d ƒƒ|j|jj|d ƒƒdS)Nzuid=%(user)s,ou=people,o=testzou=groups,o=testz(objectClass=posixGroup)T)r{rírîrrr:rDr2)r|z auth.add_userzauth.change_userr–)r}rrwrłrrruZmodify_sr2Z MOD_DELETErBZMOD_ADDr r‰rŠrrĽrŞrrrr•rr)rr2r$r$r%Ú#test_posix_group_permissions_no_gid­s z,LDAPTest.test_posix_group_permissions_no_gidcCsž|jdtdtjdƒtƒdd|jƒtjjdd}|j j |j ƒ}|j |j j |ƒdd hƒ|j |j j|ƒdd hƒ|j|j j|dƒƒ|j|j j|d ƒƒdS) Nzuid=%(user)s,ou=people,o=testzou=groups,o=testz(objectClass=nisNetgroup)T)r{rírîrr2)r|z auth.add_userzauth.change_userr–)r}rrwrłrrr r‰rŠrrĽrŞrrrr•rr)rr2r$r$r%Útest_nis_group_permissionsÁsz#LDAPTest.test_nis_group_permissionscCsP|jdtdtjƒtdddd|jƒtjjdd}|j |j j |ƒt ƒƒdS) Nzuid=%(user)s,ou=people,o=testzou=groups,o=testrH)rěT)r{rírîrr2)r|) r}rrwrłrrr r‰rŠrrrr)rr2r$r$r%Útest_foreign_user_permissionsÓs z&LDAPTest.test_foreign_user_permissionscCsÄ|jdtdtjƒtddddd|jƒtjjddj }tjjd dj }xVt d ƒD]J}|j j |ƒ}|j |j j|ƒd d hƒ|j j |ƒ}|j |j j|ƒtƒƒqTW|j |jjƒd ddd ddgƒdS)Nzuid=%(user)s,ou=people,o=testzou=groups,o=testrH)rěT)r{rírîrZ CACHE_GROUPSr2)r|r=éz auth.add_userzauth.change_userr„r…r´)r}rrwrłrrr r‰rŠrŞÚrangerrĽrrrrur‡)rZalice_idZbob_idÚir2r=r$r$r%Útest_group_cacheŕs(     zLDAPTest.test_group_cachecCsv|jdtdtjdƒtƒdd|jtjjƒdƒ|j j ddd }|jtjjƒd ƒ|jt |j j ƒƒt tjj ƒƒƒdS) Nzuid=%(user)s,ou=people,o=testzou=groups,o=testz(objectClass=posixGroup)T)r{rírîÚ MIRROR_GROUPSrr2r3)r|r3rÚ)r}rrwrłrrrr‰rŠrr~rr/Úall)rr2r$r$r%Útest_group_mirroring˙szLDAPTest.test_group_mirroringcCs‚|jdtdtjdƒtdddd|jjdd d }|jtt j j ƒj d dd ƒd dddddhƒ|jt|j j ƒƒtt j j ƒƒƒdS)Nzuid=%(user)s,ou=people,o=testzou=groups,o=testz(objectClass=groupOfNames)rH)rěT)r{rírîr r2r3)r|r3r!)ÚflatrIrJrKrXrWrY)r}rrwrłrrr~rrrr‰r!Ú values_listr/)rr2r$r$r%Útest_nested_group_mirrorings z$LDAPTest.test_nested_group_mirroringcCsŽ|jdtdtjdƒtƒddgdi}x.dd„td d ƒDƒD]}tjj|d ||<q6szALDAPTest.test_group_mirroring_whitelist_update..rr¸)r!r2rSr3)r|r3r!T)r#)r}rrwrłrrrr‰rŠrrÖr/rr~rr$)rr/r!r2r$r$r%Ú%test_group_mirroring_whitelist_update,s  z.LDAPTest.test_group_mirroring_whitelist_updatecCsŽ|jdtdtjdƒtƒddgdi}x.dd„td d ƒDƒD]}tjj|d ||<q.rr¸)r!r2rRr3)r|r3r!T)r#)r}rrwrłrrrr‰rŠrrÖr/rr~rr$)rr/r!r2r$r$r%Ú#test_group_mirroring_whitelist_noopBs  z,LDAPTest.test_group_mirroring_whitelist_noopcCsŽ|jdtdtjdƒtƒddgdi}x.dd„td d ƒDƒD]}tjj|d ||<q.rr¸)r!r2rSr3)r|r3r!T)r#rR)r}rrwrłrrrr‰rŠrrÖr/rr~rr$)rr/r!r2r$r$r%Ú%test_group_mirroring_blacklist_updateXs  z.LDAPTest.test_group_mirroring_blacklist_updatecCsŽ|jdtdtjdƒtƒddgdi}x.dd„td d ƒDƒD]}tjj|d ||<q.rr¸)r!r2rRr3)r|r3r!T)r#)r}rrwrłrrrr‰rŠrrÖr/rr~rr$)rr/r!r2r$r$r%Ú#test_group_mirroring_blacklist_noopns  z,LDAPTest.test_group_mirroring_blacklist_noopcCsT|jdtdtjƒtddddd|jƒtjjdd}|j |j j |ƒd d hƒdS) Nzuid=%(user)s,ou=people,o=testzou=groups,o=testrH)rěT)r{rírîrÚAUTHORIZE_ALL_USERSr2)r|z auth.add_userzauth.change_user) r}rrwrłrrr r‰rŠrrr)rr2r$r$r%Útest_authorize_external_users„s z&LDAPTest.test_authorize_external_userscCs\|jtdtjdƒtdtjƒtddddd|jƒtjjdd }|j |j j |ƒt ƒƒdS) Nzou=people,o=testz(uid=%(user)s)zou=groups,o=testrH)rěT)r˛rírîrr-z not-in-ldap)r|) r}rrwrłrrr r‰rŠrrrr)rr2r$r$r%Útest_authorize_external_unknown’s  z(LDAPTest.test_authorize_external_unknowncCsź|jdd|jjdƒ}|jjdƒ}|j|ƒ|j|jdƒ|j|jdƒ|j|jƒ|j |j ƒ|j |j ƒ|j|ƒ|j|jdƒ|j|jdƒ|j|jƒ|j |j ƒ|j |j ƒdS)Nzuid=%(user)s,ou=people,o=test)r{r2r=rż) r}rrÖr°rrËrĚr•rr‹rr)rr2r=r$r$r%Útest_create_without_auth˘s          z!LDAPTest.test_create_without_authc Csú|jdddddœtdtjƒtƒddd d œd tjjd d tjjdd |jj d ƒ}|jj dƒ}|j |ƒ|j |j dƒ|j |j dƒ|j|jƒ|j|jƒ|j|jƒ|j |ƒ|j |j dƒ|j |j dƒ|j|jƒ|j|jƒ|j|jƒdS)Nzuid=%(user)s,ou=people,o=testFr;r<)rËrĚzou=groups,o=testzcn=active_gon,ou=groups,o=testzcn=staff_gon,ou=groups,o=testz!cn=superuser_gon,ou=groups,o=test)rrr)r{ręr˘rírîrr2)r|r=r4r5r>r?)r}rrwrłrr r‰rŠrrÖr°rrËrĚr•rrrr‹)rr2r=r$r$r%Útest_populate_without_authˇs2           z#LDAPTest.test_populate_without_authcCs&|jdd|jjdƒ}|j|ƒdS)Nzuid=%(user)s,ou=people,o=test)r{rŽ)r}rrÖrœ)rrŽr$r$r%Útest_populate_bogus_userŘs z!LDAPTest.test_populate_bogus_usercCs>|jddd|j|jjƒ|jjddd|j|jjƒdS)Nzuid=%(user)s,ou=people,o=testF)r{Ú START_TLSr2r3)r|r3)r}r‹ruÚ tls_enabledrr~)rr$r$r%Útest_start_tls_missingás zLDAPTest.test_start_tls_missingcCs>|jddd|j|jjƒ|jjddd|j|jjƒdS)Nzuid=%(user)s,ou=people,o=testT)r{r3r2r3)r|r3)r}r‹rur4rr~r•)rr$r$r%Útest_start_tlsës zLDAPTest.test_start_tlscCs*|jtdtjdƒd|jjddddS)z: Make sure we're not phased by referrals. zou=people,o=testz(uid=%(user)s))r˛r2r3)r|r3N)r}rrwrłrr~)rr$r$r%Útest_null_search_resultsősz!LDAPTest.test_null_search_resultsc Csd|jttdtjdƒtdtjdƒƒd|jjddd}|j|ƒ|j|j j ƒdd d d d d d gƒdS) Nzou=groups,o=testz(uid=%(user)s)zou=people,o=test)r˛r2r3)r|r3r„r…ÚsearchÚresult) r}rrrwrłrr~r°rrur‡)rr2r$r$r%Útest_union_searchs   zLDAPTest.test_union_searchcCs<|jdd|jjddd}|j|ƒ|j|jjƒgƒdS)Nzuid=%(user)s,ou=people,o=test)r{r2rż)r|r3)r}rr~rœrrur‡)rr2r$r$r%Útest_deny_empty_passwords  z!LDAPTest.test_deny_empty_passwordcCsB|jddd|jjddd}|j|ƒ|j|jjƒddgƒdS) Nzuid=%(user)s,ou=people,o=testT)r{ÚPERMIT_EMPTY_PASSWORDr2rż)r|r3r„r…)r}rr~rœrrur‡)rr2r$r$r%Útest_permit_empty_passwords z#LDAPTest.test_permit_empty_passwordcCsB|jddd|jjddd}|j|ƒ|j|jjƒddgƒdS)Nzuid=%(user)s,ou=people,o=testT)r{r<r2)r|r3r„r…)r}rr~rœrrur‡)rr2r$r$r%Útest_permit_null_password)s z"LDAPTest.test_permit_null_passwordcCsĆ|jdtdtjƒtdddd|jƒ|jjddd }tj |tj ƒ}tj |ƒ}|j jj |j j_ |j|ƒ|j|jj|ƒd d hƒ|j|jj|ƒd d hƒ|j|jj|d ƒƒ|j|jj|d ƒƒdS) Nzuid=%(user)s,ou=people,o=testzou=groups,o=testrH)rěT)r{rírîrr2r3)r|r3z auth.add_userzauth.change_userr–)r}rrwrłrrrr~ÚpickleÚdumpsÚHIGHEST_PROTOCOLÚloadsrşÚsettingsr°rrrr•rr)rZalice0Zpickledr2r$r$r%Ú test_pickle7s   zLDAPTest.test_picklecCsPtdtjdddgƒ}|j|jƒ|j|jjddddtjdddgfifgƒdS)Nzou=people,o=testz (uid=alice)rŘrŮT)rƒr´)rrwrłÚexecuterurr‡)rr8r$r$r%Útest_search_attrlistLs   zLDAPTest.test_search_attrlistc s‚G‡fdd„dtjƒ‰ˆƒ|_|jddtjdd }tjdƒ|jjdd d WdQRX|jt|ƒd ƒ|j|d j t ƒdS) NcseZdZ‡‡fdd„Z‡ZS)z>LDAPTest.test_get_or_create_user_deprecated..MyBackendcstˆ|ƒj||ƒS)N)ÚsuperÚget_or_create_user)rr|rş)r‘Ú __class__r$r%rHVszQLDAPTest.test_get_or_create_user_deprecated..MyBackend.get_or_create_user)r'r(r)rHÚ __classcell__r$)r‘)rIr%r‘Usr‘zuid=%(user)s,ou=people,o=test)r{T)ÚrecordÚalwaysr2r3)r|r3rr) rrvr}rnÚcatch_warningsÚ simplefilterr~rÚlenrjÚDeprecationWarning)rÚwr$)r‘r%Ú"test_get_or_create_user_deprecatedTs  z+LDAPTest.test_get_or_create_user_deprecatedc CslGdd„dtjƒ}|ƒ|_|jddtjdd }tjdƒ|jjdd d WdQRX|jt|ƒd ƒdS) Nc@s eZdZdS)zPLDAPTest.test_custom_backend_without_get_or_create_no_warning..MyBackendN)r'r(r)r$r$r$r%r‘csr‘zuid=%(user)s,ou=people,o=test)r{T)rKrLr2r3)r|r3r) rrvr}rnrMrNr~rrO)rr‘rQr$r$r%Ú4test_custom_backend_without_get_or_create_no_warningbs  z=LDAPTest.test_custom_backend_without_get_or_create_no_warningcsNG‡fdd„dtjƒ‰ˆƒ|_|jdd|jjddd}|j|jjdƒdS) NcseZdZ‡‡fdd„Z‡ZS)zGLDAPTest.test_override_authenticate_access_ldap_user..MyBackendcsd|_tˆ|ƒj||ƒS)NÚbar)ÚfoorGÚauthenticate_ldap_user)rrşr3)r‘rIr$r%rVpsz^LDAPTest.test_override_authenticate_access_ldap_user..MyBackend.authenticate_ldap_user)r'r(r)rVrJr$)r‘)rIr%r‘osr‘zuid=%(user)s,ou=people,o=test)r{r2r3)r|r3rT)rrvr}r~rrşrU)rrŽr$)r‘r%Ú+test_override_authenticate_access_ldap_userns  z4LDAPTest.test_override_authenticate_access_ldap_usercKstf|Ž|j_dS)N)rrrC)rr r$r$r%r}}szLDAPTest._init_settingscCsntjjddtjjddg}tjjdd}|jj|Žtjjdd}|jj|Žtjjdd}|jj|ŽdS)NZadd_user)ÚcodenameZ change_userrI)r!rBrT)rr‰rrrŠÚ permissionsÚadd)rrYrIrBrTr$r$r%r€s   zLDAPTest._init_groupsN)†r'r(r)Útopr.r/r0r1r2r=rZdresslerr@rBrErFrGrIrJrKrLrMrNrOrPrQrRrSrTrUrVrWrXrYÚdictrmÚ classmethodrhrqrrrxrzr€rˆrr’r rrwrłr—r™rržrŸr¤r§rŤrŹr­rŻrąrśrźr˝rŔrÁrÂrĂrÉrĘrÍrÎrĎrŇrŐr×rŰrÜrărčrérërńrôrőrör÷rřrůrűrürýrrrr r r r r rrrrrrrrr"r%r(r)r+r,r.r/r0r1r2r5r6r7r:r;r=r>rDrFrRrSrWr}rr$r$r$r%r+Fsş                                                    !          r+)-Ú __future__rrrrÚcopyrr[r?ÚunittestrnrwÚdjango.contrib.auth.modelsrrr Údjango.core.cacher Údjango.core.exceptionsr Ú django.testr Údjango.test.utilsr Údjango.utils.encodingrrZrZdjango_auth_ldap.configrrrrrrrrÚmodelsrrlÚ ImportErrorZ LDAPSettingsrÚskipIfr+r$r$r$r%Ús,       (    django-auth-ldap-1.4.0/tests/__pycache__/models.cpython-35.pyc0000644000076600000240000000245613255034675024726 0ustar psagersstaff00000000000000 |`NZ™ă@s\ddlmZmZmZmZddlmZddlmZGdd„deƒZ dS)é)Úabsolute_importÚdivisionÚprint_functionÚunicode_literals)ÚAbstractBaseUser)Úmodelsc@seZdZejddddddƒZejƒZdZdd„Z d d „Z d d „Z d d„Z e e e ƒZdS)ÚTestUserÚ max_lengthé(ÚuniqueTÚdb_indexÚ identifiercCs|jS)N)r )ÚselfŠrú8/Users/psagers/Projects/django-auth-ldap/tests/models.pyÚ get_full_name szTestUser.get_full_namecCs|jS)N)r )rrrrÚget_short_nameszTestUser.get_short_namecCsdS)NZAlicer)rrrrÚget_first_nameszTestUser.get_first_namecCstdƒ‚dS)NzOops...)Ú Exception)rÚvaluerrrÚset_first_nameszTestUser.set_first_nameN)Ú__name__Ú __module__Ú __qualname__rÚ CharFieldr Ú IntegerFieldZ uid_numberÚUSERNAME_FIELDrrrrÚpropertyÚ first_namerrrrrs      rN) Ú __future__rrrrÚdjango.contrib.auth.modelsrÚ django.dbrrrrrrÚs"django-auth-ldap-1.4.0/tests/__pycache__/tests.cpython-34.pyc0000644000076600000240000015247613255034645024611 0ustar psagersstaff00000000000000î RŃŻZűůă @sŞddlmZmZmZmZddlmZddlZddlZddl Z ddl Z ddl Z ddl m Z mZmZddlmZddlmZddlmZddlmZdd lmZdd lmZdd lmZmZmZm Z m!Z!m"Z"m#Z#m$Z$d d l%m&Z&yddl'Z'Wne(k r^dZ'YnXGdd„dej)ƒZ*e j+e'dkdƒGdd„deƒƒZ,dS)é)Úabsolute_importÚdivisionÚprint_functionÚunicode_literals)ÚdeepcopyN)ÚGroupÚ PermissionÚUser)Úcache)ÚImproperlyConfigured)ÚTestCase)Úoverride_settings)Ú force_str)Úbackend)ÚGroupOfNamesTypeÚLDAPGroupQueryÚ LDAPSearchÚLDAPSearchUnionÚMemberDNGroupTypeÚNestedMemberDNGroupTypeÚ NISGroupTypeÚPosixGroupTypeé)ÚTestUserc@s"eZdZdZdd„ZdS)Ú TestSettingszb A replacement for backend.LDAPSettings that does not load settings from django.conf. cKsFx?|jjƒD].\}}|j||ƒ}t|||ƒqWdS)N)ÚdefaultsÚitemsÚgetÚsetattr)ÚselfÚkwargsÚnameÚdefaultÚvalueŠr$ú7/Users/psagers/Projects/django-auth-ldap/tests/tests.pyÚ__init__@szTestSettings.__init__N)Ú__name__Ú __module__Ú __qualname__Ú__doc__r&r$r$r$r%r;s rz4django_auth_ldap tests require the mockldap package.c@s´ eZdZdidd6fZdidd6fZdidd6fZd id d6fZd id d6fZd idgd6ddddgd6dgd6dgd6dgd6dgd6dgd6fZdidgd6ddddgd6dgd6d gd6d!gd6d"gd6d#gd6fZ e d$ƒie d%ƒgd6ddddgd6dgd6d&gd6d!gd6d'gd6e d(ƒgd6fZ d)id*gd6ddddgd6dgd6d+gd,6fZ d-id.gd/6d0gd6dgd6gd16fZ d2id3gd/6d0gd6d gd6dgd16fZd4id5gd/6d0gd6d&gd6dgd16fZd6id7gd/6d8gd6gd96fZd:id;gd/6d8gd6d gd96fZd<id=gd/6d8gd6d gd96fZd>id?gd/6d8gd6d gd96fZd@idAgd/6d8gd6dgd96fZdBidCgd/6d8gd6d gd96fZdDidEgd/6d8gd6d dgd96fZdFidGgd/6d8gd6dgd96fZdHidIgd/6d8gd6d gd96fZdJidKgd/6d8gd6gd96fZdLidMgd/6d8gd6d gd96fZdNidOgd/6d8gd6gd96fZdPidQgd/6dRgd6dSgdT6fZdUidVgd/6dRgd6dSgdT6fZdWidXgd/6dRgd6dSgdT6fZdYidZgd/6d8gd6d[gd96fZd\id]gd/6d8gd6d d^gd96fZ d^id_gd/6d8gd6dYgd96fZ!e"eeeeeee e e e eeeeeeeeeeeeeeeeeee e!gƒZ#e$d`da„ƒZ%e$dbdc„ƒZ&e$ddde„ƒZ'dfdg„Z(dhdi„Z)djdk„Z*dldm„Z+dndo„Z,dpdq„Z-e.dre/de0j1dsƒƒdtdu„ƒZ2e.dre/de0j1dsƒƒdvdw„ƒZ3dxdy„Z4dzd{„Z5d|d}„Z6e.d~dƒd€d„ƒZ7e.d~dƒd‚dƒ„ƒZ8e.d~dƒd„d…„ƒZ9d†d‡„Z:dˆd‰„Z;dŠd‹„Z<dŒd„Z=dŽd„Z>dd‘„Z?d’d“„Z@d”d•„ZAd–d—„ZBd˜d™„ZCdšd›„ZDdœd„ZEdždŸ„ZFd dĄ„ZGd˘dŁ„ZHd¤dĽ„ZIdŚd§„ZJe.d~dƒd¨dŠ„ƒZKe.d~dƒdŞdŤ„ƒZLdŹd­„ZMdŽdŻ„ZNd°dą„ZOd˛dł„ZPd´dľ„ZQdśdˇ„ZRd¸dš„ZSdşdť„ZTdźd˝„ZUdždż„ZVdŔdÁ„ZWdÂdÄZXdÄdńZYdĆdDŽZZdČdɄZ[dĘd˄Z\dĚd̈́Z]dÎdτZ^dĐdфZ_dŇdӄZ`dÔdՄZadÖdׄZbdŘdلZcdÚdۄZddÜd݄ZedŢd߄Zfdŕdá„Zgdâdă„Zhdädĺ„Zidćdç„Zjdčdé„Zkdędë„Zldědí„Zmdîdď„Zndđdń„Zodňdó„Zpdôdő„Zqdöd÷„Zrdřdů„Zsdúdű„Ztdüdý„Zudţd˙„Zvdd„Zwdd„Zxdd„Zydd„Zzdd „Z{d d „Z|d d „Z}dd„Z~dd„Zdd„Z€dd„Zdd„Z‚dd„Zƒdd„Z„dd„Z…dS(ÚLDAPTestzo=testÚtestÚozou=people,o=testÚpeopleZouzou=groups,o=testÚgroupszou=moregroups,o=testÚ moregroupszou=mirror_groups,o=testÚ mirror_groupszuid=alice,ou=people,o=testÚaliceÚuidZpersonZorganizationalPersonZ inetOrgPersonZ posixAccountZ objectClassÚpasswordZ userPasswordZ1000Ú uidNumberÚ gidNumberÚAliceÚ givenNameÚAdamsÚsnzuid=bob,ou=people,o=testÚbobZ1001Z50ÚRobertÚBarkeruuid=dreßler,ou=people,o=testudreßlerZ1002ZWolfganguDreßlerzuid=nobody,ou=people,o=testÚnobodyő²Z binaryAttrzcn=active_px,ou=groups,o=testÚ active_pxZcnZ posixGroupÚ memberUidzcn=staff_px,ou=groups,o=testÚstaff_pxz cn=superuser_px,ou=groups,o=testÚ superuser_pxzcn=empty_gon,ou=groups,o=testÚ empty_gonZ groupOfNamesÚmemberzcn=active_gon,ou=groups,o=testÚ active_gonzcn=staff_gon,ou=groups,o=testÚ staff_gonz!cn=superuser_gon,ou=groups,o=testÚ superuser_gonz!cn=other_gon,ou=moregroups,o=testÚ other_gonz#cn=alice_gon,ou=query_groups,o=testÚ alice_gonz$cn=mutual_gon,ou=query_groups,o=testÚ mutual_gonz!cn=bob_gon,ou=query_groups,o=testÚbob_gonz"cn=mirror1,ou=mirror_groups,o=testÚmirror1z"cn=mirror2,ou=mirror_groups,o=testÚmirror2z"cn=mirror3,ou=mirror_groups,o=testÚmirror3z"cn=mirror4,ou=mirror_groups,o=testÚmirror4zcn=active_nis,ou=groups,o=testÚ active_nisZ nisNetgroupz (,alice,)ZnisNetgroupTriplezcn=staff_nis,ou=groups,o=testÚ staff_nisz!cn=superuser_nis,ou=groups,o=testÚ superuser_niszcn=parent_gon,ou=groups,o=testÚ parent_gonzcn=nested_gon,ou=groups,o=testzCN=nested_gon,ou=groups,o=testÚ nested_gonz cn=circular_gon,ou=groups,o=testÚ circular_goncCshtjdƒ}tjdƒ}tjƒ}|jtjƒ|j|ƒ|j|ƒ|jtjƒdS)NÚdjango_auth_ldapz'LDAP auth - %(levelname)s - %(message)s) ÚloggingÚ getLoggerÚ FormatterÚ StreamHandlerÚsetLevelÚDEBUGÚ setFormatterÚ addHandlerÚCRITICAL)ÚclsÚloggerÚ formatterÚhandlerr$r$r%Úconfigure_loggerós   zLDAPTest.configure_loggercCs<|jƒtj|jƒ|_tjddtddƒdS)NÚignoreÚcategoryÚmodulezmockldap.ldapobject)reÚmockldapZMockLdapÚ directoryÚwarningsÚfilterwarningsÚUnicodeWarning)rar$r$r%Ú setUpClass˙s zLDAPTest.setUpClasscCs |`dS)N)ri)rar$r$r%Ú tearDownClassszLDAPTest.tearDownClasscCsDtjƒ|jjƒ|jd|_tjƒ|_|jjdS)Nzldap://localhost)r ÚclearriÚstartÚldapobjrÚ LDAPBackendÚldap)rr$r$r%ÚsetUp s   zLDAPTest.setUpcCs|jjƒ|`dS)N)riÚstoprr)rr$r$r%ÚtearDowns zLDAPTest.tearDowncCsV|jdddidd6ƒ|jjddddƒ|j|jjdƒdƒdS) NÚUSER_DN_TEMPLATEzuid=%(user)s,ou=people,o=testZCONNECTION_OPTIONSZvalue1Zopt1Úusernamer2r4)Ú_init_settingsrÚ authenticateÚ assertEqualrrZ get_option)rr$r$r%Ú test_optionss  zLDAPTest.test_optionscCsz|jddd„ddƒ|jjddddƒ|jd }|j|jd d ƒd difd difgƒdS)NZ SERVER_URIcSsdS)Nzldap://ldap.example.comr$r$r$r$r%Ú(sz3LDAPTest.test_callable_server_uri..rxzuid=%(user)s,ou=people,o=testryr2r4úldap://ldap.example.comÚ with_argsTÚ initializeÚ simple_bind_súuid=alice,ou=people,o=test)r)rƒr4)rzrr{rir|Úmethods_called)rrrr$r$r%Útest_callable_server_uri&s    z!LDAPTest.test_callable_server_uricCs |jddƒtjjƒ}|jjddddƒ}|j|jƒƒ|j|j dƒ|jtjjƒ|dƒ|j|j j ƒddgƒdS) Nrxzuid=%(user)s,ou=people,o=testryr2r4rrr‚) rzr ÚobjectsÚcountrr{Ú assertFalseÚhas_usable_passwordr|ryrrr„)rÚ user_countÚuserr$r$r%Útest_simple_bind5s  zLDAPTest.test_simple_bindcCsľGdd„dtjƒ}|ƒ|_tjjƒ}|jjddddƒ}|j|jƒƒ|j|j dƒ|jtjjƒ|dƒ|j|j j ƒddgƒdS) Nc@seZdZidd6ZdS)z1LDAPTest.test_default_settings..MyBackendzuid=%(user)s,ou=people,o=testrxN)r'r(r)Údefault_settingsr$r$r$r%Ú MyBackendFs rŽryr2r4rrr‚) rrsr r†r‡r{rˆr‰r|ryrrr„)rrŽrŠr‹r$r$r%Útest_default_settingsEs  zLDAPTest.test_default_settingsZAUTH_LDAP_USER_SEARCHz(uid=%(user)s)cCs,|jjddddƒ}|j|ƒdS)Nryr2r4)ÚclientÚloginÚ assertTrue)rÚauthr$r$r%Ú&test_login_with_multiple_auth_backendsXsz/LDAPTest.test_login_with_multiple_auth_backendscCs,|jjddddƒ}|j|ƒdS)NryÚinvalidr4Zi_do_not_exist)rr‘rˆ)rr“r$r$r%Ú*test_bad_login_with_multiple_auth_backends]sz3LDAPTest.test_bad_login_with_multiple_auth_backendscCss|jddƒ|jjddddƒ}|j|ƒ|j|jjddƒdd ifd difgƒd S)z. Bind with a username that requires escaping. rxzuid=%(user)s,ou=people,o=testryzalice,1r4r€Trúldap://localhostr‚úuid=alice\,1,ou=people,o=testN)r—)r˜r4)rzrr{Ú assertIsNoner|rrr„)rr‹r$r$r%Útest_simple_bind_escapedbs   z!LDAPTest.test_simple_bind_escapedcCs |jddƒtjjƒ}|jjddddƒ}|j|jƒƒ|j|j dƒ|jtjjƒ|dƒ|j|j j ƒdd gƒdS) Nrxzuid=%(user)s,ou=people,o=testryr7r4r2rrr‚) rzr r†r‡rr{rˆr‰r|ryrrr„)rrŠr‹r$r$r%Útest_new_user_lowercaseqs  z LDAPTest.test_new_user_lowercasecCs;|jddƒ|jjddddƒ}t|ƒ}dS)Nrxzuid=%(user)s,ou=people,o=testryr7r4)rzrr{r)rr‹r$r$r%Ú test_deepcopys zLDAPTest.test_deepcopyÚAUTH_USER_MODELztests.TestUsercCsL|jdddidd6ƒ|jjddddƒ}|j|tƒdS) Nrxzuid=%(user)s,ou=people,o=testÚ USER_ATTR_MAPr5Ú uid_numberryr7r4)rzrr{ÚassertIsInstancer)rr‹r$r$r%Útest_auth_custom_user‰s  zLDAPTest.test_auth_custom_usercCsa|jdddidd6ƒ|jjddddƒ}|jj|jƒ}|j|tƒdS) Nrxzuid=%(user)s,ou=people,o=testržr5rŸryr7r4)rzrr{Úget_userÚidr r)rr‹r$r$r%Útest_get_custom_user”s  zLDAPTest.test_get_custom_usercCsƒ|jdddidd6ddƒtjjdddd ƒ}|jjd d d d ƒ}|j|tƒ|j|j|jƒdS) Nrxzuid=%(user)s,ou=people,o=testržr5rŸZUSER_QUERY_FIELDÚ identifierZabcdefičryr7r4) rzrr†Úcreaterr{r r|Úpk)rr2r‹r$r$r%Útest_get_custom_field s  zLDAPTest.test_get_custom_fieldcCsœ|jddƒtjjƒ}|jjddddƒ}|jjddddƒ}|j|jƒƒ|j|j dƒ|jtjjƒ|dƒdS) Nrxzuid=%(user)s,ou=people,o=testryz alicer4zalice r2r) rzr r†r‡rr{rˆr‰r|ry)rrŠr‹r$r$r%Útest_new_user_whitespaceŽs z!LDAPTest.test_new_user_whitespacecCsƒ|jddƒtjjƒ}|jjddddƒ}|j|ƒ|jtjjƒ|ƒ|j|jj ƒddgƒdS)Nrxzuid=%(user)s,ou=people,o=testryZ evil_alicer4rr‚) rzr r†r‡rr{r™r|rrr„)rrŠr‹r$r$r%Útest_simple_bind_bad_userťs   z"LDAPTest.test_simple_bind_bad_usercCsƒ|jddƒtjjƒ}|jjddddƒ}|j|ƒ|jtjjƒ|ƒ|j|jj ƒddgƒdS) Nrxzuid=%(user)s,ou=people,o=testryr2r4Úbogusrr‚) rzr r†r‡rr{r™r|rrr„)rrŠr‹r$r$r%Útest_simple_bind_bad_passwordĘs   z&LDAPTest.test_simple_bind_bad_passwordcCsw|jddƒtjjddƒtjjƒ}|jjddddƒ}|j|ƒ|jtjjƒ|ƒdS)Nrxzuid=%(user)s,ou=people,o=testryr2r4) rzr r†rŚr‡rr{ÚassertIsNotNoner|)rrŠr‹r$r$r%Útest_existing_userŮs  zLDAPTest.test_existing_usercCsľ|jdtdtjdƒƒ|jjjdtjddƒ|jgƒtj j ddƒ|j j ddddƒ}|j |ƒ|j|jdƒ|jtj jƒd ƒdS) NÚ USER_SEARCHzou=people,o=testz(uid=%(user)s)z (uid=Alice)ryr2r7r4r)rzrrtÚ SCOPE_SUBTREErrÚsearch_sÚseedr2r r†rŚrr{r­r|ryr‡)rr‹r$r$r%Útest_existing_user_insensitivećs  z'LDAPTest.test_existing_user_insensitivecCs Gdd„dtjƒ}|ƒ|_|jddƒtjjƒ}|jjddddƒ}|jj|jƒ}|j tjjƒ|dƒ|j |j d ƒ|j |j j dƒ|j |j dƒ|j |j d ƒ|j |j j dƒ|j |j dƒdS) Nc@s(eZdZdd„Zdd„ZdS)z1LDAPTest.test_convert_username..MyBackendcSsd|S)Nzldap_%sr$)rryr$r$r%Úldap_to_django_usernameůszILDAPTest.test_convert_username..MyBackend.ldap_to_django_usernamecSs|dd…S)Nér$)rryr$r$r%Údjango_to_ldap_usernameüszILDAPTest.test_convert_username..MyBackend.django_to_ldap_usernameN)r'r(r)r´rśr$r$r$r%rŽřs  rŽrxzuid=%(user)s,ou=people,o=testryr2r4rZ ldap_alice)rrsrzr r†r‡r{r˘r§r|ryÚ ldap_userZ _usernameZ ldap_username)rrŽrŠZuser1Zuser2r$r$r%Útest_convert_username÷s  zLDAPTest.test_convert_usernamecCsœ|jdtdtjdƒƒtjjƒ}|jjddddƒ}|j |ƒ|j tjjƒ|dƒ|j |j j ƒdd d d gƒdS) NrŻzou=people,o=testz(uid=%(user)s)ryr2r4rrr‚rą) rzrrtr°r r†r‡rr{r­r|rrr„)rrŠr‹r$r$r%Útest_search_binds   zLDAPTest.test_search_bindc Cs|jdtdtjdƒƒ|jjddddƒ}|j|ƒ|j|jj ddƒd difd difd dtjddfifgƒdS)z/ Search for a username that requires escaping. rŻzou=people,o=testz(uid=%(user)s)ryzalice*r4r€Trúldap://localhostr‚Úrąz(uid=alice\2a)N)rş)rťrť) rzrrtr°rr{r™r|rrr„)rr‹r$r$r%Útest_search_bind_escaped!s    z!LDAPTest.test_search_bind_escapedcCsm|jdtdtjdƒƒ|jjddddƒ}|j|ƒ|j|jj ƒddd gƒdS) NrŻzou=people,o=testz (cn=%(user)s)ryr2r4rr‚rą) rzrrtr°rr{r™r|rrr„)rr‹r$r$r%Útest_search_bind_no_user3s   z!LDAPTest.test_search_bind_no_usercCsm|jdtdtjdƒƒ|jjddddƒ}|j|ƒ|j|jj ƒddd gƒdS) NrŻzou=people,o=testz(uid=*)ryr2r4rr‚rą) rzrrtr°rr{r™r|rrr„)rr‹r$r$r%Útest_search_bind_multiple_usersBs   z(LDAPTest.test_search_bind_multiple_userscCsp|jdtdtjdƒƒ|jjddddƒ}|j|ƒ|j|jj ƒdd d d gƒdS) NrŻzou=people,o=testz(uid=%(user)s)ryr2r4rŤrr‚rą) rzrrtr°rr{r™r|rrr„)rr‹r$r$r%Útest_search_bind_bad_passwordQs   z&LDAPTest.test_search_bind_bad_passwordc CsŇ|jdddddtdtjdƒƒ|jjdd ddƒ}|j|ƒ|j|jƒ|j|jj |j d ƒ|j|jj tj j |j d ƒƒ|j|j jƒd d dd gƒdS)NÚBIND_DNzuid=bob,ou=people,o=testÚ BIND_PASSWORDr4rŻzou=people,o=testz(uid=%(user)s)ryr2rrrr‚rą)rzrrtr°rr{r­rˇr|Údnr2ÚattrsÚcidictrrr„)rr‹r$r$r%Ú!test_search_bind_with_credentials`s  ) z*LDAPTest.test_search_bind_with_credentialsc Csv|jdddddtdtjdƒƒ|jjdd d d ƒ}|j|ƒ|j|jj ƒd d gƒdS) NrŔzuid=bob,ou=people,o=testrÁrŤrŻzou=people,o=testz(uid=%(user)s)ryr2r4rr‚) rzrrtr°rr{r™r|rrr„)rr‹r$r$r%Ú%test_search_bind_with_bad_credentialsts   z.LDAPTest.test_search_bind_with_bad_credentialscCsv|jdddidd6dd6ƒ|jjdd d d ƒ}|j|ƒ|j|jd ƒ|j|jd ƒdS) Nrxzuid=%(user)s,ou=people,o=testržr8Ú first_namer:Ú last_nameryudreßlerr4uDreßler)rzrr{r­r|ryrČ)rr‹r$r$r%Útest_unicode_user…s  zLDAPTest.test_unicode_usercCsK|jddƒ|jjddddƒ}|j|jjtjjƒdS)Nrxzuid=%(user)s,ou=people,o=testryr2r4)rzrr{r rˇrĂrtrÄ)rr‹r$r$r%Ú test_cidicts zLDAPTest.test_cidictcCsĄ|jdddidd6dd6ƒ|jjdd d d ƒ}|j|jd ƒ|j|jd ƒ|j|jd ƒ|j|jjƒd dddgƒdS)Nrxzuid=%(user)s,ou=people,o=testržr8rÇr:rČryr2r4r7r9rr‚rą) rzrr{r|ryrÇrČrrr„)rr‹r$r$r%Útest_populate_user™s  zLDAPTest.test_populate_usercCs–|jdddidd6dd6dd 6ƒ|jjd d d d ƒ}|j|jd ƒ|j|jd ƒ|j|jdƒ|j|jdƒdS)Nrxzuid=%(user)s,ou=people,o=testržr8rÇr:rČÚmailÚemailryr2r4r7r9rť)rzrr{r|ryrÇrČrÍ)rr‹r$r$r%Ú)test_populate_user_with_missing_attributeŤs  z2LDAPTest.test_populate_user_with_missing_attributec CsZ|jdddidd6dd6ƒ|jtdƒ|jjd d d d ƒWdQXdS) Nrxzuid=%(user)s,ou=people,o=testržr8rÇr5rŸzOops...ryr2r4)rzÚassertRaisesMessageÚ Exceptionrr{)rr$r$r%Ú4test_authenticate_with_buggy_setter_raises_exceptionťs  z=LDAPTest.test_authenticate_with_buggy_setter_raises_exceptionc CsQ|jdddidd6dd6ƒ|jtdƒ|jjd ƒWdQXdS) Nrxzuid=%(user)s,ou=people,o=testržr8rÇr5rŸzOops...r2)rzrĎrĐrÚ populate_user)rr$r$r%Ú5test_populate_user_with_buggy_setter_raises_exceptionČs  z>LDAPTest.test_populate_user_with_buggy_setter_raises_exceptioncCsČ|jdddidd6dd6dd d gƒ|jjd d d d ƒ}|j|jd ƒ|j|jjƒddddgƒ|j|jjddƒdddtjdd d gfifƒdS)Nrxzuid=%(user)s,ou=people,o=testržr8rÇr:rČZ USER_ATTRLISTÚ*ú+ryr2r4rr‚rąr€Tézuid=alice,ou=people,o=testz(objectClass=*)) rzrr{r|ryrrr„rtZ SCOPE_BASE)rr‹r$r$r%Útest_populate_with_attrlistŐs   z$LDAPTest.test_populate_with_attrlistcCs¤|jdddidd6dd6dd ƒ|jjd d d d ƒ}|j|jd ƒ|j|jd ƒ|j|jdƒ|j|jjƒdddgƒdS)Nrxzuid=%(user)s,ou=people,o=testržr8rÇr:rČZBIND_AS_AUTHENTICATING_USERTryr2r4r7r9rr‚rą) rzrr{r|ryrÇrČrrr„)rr‹r$r$r%Útest_bind_as_useręs  zLDAPTest.test_bind_as_usercsqˆjddƒ‡fdd†}tjj|ƒˆjjddddƒ}ˆj|jƒtjj|ƒdS)Nrxzuid=%(user)s,ou=people,o=testcs1ˆjd|ƒˆjd|ƒd|d_dS)Nr‹rˇT)ÚassertInÚpopulate_user_handled)Úsenderr )rr$r%Úhandle_populate_usersz@LDAPTest.test_signal_populate_user..handle_populate_userryr2r4)rzrrŇÚconnectr{r’rÚÚ disconnect)rrÜr‹r$)rr%Útest_signal_populate_userýs z"LDAPTest.test_signal_populate_userc s“ˆjdddddtdtjdƒƒ‡fdd †}tjj|ƒˆjtjƒˆjj d d d d ƒWdQXtjj |ƒdS) NrŔzuid=bob,ou=people,o=testrÁrŤrŻzou=people,o=testz(uid=%(user)s)cs"ˆj|ddƒ|d‚dS)NÚcontextr{Ú exception)r|)rŰr )rr$r%Úhandle_ldap_errorsz?LDAPTest.test_auth_signal_ldap_error..handle_ldap_errorryr2r4) rzrrtr°rZ ldap_errorrÝÚ assertRaisesZ LDAPErrorr{rŢ)rrâr$)rr%Útest_auth_signal_ldap_errors z$LDAPTest.test_auth_signal_ldap_errorc CsN|jdddddtdtjdƒƒ|jjdƒ}|j|ƒdS) NrŔzuid=bob,ou=people,o=testrÁrŤrŻzou=people,o=testz(uid=%(user)s)r2)rzrrtr°rrŇr™)rr‹r$r$r%Útest_populate_signal_ldap_error s z(LDAPTest.test_populate_signal_ldap_errorcCsĎ|jdddidd6dd6dd ƒtjjd d dd dd ƒ|jjd d ddƒ}|jjd dddƒ}|j|jd ƒ|j|jd ƒ|j|jdƒ|j|jdƒdS)Nrxzuid=%(user)s,ou=people,o=testržr8rÇr:rČÚALWAYS_UPDATE_USERFryr2ZAliciaZAstror4r;r<r=) rzr r†rŚrr{r|rÇrČ)rr2r;r$r$r%Útest_no_update_existing-s z LDAPTest.test_no_update_existingc Csż|jdddtdtjdƒdtddƒd d ƒ|jjd d d d ƒ}|jjd dd d ƒ}|j|ƒ|j|ƒ|j |j j ƒddddddddgƒdS)Nrxzuid=%(user)s,ou=people,o=testÚ GROUP_SEARCHzou=groups,o=testz(objectClass=groupOfNames)Ú GROUP_TYPEÚ member_attrrEÚ REQUIRE_GROUPzcn=active_gon,ou=groups,o=testryr2r4r;rr‚Ú compare_s) rzrrtr°rrr{r­r™r|rrr„)rr2r;r$r$r%Útest_require_group=s     zLDAPTest.test_require_groupc Csx|jdddtdtjdƒdtddƒƒ|jjd d d d ƒ}td ƒ}|j|j |j ƒƒdS) Nrxzuid=%(user)s,ou=people,o=testrčzou=query_groups,o=testz(objectClass=groupOfNames)réręrEryr2r4z#cn=alice_gon,ou=query_groups,o=test) rzrrtr°rrr{rr’Úresolverˇ)rr2Úqueryr$r$r%Útest_simple_group_queryPs  z LDAPTest.test_simple_group_queryc Csy|jdddtdtjdƒdtddƒƒ|jjd d d d ƒ}td ƒ}|j|j |j ƒƒdS) Nrxzuid=%(user)s,ou=people,o=testrčzou=query_groups,o=testz(objectClass=groupOfNames)réręrEryr2r4z#cn=alice_gon,ou=query_groups,o=test) rzrrtr°rrr{rrˆrîrˇ)rr2rďr$r$r%Útest_negated_group_queryZs  z!LDAPTest.test_negated_group_queryc Csś|jdddtdtjdƒdtddƒƒ|jjd d d d ƒ}|jjd d d d ƒ}td ƒtdƒB}|j|j |j ƒƒ|j|j |j ƒƒdS)Nrxzuid=%(user)s,ou=people,o=testrčzou=query_groups,o=testz(objectClass=groupOfNames)réręrEryr2r4r;z#cn=alice_gon,ou=query_groups,o=testz!cn=bob_gon,ou=query_groups,o=test) rzrrtr°rrr{rr’rîrˇ)rr2r;rďr$r$r%Útest_or_group_queryds   zLDAPTest.test_or_group_queryc Csś|jdddtdtjdƒdtddƒƒ|jjd d d d ƒ}|jjd d d d ƒ}td ƒtdƒ@}|j|j |j ƒƒ|j |j |j ƒƒdS)Nrxzuid=%(user)s,ou=people,o=testrčzou=query_groups,o=testz(objectClass=groupOfNames)réręrEryr2r4r;z#cn=alice_gon,ou=query_groups,o=testz$cn=mutual_gon,ou=query_groups,o=test) rzrrtr°rrr{rr’rîrˇrˆ)rr2r;rďr$r$r%Útest_and_group_queryts   zLDAPTest.test_and_group_queryc CsŔ|jdddtdtjdƒdtddƒƒ|jjd d d d ƒ}|jjd d d d ƒ}td ƒtdƒ@tdƒB}|j|j |j ƒƒ|j|j |j ƒƒdS)Nrxzuid=%(user)s,ou=people,o=testrčzou=query_groups,o=testz(objectClass=groupOfNames)réręrEryr2r4r;z#cn=alice_gon,ou=query_groups,o=testz$cn=mutual_gon,ou=query_groups,o=testz!cn=bob_gon,ou=query_groups,o=test) rzrrtr°rrr{rr’rîrˇ)rr2r;rďr$r$r%Útest_nested_group_query„s    z LDAPTest.test_nested_group_queryc Cs¤tdƒtdƒ@}|jdddtdtjdƒdtd d ƒd |ƒ|jjd d ddƒ}|jjd dddƒ}|j|ƒ|j |ƒdS)Nz#cn=alice_gon,ou=query_groups,o=testz$cn=mutual_gon,ou=query_groups,o=testrxzuid=%(user)s,ou=people,o=testrčzou=query_groups,o=testz(objectClass=groupOfNames)réręrErëryr2r4r;) rrzrrtr°rrr{r­r™)rrďr2r;r$r$r%Ú!test_require_group_as_group_query—s    z*LDAPTest.test_require_group_as_group_queryc Csż|jdddttdtjdƒtdtjdƒƒdtdd ƒd d ƒ|jjd d ddƒ}|jjd dddƒ}|j|ƒ|j |ƒ|j |j j dhƒdS)Nrxzuid=%(user)s,ou=people,o=testrčzou=groups,o=testz(objectClass=groupOfNames)zou=moregroups,o=testréręrErëz!cn=other_gon,ou=moregroups,o=testryr2r4r;rI) rzrrrtr°rrr{r™r­r|rˇÚ group_names)rr2r;r$r$r%Útest_group_unionŠs   zLDAPTest.test_group_unionc Csż|jdddttdtjdƒtdtjdƒƒdtdd ƒd d ƒ|jjd d ddƒ}|jjd dddƒ}|j|ƒ|j |ƒ|j |j j dhƒdS)Nrxzuid=%(user)s,ou=people,o=testrčzou=groups,o=testz(objectClass=groupOfNames)zou=moregroups,o=testréręrErëz!cn=other_gon,ou=moregroups,o=testryr2r4r;rI) rzrrrtr°rrr{r™r­r|rˇrö)rr2r;r$r$r%Útest_nested_group_unionťs   z LDAPTest.test_nested_group_unionc Csź|jdddtdtjƒdtddƒdd ƒ|jjd d d d ƒ}|jjd d d d ƒ}|j|ƒ|j|ƒ|j |j j ƒddddddddgƒdS)Nrxzuid=%(user)s,ou=people,o=testrčzou=groups,o=testréręrEZ DENY_GROUPzcn=active_gon,ou=groups,o=testryr2r4r;rr‚rě) rzrrtr°rrr{r™r­r|rrr„)rr2r;r$r$r%Útest_denied_groupÍs     zLDAPTest.test_denied_groupc Cs‹|jdddtdtjƒdtddƒƒ|jjdd d d ƒ}|j|jj d d „|j |j |j |j gDƒƒdS) Nrxzuid=%(user)s,ou=people,o=testrčzou=groups,o=testréręrEryr2r4cSs h|]}|djƒ’qS)r)Úlower)Ú.0Úgr$r$r%ú ęs z*LDAPTest.test_group_dns..)rzrrtr°rrr{r|rˇZ group_dnsrFrGrHrU)rr2r$r$r%Útest_group_dnsŕs  zLDAPTest.test_group_dnsc Csr|jdddtdtjƒdtddƒƒ|jjdd d d ƒ}|j|jj d d d dhƒdS)Nrxzuid=%(user)s,ou=people,o=testrčzou=groups,o=testréręrEryr2r4rFrGrHrU) rzrrtr°rrr{r|rˇrö)rr2r$r$r%Útest_group_namesís  zLDAPTest.test_group_namesc Csň|jdddtdtjƒdtddƒditd ƒd 6d d gd 6dd6ƒ|jjddddƒ}|jjddddƒ}|j|j ƒ|j|j ƒ|j|j ƒ|j |j ƒ|j |j ƒ|j |j ƒdS)Nrxzuid=%(user)s,ou=people,o=testrčzou=groups,o=testréręrEÚUSER_FLAGS_BY_GROUPzcn=active_gon,ou=groups,o=testÚ is_activezcn=empty_gon,ou=groups,o=testzcn=staff_gon,ou=groups,o=testÚis_staffz!cn=superuser_gon,ou=groups,o=testÚ is_superuserryr2r4r;) rzrrtr°rrrr{r’rrrrˆ)rr2r;r$r$r%Útest_dn_group_membership÷s"    z!LDAPTest.test_dn_group_membershipc Cs…|jdddtdtjƒdtddƒditd ƒd 6gd 6d d 6ƒ|jtƒ|jj ddddƒWdQXdS)Nrxzuid=%(user)s,ou=people,o=testrčzou=groups,o=testréręrErzcn=active_gon,ou=groups,o=testrrz!cn=superuser_gon,ou=groups,o=testrryr2r4) rzrrtr°rrrăr rr{)rr$r$r%Útest_user_flags_misconfigureds   z&LDAPTest.test_user_flags_misconfiguredc Csŕ|jdddtdtjƒdtƒdidd6d d 6d d 6ƒ|jjd dddƒ}|jjd dddƒ}|j|jƒ|j|j ƒ|j|j ƒ|j |jƒ|j |j ƒ|j |j ƒdS)Nrxzuid=%(user)s,ou=people,o=testrčzou=groups,o=testrérzcn=active_px,ou=groups,o=testrzcn=staff_px,ou=groups,o=testrz cn=superuser_px,ou=groups,o=testrryr2r4r;) rzrrtr°rrr{r’rrrrˆ)rr2r;r$r$r%Útest_posix_memberships    zLDAPTest.test_posix_membershipc Csŕ|jdddtdtjƒdtƒdidd6d d 6d d 6ƒ|jjd dddƒ}|jjd dddƒ}|j|jƒ|j|j ƒ|j|j ƒ|j |jƒ|j |j ƒ|j |j ƒdS)Nrxzuid=%(user)s,ou=people,o=testrčzou=groups,o=testrérzcn=active_nis,ou=groups,o=testrzcn=staff_nis,ou=groups,o=testrz!cn=superuser_nis,ou=groups,o=testrryr2r4r;) rzrrtr°rrr{r’rrrrˆ)rr2r;r$r$r%Útest_nis_membership3s    zLDAPTest.test_nis_membershipc Csż|jdddtdtjƒdtddƒdid d 6d d 6ƒ|jjd d ddƒ}|jjd dddƒ}|j|jƒ|j|j ƒ|j |jƒ|j |j ƒdS)Nrxzuid=%(user)s,ou=people,o=testrčzou=groups,o=testréręrErzcn=parent_gon,ou=groups,o=testrrryr2r4r;) rzrrtr°rrr{r’rrrˆ)rr2r;r$r$r%Útest_nested_dn_group_membershipIs  z(LDAPTest.test_nested_dn_group_membershipc Csg|jdddtdtjƒdtƒdidd6ƒ|jjd d d d ƒ}|j|jƒdS) Nrxzuid=%(user)s,ou=people,o=testrčzou=groups,o=testrérzcn=active_px,ou=groups,o=testrryr>r4) rzrrtr°rrr{rˆr)rr>r$r$r%Útest_posix_missing_attributes[s   z&LDAPTest.test_posix_missing_attributesc Csë|jdddtdtjƒdtddƒdd ƒ|jƒtjjd d ƒ}|j j |j ƒ}|j |j j |ƒd d hƒ|j |j j|ƒd d hƒ|j|j j|d ƒƒ|j|j j|dƒƒdS)Nrxzuid=%(user)s,ou=people,o=testrčzou=groups,o=testréręrEÚFIND_GROUP_PERMSTryr2z auth.add_userzauth.change_userr“)rzrrtr°rÚ _init_groupsr r†rŚrr˘r§r|Úget_group_permissionsÚget_all_permissionsr’Úhas_permÚhas_module_perms)rr2r$r$r%Útest_dn_group_permissionsis  ""z"LDAPTest.test_dn_group_permissionsc Csš|jdddddddtdtjƒd td d ƒd d ƒ|jƒtjjddƒ}|j j |j ƒ}|j |j j |ƒtƒƒdS)NrŔzuid=bob,ou=people,o=testrÁrŤrxzuid=%(user)s,ou=people,o=testrčzou=groups,o=testréręrEr Tryr2)rzrrtr°rr r r†rŚrr˘r§r|r Úset)rr2r$r$r%Ú!test_group_permissions_ldap_errorzs  z*LDAPTest.test_group_permissions_ldap_errorc Csĺ|jdddtdtjƒdtddƒdd ƒ|jƒtjjd d ƒ}|j j |j ƒ}|j |j j |ƒtƒƒ|j |j j|ƒtƒƒ|j|j j|d ƒƒ|j|j j|d ƒƒdS)Nrxzuid=%(user)s,ou=people,o=testrčzou=groups,o=testréręrEr Tryr;z auth.add_userr“)rzrrtr°rr r r†rŚrr˘r§r|r rr rˆrr)rr;r$r$r%Útest_empty_group_permissionsŠs  z%LDAPTest.test_empty_group_permissionsc Csč|jdddtdtjdƒdtƒddƒ|jƒtjjd d ƒ}|j j |j ƒ}|j |j j |ƒd d hƒ|j |j j|ƒd d hƒ|j|j j|d ƒƒ|j|j j|d ƒƒdS)Nrxzuid=%(user)s,ou=people,o=testrčzou=groups,o=testz(objectClass=posixGroup)rér Tryr2z auth.add_userzauth.change_userr“)rzrrtr°rr r r†rŚrr˘r§r|r r r’rr)rr2r$r$r%Útest_posix_group_permissions›s     ""z%LDAPTest.test_posix_group_permissionsc Cs=|jdddtdtjdƒdtƒddƒ|jƒ|jj|jd tj d dfgƒ|jj|j d tj d d gfgƒt j jd d ƒ}|jj|jƒ}|j|jj|ƒddhƒ|j|jj|ƒddhƒ|j|jj|dƒƒ|j|jj|dƒƒdS)Nrxzuid=%(user)s,ou=people,o=testrčzou=groups,o=testz(objectClass=posixGroup)rér Trr6rAr2ryz auth.add_userzauth.change_userr“)rzrrtr°rr rrZmodify_sr2Z MOD_DELETEr@ZMOD_ADDr r†rŚrr˘r§r|r r r’rr)rr2r$r$r%Ú#test_posix_group_permissions_no_gid­s     ),""z,LDAPTest.test_posix_group_permissions_no_gidc Csč|jdddtdtjdƒdtƒddƒ|jƒtjjd d ƒ}|j j |j ƒ}|j |j j |ƒd d hƒ|j |j j|ƒd d hƒ|j|j j|d ƒƒ|j|j j|d ƒƒdS)Nrxzuid=%(user)s,ou=people,o=testrčzou=groups,o=testz(objectClass=nisNetgroup)rér Tryr2z auth.add_userzauth.change_userr“)rzrrtr°rr r r†rŚrr˘r§r|r r r’rr)rr2r$r$r%Útest_nis_group_permissionsÁs     ""z#LDAPTest.test_nis_group_permissionsc Csy|jdddtdtjƒdtddƒdd ƒ|jƒtjjd d ƒ}|j |j j |ƒt ƒƒdS) Nrxzuid=%(user)s,ou=people,o=testrčzou=groups,o=testréręrEr Tryr2) rzrrtr°rr r r†rŚr|rr r)rr2r$r$r%Útest_foreign_user_permissionsÓs  z&LDAPTest.test_foreign_user_permissionsc Cs"|jdddtdtjƒdtddƒdd d d ƒ|jƒtjjd d ƒj }tjjd d ƒj }xyt dƒD]k}|j j |ƒ}|j |j j|ƒddhƒ|j j |ƒ}|j |j j|ƒtƒƒq„W|j |jjƒddddddgƒdS)Nrxzuid=%(user)s,ou=people,o=testrčzou=groups,o=testréręrEr TZ CACHE_GROUPSryr2r;éz auth.add_userzauth.change_userrr‚rą)rzrrtr°rr r r†rŚr§Úrangerr˘r|r rrrr„)rZalice_idZbob_idÚir2r;r$r$r%Útest_group_cacheŕs(   #  zLDAPTest.test_group_cachec Csł|jdddtdtjdƒdtƒddƒ|jtjjƒd ƒ|j j d d d d ƒ}|jtjjƒd ƒ|jt |j j ƒƒt tjj ƒƒƒdS)Nrxzuid=%(user)s,ou=people,o=testrčzou=groups,o=testz(objectClass=posixGroup)réÚ MIRROR_GROUPSTrryr2r4rÖ)rzrrtr°rr|rr†r‡rr{rr/Úall)rr2r$r$r%Útest_group_mirroring˙s    zLDAPTest.test_group_mirroringc CsÇ|jdddtdtjdƒdtddƒd d ƒ|jjd d d d ƒ}|jtt j j ƒj ddd ƒƒddddddhƒ|jt|j j ƒƒtt j j ƒƒƒdS)Nrxzuid=%(user)s,ou=people,o=testrčzou=groups,o=testz(objectClass=groupOfNames)réręrErTryr2r4r!ÚflatrFrGrHrUrTrV)rzrrtr°rrr{r|rrr†rÚ values_listr/)rr2r$r$r%Útest_nested_group_mirrorings   !  z$LDAPTest.test_nested_group_mirroringc Csý|jdddtdtjdƒdtƒddd gƒi}x=d d „td d ƒDƒD]}tjjd|ƒ||6szALDAPTest.test_group_mirroring_whitelist_update..rrľr!r2rPryr4rT)rzrrtr°rrrr†rŚrrŇr/rr{r|r )rr/r!r2r$r$r%Ú%test_group_mirroring_whitelist_update,s     #z.LDAPTest.test_group_mirroring_whitelist_updatec Csý|jdddtdtjdƒdtƒddd gƒi}x=d d „td d ƒDƒD]}tjjd|ƒ||.rrľr!r2rOryr4rT)rzrrtr°rrrr†rŚrrŇr/rr{r|r )rr/r!r2r$r$r%Ú#test_group_mirroring_whitelist_noopBs     #z,LDAPTest.test_group_mirroring_whitelist_noopc Csý|jdddtdtjdƒdtƒddd gƒi}x=d d „td d ƒDƒD]}tjjd|ƒ||.rrľr!r2rPryr4rTrO)rzrrtr°rrrr†rŚrrŇr/rr{r|r )rr/r!r2r$r$r%Ú%test_group_mirroring_blacklist_updateXs     #z.LDAPTest.test_group_mirroring_blacklist_updatec Csý|jdddtdtjdƒdtƒddd gƒi}x=d d „td d ƒDƒD]}tjjd|ƒ||.rrľr!r2rOryr4rT)rzrrtr°rrrr†rŚrrŇr/rr{r|r )rr/r!r2r$r$r%Ú#test_group_mirroring_blacklist_noopns     #z,LDAPTest.test_group_mirroring_blacklist_noopc Cs‚|jdddtdtjƒdtddƒdd d d ƒ|jƒtjjd d ƒ}|j |j j |ƒd dhƒdS)Nrxzuid=%(user)s,ou=people,o=testrčzou=groups,o=testréręrEr TÚAUTHORIZE_ALL_USERSryr2z auth.add_userzauth.change_user) rzrrtr°rr r r†rŚr|rr )rr2r$r$r%Útest_authorize_external_users„s  z&LDAPTest.test_authorize_external_usersc CsŽ|jdtdtjdƒdtdtjƒdtddƒd d d d ƒ|jƒtjjd d ƒ}|j |j j |ƒt ƒƒdS)NrŻzou=people,o=testz(uid=%(user)s)rčzou=groups,o=testréręrEr Tr)ryz not-in-ldap) rzrrtr°rr r r†rŚr|rr r)rr2r$r$r%Útest_authorize_external_unknown’s  z(LDAPTest.test_authorize_external_unknowncCsţ|jddƒ|jjdƒ}|jjdƒ}|j|ƒ|j|jdƒ|j|jdƒ|j|jƒ|j |j ƒ|j |j ƒ|j|ƒ|j|jdƒ|j|jdƒ|j|jƒ|j |j ƒ|j |j ƒdS)Nrxzuid=%(user)s,ou=people,o=testr2r;rť) rzrrŇr­r|rÇrČr’rrˆrr)rr2r;r$r$r%Útest_create_without_auth˘s    z!LDAPTest.test_create_without_authcCst|jdddddidd6dd 6d td tjƒd tƒd idd6dd6dd6ƒtjjddƒtjjddƒ|jj dƒ}|jj dƒ}|j |ƒ|j |j dƒ|j |j dƒ|j|jƒ|j|jƒ|j|jƒ|j |ƒ|j |j dƒ|j |j dƒ|j|jƒ|j|jƒ|j|jƒdS)Nrxzuid=%(user)s,ou=people,o=testrćFržr8rÇr:rČrčzou=groups,o=testrérzcn=active_gon,ou=groups,o=testrzcn=staff_gon,ou=groups,o=testrz!cn=superuser_gon,ou=groups,o=testrryr2r;r7r9r<r=)rzrrtr°rr r†rŚrrŇr­r|rÇrČr’rrrrˆ)rr2r;r$r$r%Útest_populate_without_authˇs4     z#LDAPTest.test_populate_without_authcCs3|jddƒ|jjdƒ}|j|ƒdS)Nrxzuid=%(user)s,ou=people,o=testrŤ)rzrrŇr™)rrŤr$r$r%Útest_populate_bogus_userŘs z!LDAPTest.test_populate_bogus_usercCsY|jddddƒ|j|jjƒ|jjddddƒ|j|jjƒdS)Nrxzuid=%(user)s,ou=people,o=testÚ START_TLSFryr2r4)rzrˆrrÚ tls_enabledrr{)rr$r$r%Útest_start_tls_missingás  zLDAPTest.test_start_tls_missingcCsY|jddddƒ|j|jjƒ|jjddddƒ|j|jjƒdS)Nrxzuid=%(user)s,ou=people,o=testr/Tryr2r4)rzrˆrrr0rr{r’)rr$r$r%Útest_start_tlsës  zLDAPTest.test_start_tlscCs<|jdtdtjdƒƒ|jjddddƒdS)z: Make sure we're not phased by referrals. rŻzou=people,o=testz(uid=%(user)s)ryr2r4N)rzrrtr°rr{)rr$r$r%Útest_null_search_resultsős z!LDAPTest.test_null_search_resultsc Cs‘|jdttdtjdƒtdtjdƒƒƒ|jjddddƒ}|j|ƒ|j|j j ƒdd d d d d d gƒdS) NrŻzou=groups,o=testz(uid=%(user)s)zou=people,o=testryr2r4rr‚ÚsearchÚresult) rzrrrtr°rr{r­r|rrr„)rr2r$r$r%Útest_union_searchs   zLDAPTest.test_union_searchcCsU|jddƒ|jjddddƒ}|j|ƒ|j|jjƒgƒdS)Nrxzuid=%(user)s,ou=people,o=testryr2r4rť)rzrr{r™r|rrr„)rr2r$r$r%Útest_deny_empty_passwords   z!LDAPTest.test_deny_empty_passwordcCsa|jddddƒ|jjddddƒ}|j|ƒ|j|jjƒd d gƒdS) Nrxzuid=%(user)s,ou=people,o=testÚPERMIT_EMPTY_PASSWORDTryr2r4rťrr‚)rzrr{r™r|rrr„)rr2r$r$r%Útest_permit_empty_passwords   z#LDAPTest.test_permit_empty_passwordcCsa|jddddƒ|jjddddƒ}|j|ƒ|j|jjƒdd gƒdS) Nrxzuid=%(user)s,ou=people,o=testr8Tryr2r4rr‚)rzrr{r™r|rrr„)rr2r$r$r%Útest_permit_null_password)s   z"LDAPTest.test_permit_null_passwordc Cs%|jdddtdtjƒdtddƒdd ƒ|jƒ|jjd d d d ƒ}tj |tj ƒ}tj |ƒ}|j jj |j j_ |j|ƒ|j|jj|ƒd dhƒ|j|jj|ƒd dhƒ|j|jj|d ƒƒ|j|jj|dƒƒdS)Nrxzuid=%(user)s,ou=people,o=testrčzou=groups,o=testréręrEr Tryr2r4z auth.add_userzauth.change_userr“)rzrrtr°rr rr{ÚpickleÚdumpsÚHIGHEST_PROTOCOLÚloadsrˇÚsettingsr­r|r r r’rr)rZalice0Zpickledr2r$r$r%Ú test_pickle7s   ""zLDAPTest.test_picklecCsrtdtjdddgƒ}|j|jƒ|j|jjddƒddtjdddgfifgƒdS)Nzou=people,o=testz (uid=alice)rÔrŐr€Trą)rrtr°Úexecuterrr|r„)rr4r$r$r%Útest_search_attrlistLs zLDAPTest.test_search_attrlistc s­G‡fdd†dtjƒ‰ˆƒ|_|jddƒtjddƒ-}tjdƒ|jjdd d d ƒWdQX|jt|ƒd ƒ|j|d j t ƒdS) Ncs%eZdZ‡‡fdd†Z‡S)z>LDAPTest.test_get_or_create_user_deprecated..MyBackendcstˆ|ƒj||ƒS)N)ÚsuperÚget_or_create_user)rryrˇ)rŽÚ __class__r$r%rDVszQLDAPTest.test_get_or_create_user_deprecated..MyBackend.get_or_create_user)r'r(r)rDr$)rŽ)rEr%rŽUs rŽrxzuid=%(user)s,ou=people,o=testÚrecordTÚalwaysryr2r4rr) rrsrzrkÚcatch_warningsÚ simplefilterr{r|ÚlenrgÚDeprecationWarning)rÚwr$)rŽr%Ú"test_get_or_create_user_deprecatedTs  z+LDAPTest.test_get_or_create_user_deprecatedc CsGdd„dtjƒ}|ƒ|_|jddƒtjddƒ-}tjdƒ|jjdd d d ƒWdQX|jt|ƒd ƒdS) Nc@seZdZdS)zPLDAPTest.test_custom_backend_without_get_or_create_no_warning..MyBackendN)r'r(r)r$r$r$r%rŽcs rŽrxzuid=%(user)s,ou=people,o=testrFTrGryr2r4r) rrsrzrkrHrIr{r|rJ)rrŽrLr$r$r%Ú4test_custom_backend_without_get_or_create_no_warningbs  z=LDAPTest.test_custom_backend_without_get_or_create_no_warningcspG‡fdd†dtjƒ‰ˆƒ|_|jddƒ|jjddddƒ}|j|jjdƒdS) Ncs%eZdZ‡‡fdd†Z‡S)zGLDAPTest.test_override_authenticate_access_ldap_user..MyBackendcs"d|_tˆ|ƒj||ƒS)NÚbar)ÚfoorCÚauthenticate_ldap_user)rrˇr4)rŽrEr$r%rQps z^LDAPTest.test_override_authenticate_access_ldap_user..MyBackend.authenticate_ldap_user)r'r(r)rQr$)rŽ)rEr%rŽos rŽrxzuid=%(user)s,ou=people,o=testryr2r4rO)rrsrzr{r|rˇrP)rr‹r$)rŽr%Ú+test_override_authenticate_access_ldap_userns  z4LDAPTest.test_override_authenticate_access_ldap_usercKst||j_dS)N)rrr?)rr r$r$r%rz}szLDAPTest._init_settingscCstjjddƒtjjddƒg}tjjddƒ}|jj|Œtjjddƒ}|jj|Œtjjddƒ}|jj|ŒdS)NÚcodenameZadd_userZ change_userr!rFr@rQ)rr†rrrŚÚ permissionsÚadd)rrTrFr@rQr$r$r%r €szLDAPTest._init_groupsN)†r'r(r)Útopr.r/r0r1r2r;rZdresslerr>r@rBrCrDrFrGrHrIrJrKrLrMrNrOrPrQrRrSrTrUrVÚdictrjÚ classmethodrernrorurwr}r…rŒrr rrtr°r”r–ršr›rœrĄr¤r¨rŠrŞrŹrŽrłr¸ršrźr˝ržrżrĹrĆrÉrĘrËrÎrŃrÓr×rŘrßrärĺrçrírđrńrňrórôrőr÷rřrůrţr˙rrrrrr rrrrrrrrrr!r$r%r'r(r*r+r,r-r.r1r2r3r6r7r9r:r@rBrMrNrRrzr r$r$r$r%r+Fsş                                                                         **                                                       !        r+)-Ú __future__rrrrÚcopyrrXr;ÚunittestrkrtÚdjango.contrib.auth.modelsrrr Údjango.core.cacher Údjango.core.exceptionsr Ú django.testr Údjango.test.utilsr Údjango.utils.encodingrrWrZdjango_auth_ldap.configrrrrrrrrÚmodelsrriÚ ImportErrorZ LDAPSettingsrÚskipIfr+r$r$r$r%Ús,"     :   "django-auth-ldap-1.4.0/tests/__pycache__/tests.cpython-35.pyc0000644000076600000240000015224413255034675024606 0ustar psagersstaff00000000000000 RŃŻZűůă @sŞddlmZmZmZmZddlmZddlZddlZddl Z ddl Z ddl Z ddl m Z mZmZddlmZddlmZddlmZddlmZdd lmZdd lmZdd lmZmZmZm Z m!Z!m"Z"m#Z#m$Z$d d l%m&Z&yddl'Z'Wne(k r^dZ'YnXGdd„dej)ƒZ*e j+e'dkdƒGdd„deƒƒZ,dS)é)Úabsolute_importÚdivisionÚprint_functionÚunicode_literals)ÚdeepcopyN)ÚGroupÚ PermissionÚUser)Úcache)ÚImproperlyConfigured)ÚTestCase)Úoverride_settings)Ú force_str)Úbackend)ÚGroupOfNamesTypeÚLDAPGroupQueryÚ LDAPSearchÚLDAPSearchUnionÚMemberDNGroupTypeÚNestedMemberDNGroupTypeÚ NISGroupTypeÚPosixGroupTypeé)ÚTestUserc@s"eZdZdZdd„ZdS)Ú TestSettingszb A replacement for backend.LDAPSettings that does not load settings from django.conf. cKsFx?|jjƒD].\}}|j||ƒ}t|||ƒqWdS)N)ÚdefaultsÚitemsÚgetÚsetattr)ÚselfÚkwargsÚnameÚdefaultÚvalueŠr$ú7/Users/psagers/Projects/django-auth-ldap/tests/tests.pyÚ__init__@szTestSettings.__init__N)Ú__name__Ú __module__Ú __qualname__Ú__doc__r&r$r$r$r%r;s rz4django_auth_ldap tests require the mockldap package.c@sT eZdZdddifZdddifZdddifZd dd ifZd dd ifZd ddgdddddgddgddgddgddgddgifZdddgdddddgddgdd gdd!gdd"gdd#gifZ e d$ƒde d%ƒgdddddgddgdd&gdd!gdd'gde d(ƒgifZ d)dd*gdddddgddgd+d,gifZ d-d.d/gdd0gddgd1gifZ d2d.d3gdd0gdd gd1dgifZd4d.d5gdd0gdd&gd1dgifZd6d.d7gdd8gd9gifZd:d.d;gdd8gd9d gifZd<d.d=gdd8gd9d gifZd>d.d?gdd8gd9d gifZd@d.dAgdd8gd9dgifZdBd.dCgdd8gd9d gifZdDd.dEgdd8gd9d dgifZdFd.dGgdd8gd9dgifZdHd.dIgdd8gd9d gifZdJd.dKgdd8gd9gifZdLd.dMgdd8gd9d gifZdNd.dOgdd8gd9gifZdPd.dQgddRgdSdTgifZdUd.dVgddRgdSdTgifZdWd.dXgddRgdSdTgifZdYd.dZgdd8gd9d[gifZd\d.d]gdd8gd9d d^gifZ d^d.d_gdd8gd9dYgifZ!e"eeeeeee e e e eeeeeeeeeeeeeeeeeee e!gƒZ#e$d`da„ƒZ%e$dbdc„ƒZ&e$ddde„ƒZ'dfdg„Z(dhdi„Z)djdk„Z*dldm„Z+dndo„Z,dpdq„Z-e.dre/de0j1dsƒƒdtdu„ƒZ2e.dre/de0j1dsƒƒdvdw„ƒZ3dxdy„Z4dzd{„Z5d|d}„Z6e.d~dƒd€d„ƒZ7e.d~dƒd‚dƒ„ƒZ8e.d~dƒd„d…„ƒZ9d†d‡„Z:dˆd‰„Z;dŠd‹„Z<dŒd„Z=dŽd„Z>dd‘„Z?d’d“„Z@d”d•„ZAd–d—„ZBd˜d™„ZCdšd›„ZDdœd„ZEdždŸ„ZFd dĄ„ZGd˘dŁ„ZHd¤dĽ„ZIdŚd§„ZJe.d~dƒd¨dŠ„ƒZKe.d~dƒdŞdŤ„ƒZLdŹd­„ZMdŽdŻ„ZNd°dą„ZOd˛dł„ZPd´dľ„ZQdśdˇ„ZRd¸dš„ZSdşdť„ZTdźd˝„ZUdždż„ZVdŔdÁ„ZWdÂdÄZXdÄdńZYdĆdDŽZZdČdɄZ[dĘd˄Z\dĚd̈́Z]dÎdτZ^dĐdфZ_dŇdӄZ`dÔdՄZadÖdׄZbdŘdلZcdÚdۄZddÜd݄ZedŢd߄Zfdŕdá„Zgdâdă„Zhdädĺ„Zidćdç„Zjdčdé„Zkdędë„Zldědí„Zmdîdď„Zndđdń„Zodňdó„Zpdôdő„Zqdöd÷„Zrdřdů„Zsdúdű„Ztdüdý„Zudţd˙„Zvdd„Zwdd„Zxdd„Zydd„Zzdd „Z{d d „Z|d d „Z}dd„Z~dd„Zdd„Z€dd„Zdd„Z‚dd„Zƒdd„Z„dd„Z…dS(ÚLDAPTestzo=testÚoÚtestzou=people,o=testZouÚpeoplezou=groups,o=testÚgroupszou=moregroups,o=testÚ moregroupszou=mirror_groups,o=testÚ mirror_groupszuid=alice,ou=people,o=testÚuidÚaliceZ objectClassZpersonZorganizationalPersonZ inetOrgPersonZ posixAccountZ userPasswordÚpasswordÚ uidNumberZ1000Ú gidNumberÚ givenNameÚAliceÚsnÚAdamszuid=bob,ou=people,o=testÚbobZ1001Z50ÚRobertÚBarkeruuid=dreßler,ou=people,o=testudreßlerZ1002ZWolfganguDreßlerzuid=nobody,ou=people,o=testÚnobodyZ binaryAttrő²zcn=active_px,ou=groups,o=testZcnÚ active_pxZ posixGroupÚ memberUidzcn=staff_px,ou=groups,o=testÚstaff_pxz cn=superuser_px,ou=groups,o=testÚ superuser_pxzcn=empty_gon,ou=groups,o=testÚ empty_gonZ groupOfNamesÚmemberzcn=active_gon,ou=groups,o=testÚ active_gonzcn=staff_gon,ou=groups,o=testÚ staff_gonz!cn=superuser_gon,ou=groups,o=testÚ superuser_gonz!cn=other_gon,ou=moregroups,o=testÚ other_gonz#cn=alice_gon,ou=query_groups,o=testÚ alice_gonz$cn=mutual_gon,ou=query_groups,o=testÚ mutual_gonz!cn=bob_gon,ou=query_groups,o=testÚbob_gonz"cn=mirror1,ou=mirror_groups,o=testÚmirror1z"cn=mirror2,ou=mirror_groups,o=testÚmirror2z"cn=mirror3,ou=mirror_groups,o=testÚmirror3z"cn=mirror4,ou=mirror_groups,o=testÚmirror4zcn=active_nis,ou=groups,o=testÚ active_nisZ nisNetgroupZnisNetgroupTriplez (,alice,)zcn=staff_nis,ou=groups,o=testÚ staff_nisz!cn=superuser_nis,ou=groups,o=testÚ superuser_niszcn=parent_gon,ou=groups,o=testÚ parent_gonzcn=nested_gon,ou=groups,o=testzCN=nested_gon,ou=groups,o=testÚ nested_gonz cn=circular_gon,ou=groups,o=testÚ circular_goncCshtjdƒ}tjdƒ}tjƒ}|jtjƒ|j|ƒ|j|ƒ|jtjƒdS)NÚdjango_auth_ldapz'LDAP auth - %(levelname)s - %(message)s) ÚloggingÚ getLoggerÚ FormatterÚ StreamHandlerÚsetLevelÚDEBUGÚ setFormatterÚ addHandlerÚCRITICAL)ÚclsÚloggerÚ formatterÚhandlerr$r$r%Úconfigure_loggerós   zLDAPTest.configure_loggercCs<|jƒtj|jƒ|_tjddtddƒdS)NÚignoreÚcategoryÚmodulezmockldap.ldapobject)reÚmockldapZMockLdapÚ directoryÚwarningsÚfilterwarningsÚUnicodeWarning)rar$r$r%Ú setUpClass˙s zLDAPTest.setUpClasscCs |`dS)N)ri)rar$r$r%Ú tearDownClassszLDAPTest.tearDownClasscCsDtjƒ|jjƒ|jd|_tjƒ|_|jjdS)Nzldap://localhost)r ÚclearriÚstartÚldapobjrÚ LDAPBackendÚldap)rr$r$r%ÚsetUp s   zLDAPTest.setUpcCs|jjƒ|`dS)N)riÚstoprr)rr$r$r%ÚtearDowns zLDAPTest.tearDowncCsU|jdddddiƒ|jjddddƒ|j|jjdƒdƒdS) NÚUSER_DN_TEMPLATEzuid=%(user)s,ou=people,o=testZCONNECTION_OPTIONSZopt1Zvalue1Úusernamer3r4)Ú_init_settingsrÚ authenticateÚ assertEqualrrZ get_option)rr$r$r%Ú test_optionss   zLDAPTest.test_optionscCsz|jddd„ddƒ|jjddddƒ|jd }|j|jd d ƒd difd difgƒdS)NZ SERVER_URIcSsdS)Nzldap://ldap.example.comr$r$r$r$r%Ú(sz3LDAPTest.test_callable_server_uri..rxzuid=%(user)s,ou=people,o=testryr3r4úldap://ldap.example.comÚ with_argsTÚ initializeÚ simple_bind_súuid=alice,ou=people,o=test)r)rƒr4)rzrr{rir|Úmethods_called)rrrr$r$r%Útest_callable_server_uri&s    z!LDAPTest.test_callable_server_uricCs |jddƒtjjƒ}|jjddddƒ}|j|jƒƒ|j|j dƒ|jtjjƒ|dƒ|j|j j ƒddgƒdS) Nrxzuid=%(user)s,ou=people,o=testryr3r4rrr‚) rzr ÚobjectsÚcountrr{Ú assertFalseÚhas_usable_passwordr|ryrrr„)rÚ user_countÚuserr$r$r%Útest_simple_bind5s  zLDAPTest.test_simple_bindcCsľGdd„dtjƒ}|ƒ|_tjjƒ}|jjddddƒ}|j|jƒƒ|j|j dƒ|jtjjƒ|dƒ|j|j j ƒddgƒdS) Nc@seZdZddiZdS)z1LDAPTest.test_default_settings..MyBackendrxzuid=%(user)s,ou=people,o=testN)r'r(r)Údefault_settingsr$r$r$r%Ú MyBackendFs rŽryr3r4rrr‚) rrsr r†r‡r{rˆr‰r|ryrrr„)rrŽrŠr‹r$r$r%Útest_default_settingsEs  zLDAPTest.test_default_settingsZAUTH_LDAP_USER_SEARCHz(uid=%(user)s)cCs,|jjddddƒ}|j|ƒdS)Nryr3r4)ÚclientÚloginÚ assertTrue)rÚauthr$r$r%Ú&test_login_with_multiple_auth_backendsXsz/LDAPTest.test_login_with_multiple_auth_backendscCs,|jjddddƒ}|j|ƒdS)NryÚinvalidr4Zi_do_not_exist)rr‘rˆ)rr“r$r$r%Ú*test_bad_login_with_multiple_auth_backends]sz3LDAPTest.test_bad_login_with_multiple_auth_backendscCss|jddƒ|jjddddƒ}|j|ƒ|j|jjddƒdd ifd difgƒd S)z. Bind with a username that requires escaping. rxzuid=%(user)s,ou=people,o=testryzalice,1r4r€Trúldap://localhostr‚úuid=alice\,1,ou=people,o=testN)r—)r˜r4)rzrr{Ú assertIsNoner|rrr„)rr‹r$r$r%Útest_simple_bind_escapedbs   z!LDAPTest.test_simple_bind_escapedcCs |jddƒtjjƒ}|jjddddƒ}|j|jƒƒ|j|j dƒ|jtjjƒ|dƒ|j|j j ƒdd gƒdS) Nrxzuid=%(user)s,ou=people,o=testryr8r4r3rrr‚) rzr r†r‡rr{rˆr‰r|ryrrr„)rrŠr‹r$r$r%Útest_new_user_lowercaseqs  z LDAPTest.test_new_user_lowercasecCs;|jddƒ|jjddddƒ}t|ƒ}dS)Nrxzuid=%(user)s,ou=people,o=testryr8r4)rzrr{r)rr‹r$r$r%Ú test_deepcopys zLDAPTest.test_deepcopyÚAUTH_USER_MODELztests.TestUsercCsK|jdddddiƒ|jjddddƒ}|j|tƒdS) Nrxzuid=%(user)s,ou=people,o=testÚ USER_ATTR_MAPÚ uid_numberr5ryr8r4)rzrr{ÚassertIsInstancer)rr‹r$r$r%Útest_auth_custom_user‰s   zLDAPTest.test_auth_custom_usercCs`|jdddddiƒ|jjddddƒ}|jj|jƒ}|j|tƒdS) Nrxzuid=%(user)s,ou=people,o=testržrŸr5ryr8r4)rzrr{Úget_userÚidr r)rr‹r$r$r%Útest_get_custom_user”s   zLDAPTest.test_get_custom_usercCs‚|jdddddiddƒtjjdddd ƒ}|jjd d d d ƒ}|j|tƒ|j|j|jƒdS) Nrxzuid=%(user)s,ou=people,o=testržrŸr5ZUSER_QUERY_FIELDÚ identifierZabcdefičryr8r4) rzrr†Úcreaterr{r r|Úpk)rr3r‹r$r$r%Útest_get_custom_field s  zLDAPTest.test_get_custom_fieldcCsœ|jddƒtjjƒ}|jjddddƒ}|jjddddƒ}|j|jƒƒ|j|j dƒ|jtjjƒ|dƒdS) Nrxzuid=%(user)s,ou=people,o=testryz alicer4zalice r3r) rzr r†r‡rr{rˆr‰r|ry)rrŠr‹r$r$r%Útest_new_user_whitespaceŽs z!LDAPTest.test_new_user_whitespacecCsƒ|jddƒtjjƒ}|jjddddƒ}|j|ƒ|jtjjƒ|ƒ|j|jj ƒddgƒdS)Nrxzuid=%(user)s,ou=people,o=testryZ evil_alicer4rr‚) rzr r†r‡rr{r™r|rrr„)rrŠr‹r$r$r%Útest_simple_bind_bad_userťs   z"LDAPTest.test_simple_bind_bad_usercCsƒ|jddƒtjjƒ}|jjddddƒ}|j|ƒ|jtjjƒ|ƒ|j|jj ƒddgƒdS) Nrxzuid=%(user)s,ou=people,o=testryr3r4Úbogusrr‚) rzr r†r‡rr{r™r|rrr„)rrŠr‹r$r$r%Útest_simple_bind_bad_passwordĘs   z&LDAPTest.test_simple_bind_bad_passwordcCsw|jddƒtjjddƒtjjƒ}|jjddddƒ}|j|ƒ|jtjjƒ|ƒdS)Nrxzuid=%(user)s,ou=people,o=testryr3r4) rzr r†rŚr‡rr{ÚassertIsNotNoner|)rrŠr‹r$r$r%Útest_existing_userŮs  zLDAPTest.test_existing_usercCsľ|jdtdtjdƒƒ|jjjdtjddƒ|jgƒtj j ddƒ|j j ddddƒ}|j |ƒ|j|jdƒ|jtj jƒd ƒdS) NÚ USER_SEARCHzou=people,o=testz(uid=%(user)s)z (uid=Alice)ryr3r8r4r)rzrrtÚ SCOPE_SUBTREErrÚsearch_sÚseedr3r r†rŚrr{r­r|ryr‡)rr‹r$r$r%Útest_existing_user_insensitivećs  z'LDAPTest.test_existing_user_insensitivecCs Gdd„dtjƒ}|ƒ|_|jddƒtjjƒ}|jjddddƒ}|jj|jƒ}|j tjjƒ|dƒ|j |j d ƒ|j |j j dƒ|j |j dƒ|j |j d ƒ|j |j j dƒ|j |j dƒdS) Nc@s(eZdZdd„Zdd„ZdS)z1LDAPTest.test_convert_username..MyBackendcSsd|S)Nzldap_%sr$)rryr$r$r%Úldap_to_django_usernameůszILDAPTest.test_convert_username..MyBackend.ldap_to_django_usernamecSs|dd…S)Nér$)rryr$r$r%Údjango_to_ldap_usernameüszILDAPTest.test_convert_username..MyBackend.django_to_ldap_usernameN)r'r(r)r´rśr$r$r$r%rŽřs  rŽrxzuid=%(user)s,ou=people,o=testryr3r4rZ ldap_alice)rrsrzr r†r‡r{r˘r§r|ryÚ ldap_userZ _usernameZ ldap_username)rrŽrŠZuser1Zuser2r$r$r%Útest_convert_username÷s  zLDAPTest.test_convert_usernamecCsœ|jdtdtjdƒƒtjjƒ}|jjddddƒ}|j |ƒ|j tjjƒ|dƒ|j |j j ƒdd d d gƒdS) NrŻzou=people,o=testz(uid=%(user)s)ryr3r4rrr‚rą) rzrrtr°r r†r‡rr{r­r|rrr„)rrŠr‹r$r$r%Útest_search_binds   zLDAPTest.test_search_bindc Cs|jdtdtjdƒƒ|jjddddƒ}|j|ƒ|j|jj ddƒd difd difd dtjddfifgƒdS)z/ Search for a username that requires escaping. rŻzou=people,o=testz(uid=%(user)s)ryzalice*r4r€Trúldap://localhostr‚Úrąz(uid=alice\2a)N)rş)rťrť) rzrrtr°rr{r™r|rrr„)rr‹r$r$r%Útest_search_bind_escaped!s    z!LDAPTest.test_search_bind_escapedcCsm|jdtdtjdƒƒ|jjddddƒ}|j|ƒ|j|jj ƒddd gƒdS) NrŻzou=people,o=testz (cn=%(user)s)ryr3r4rr‚rą) rzrrtr°rr{r™r|rrr„)rr‹r$r$r%Útest_search_bind_no_user3s   z!LDAPTest.test_search_bind_no_usercCsm|jdtdtjdƒƒ|jjddddƒ}|j|ƒ|j|jj ƒddd gƒdS) NrŻzou=people,o=testz(uid=*)ryr3r4rr‚rą) rzrrtr°rr{r™r|rrr„)rr‹r$r$r%Útest_search_bind_multiple_usersBs   z(LDAPTest.test_search_bind_multiple_userscCsp|jdtdtjdƒƒ|jjddddƒ}|j|ƒ|j|jj ƒdd d d gƒdS) NrŻzou=people,o=testz(uid=%(user)s)ryr3r4rŤrr‚rą) rzrrtr°rr{r™r|rrr„)rr‹r$r$r%Útest_search_bind_bad_passwordQs   z&LDAPTest.test_search_bind_bad_passwordc CsŇ|jdddddtdtjdƒƒ|jjdd ddƒ}|j|ƒ|j|jƒ|j|jj |j d ƒ|j|jj tj j |j d ƒƒ|j|j jƒd d dd gƒdS)NÚBIND_DNzuid=bob,ou=people,o=testÚ BIND_PASSWORDr4rŻzou=people,o=testz(uid=%(user)s)ryr3rrrr‚rą)rzrrtr°rr{r­rˇr|Údnr3ÚattrsÚcidictrrr„)rr‹r$r$r%Ú!test_search_bind_with_credentials`s  ) z*LDAPTest.test_search_bind_with_credentialsc Csv|jdddddtdtjdƒƒ|jjdd d d ƒ}|j|ƒ|j|jj ƒd d gƒdS) NrŔzuid=bob,ou=people,o=testrÁrŤrŻzou=people,o=testz(uid=%(user)s)ryr3r4rr‚) rzrrtr°rr{r™r|rrr„)rr‹r$r$r%Ú%test_search_bind_with_bad_credentialsts   z.LDAPTest.test_search_bind_with_bad_credentialscCst|jdddddddiƒ|jjdd d d ƒ}|j|ƒ|j|jd ƒ|j|jd ƒdS) Nrxzuid=%(user)s,ou=people,o=testržÚ first_namer7Ú last_namer9ryudreßlerr4uDreßler)rzrr{r­r|ryrČ)rr‹r$r$r%Útest_unicode_user…s  zLDAPTest.test_unicode_usercCsK|jddƒ|jjddddƒ}|j|jjtjjƒdS)Nrxzuid=%(user)s,ou=people,o=testryr3r4)rzrr{r rˇrĂrtrÄ)rr‹r$r$r%Ú test_cidicts zLDAPTest.test_cidictcCsŸ|jdddddddiƒ|jjdd d d ƒ}|j|jd ƒ|j|jd ƒ|j|jd ƒ|j|jjƒd dddgƒdS)Nrxzuid=%(user)s,ou=people,o=testržrÇr7rČr9ryr3r4r8r:rr‚rą) rzrr{r|ryrÇrČrrr„)rr‹r$r$r%Útest_populate_user™s  zLDAPTest.test_populate_userc Cs“|jddddddddd iƒ|jjd d d d ƒ}|j|jd ƒ|j|jd ƒ|j|jdƒ|j|jdƒdS)Nrxzuid=%(user)s,ou=people,o=testržrÇr7rČr9ÚemailÚmailryr3r4r8r:rť)rzrr{r|ryrÇrČrĚ)rr‹r$r$r%Ú)test_populate_user_with_missing_attributeŤs  z2LDAPTest.test_populate_user_with_missing_attributec CsY|jdddddddiƒ|jtdƒ|jjd d d d ƒWdQRXdS) Nrxzuid=%(user)s,ou=people,o=testržrÇr7rŸr5zOops...ryr3r4)rzÚassertRaisesMessageÚ Exceptionrr{)rr$r$r%Ú4test_authenticate_with_buggy_setter_raises_exceptionťs   z=LDAPTest.test_authenticate_with_buggy_setter_raises_exceptionc CsP|jdddddddiƒ|jtdƒ|jjd ƒWdQRXdS) Nrxzuid=%(user)s,ou=people,o=testržrÇr7rŸr5zOops...r3)rzrĎrĐrÚ populate_user)rr$r$r%Ú5test_populate_user_with_buggy_setter_raises_exceptionČs   z>LDAPTest.test_populate_user_with_buggy_setter_raises_exceptioncCsĆ|jdddddddidd d gƒ|jjd d d d ƒ}|j|jd ƒ|j|jjƒddddgƒ|j|jjddƒdddtjdd d gfifƒdS)Nrxzuid=%(user)s,ou=people,o=testržrÇr7rČr9Z USER_ATTRLISTÚ*ú+ryr3r4rr‚rąr€Tézuid=alice,ou=people,o=testz(objectClass=*)) rzrr{r|ryrrr„rtZ SCOPE_BASE)rr‹r$r$r%Útest_populate_with_attrlistŐs   z$LDAPTest.test_populate_with_attrlistcCs˘|jdddddddidd ƒ|jjd d d d ƒ}|j|jd ƒ|j|jd ƒ|j|jdƒ|j|jjƒdddgƒdS)Nrxzuid=%(user)s,ou=people,o=testržrÇr7rČr9ZBIND_AS_AUTHENTICATING_USERTryr3r4r8r:rr‚rą) rzrr{r|ryrÇrČrrr„)rr‹r$r$r%Útest_bind_as_useręs  zLDAPTest.test_bind_as_usercsqˆjddƒ‡fdd†}tjj|ƒˆjjddddƒ}ˆj|jƒtjj|ƒdS)Nrxzuid=%(user)s,ou=people,o=testcs1ˆjd|ƒˆjd|ƒd|d_dS)Nr‹rˇT)ÚassertInÚpopulate_user_handled)Úsenderr )rr$r%Úhandle_populate_usersz@LDAPTest.test_signal_populate_user..handle_populate_userryr3r4)rzrrŇÚconnectr{r’rÚÚ disconnect)rrÜr‹r$)rr%Útest_signal_populate_userýs z"LDAPTest.test_signal_populate_userc s”ˆjdddddtdtjdƒƒ‡fdd †}tjj|ƒˆjtjƒˆjj d d d d ƒWdQRXtjj |ƒdS) NrŔzuid=bob,ou=people,o=testrÁrŤrŻzou=people,o=testz(uid=%(user)s)cs"ˆj|ddƒ|d‚dS)NÚcontextr{Ú exception)r|)rŰr )rr$r%Úhandle_ldap_errorsz?LDAPTest.test_auth_signal_ldap_error..handle_ldap_errorryr3r4) rzrrtr°rZ ldap_errorrÝÚ assertRaisesZ LDAPErrorr{rŢ)rrâr$)rr%Útest_auth_signal_ldap_errors  z$LDAPTest.test_auth_signal_ldap_errorc CsN|jdddddtdtjdƒƒ|jjdƒ}|j|ƒdS) NrŔzuid=bob,ou=people,o=testrÁrŤrŻzou=people,o=testz(uid=%(user)s)r3)rzrrtr°rrŇr™)rr‹r$r$r%Útest_populate_signal_ldap_error s z(LDAPTest.test_populate_signal_ldap_errorcCsÍ|jdddddddidd ƒtjjd d dd dd ƒ|jjd d ddƒ}|jjd dddƒ}|j|jd ƒ|j|jd ƒ|j|jdƒ|j|jdƒdS)Nrxzuid=%(user)s,ou=people,o=testržrÇr7rČr9ÚALWAYS_UPDATE_USERFryr3ZAliciaZAstror4r;r<r=) rzr r†rŚrr{r|rÇrČ)rr3r;r$r$r%Útest_no_update_existing-s z LDAPTest.test_no_update_existingc Csż|jdddtdtjdƒdtddƒd d ƒ|jjd d d d ƒ}|jjd dd d ƒ}|j|ƒ|j|ƒ|j |j j ƒddddddddgƒdS)Nrxzuid=%(user)s,ou=people,o=testÚ GROUP_SEARCHzou=groups,o=testz(objectClass=groupOfNames)Ú GROUP_TYPEÚ member_attrrEÚ REQUIRE_GROUPzcn=active_gon,ou=groups,o=testryr3r4r;rr‚Ú compare_s) rzrrtr°rrr{r­r™r|rrr„)rr3r;r$r$r%Útest_require_group=s     zLDAPTest.test_require_groupc Csx|jdddtdtjdƒdtddƒƒ|jjd d d d ƒ}td ƒ}|j|j |j ƒƒdS) Nrxzuid=%(user)s,ou=people,o=testrčzou=query_groups,o=testz(objectClass=groupOfNames)réręrEryr3r4z#cn=alice_gon,ou=query_groups,o=test) rzrrtr°rrr{rr’Úresolverˇ)rr3Úqueryr$r$r%Útest_simple_group_queryPs  z LDAPTest.test_simple_group_queryc Csy|jdddtdtjdƒdtddƒƒ|jjd d d d ƒ}td ƒ}|j|j |j ƒƒdS) Nrxzuid=%(user)s,ou=people,o=testrčzou=query_groups,o=testz(objectClass=groupOfNames)réręrEryr3r4z#cn=alice_gon,ou=query_groups,o=test) rzrrtr°rrr{rrˆrîrˇ)rr3rďr$r$r%Útest_negated_group_queryZs  z!LDAPTest.test_negated_group_queryc Csś|jdddtdtjdƒdtddƒƒ|jjd d d d ƒ}|jjd d d d ƒ}td ƒtdƒB}|j|j |j ƒƒ|j|j |j ƒƒdS)Nrxzuid=%(user)s,ou=people,o=testrčzou=query_groups,o=testz(objectClass=groupOfNames)réręrEryr3r4r;z#cn=alice_gon,ou=query_groups,o=testz!cn=bob_gon,ou=query_groups,o=test) rzrrtr°rrr{rr’rîrˇ)rr3r;rďr$r$r%Útest_or_group_queryds   zLDAPTest.test_or_group_queryc Csś|jdddtdtjdƒdtddƒƒ|jjd d d d ƒ}|jjd d d d ƒ}td ƒtdƒ@}|j|j |j ƒƒ|j |j |j ƒƒdS)Nrxzuid=%(user)s,ou=people,o=testrčzou=query_groups,o=testz(objectClass=groupOfNames)réręrEryr3r4r;z#cn=alice_gon,ou=query_groups,o=testz$cn=mutual_gon,ou=query_groups,o=test) rzrrtr°rrr{rr’rîrˇrˆ)rr3r;rďr$r$r%Útest_and_group_queryts   zLDAPTest.test_and_group_queryc CsŔ|jdddtdtjdƒdtddƒƒ|jjd d d d ƒ}|jjd d d d ƒ}td ƒtdƒ@tdƒB}|j|j |j ƒƒ|j|j |j ƒƒdS)Nrxzuid=%(user)s,ou=people,o=testrčzou=query_groups,o=testz(objectClass=groupOfNames)réręrEryr3r4r;z#cn=alice_gon,ou=query_groups,o=testz$cn=mutual_gon,ou=query_groups,o=testz!cn=bob_gon,ou=query_groups,o=test) rzrrtr°rrr{rr’rîrˇ)rr3r;rďr$r$r%Útest_nested_group_query„s    z LDAPTest.test_nested_group_queryc Cs¤tdƒtdƒ@}|jdddtdtjdƒdtd d ƒd |ƒ|jjd d ddƒ}|jjd dddƒ}|j|ƒ|j |ƒdS)Nz#cn=alice_gon,ou=query_groups,o=testz$cn=mutual_gon,ou=query_groups,o=testrxzuid=%(user)s,ou=people,o=testrčzou=query_groups,o=testz(objectClass=groupOfNames)réręrErëryr3r4r;) rrzrrtr°rrr{r­r™)rrďr3r;r$r$r%Ú!test_require_group_as_group_query—s    z*LDAPTest.test_require_group_as_group_queryc Csż|jdddttdtjdƒtdtjdƒƒdtdd ƒd d ƒ|jjd d ddƒ}|jjd dddƒ}|j|ƒ|j |ƒ|j |j j dhƒdS)Nrxzuid=%(user)s,ou=people,o=testrčzou=groups,o=testz(objectClass=groupOfNames)zou=moregroups,o=testréręrErëz!cn=other_gon,ou=moregroups,o=testryr3r4r;rI) rzrrrtr°rrr{r™r­r|rˇÚ group_names)rr3r;r$r$r%Útest_group_unionŠs   zLDAPTest.test_group_unionc Csż|jdddttdtjdƒtdtjdƒƒdtdd ƒd d ƒ|jjd d ddƒ}|jjd dddƒ}|j|ƒ|j |ƒ|j |j j dhƒdS)Nrxzuid=%(user)s,ou=people,o=testrčzou=groups,o=testz(objectClass=groupOfNames)zou=moregroups,o=testréręrErëz!cn=other_gon,ou=moregroups,o=testryr3r4r;rI) rzrrrtr°rrr{r™r­r|rˇrö)rr3r;r$r$r%Útest_nested_group_unionťs   z LDAPTest.test_nested_group_unionc Csź|jdddtdtjƒdtddƒdd ƒ|jjd d d d ƒ}|jjd d d d ƒ}|j|ƒ|j|ƒ|j |j j ƒddddddddgƒdS)Nrxzuid=%(user)s,ou=people,o=testrčzou=groups,o=testréręrEZ DENY_GROUPzcn=active_gon,ou=groups,o=testryr3r4r;rr‚rě) rzrrtr°rrr{r™r­r|rrr„)rr3r;r$r$r%Útest_denied_groupÍs     zLDAPTest.test_denied_groupc Cs‹|jdddtdtjƒdtddƒƒ|jjdd d d ƒ}|j|jj d d „|j |j |j |j gDƒƒdS) Nrxzuid=%(user)s,ou=people,o=testrčzou=groups,o=testréręrEryr3r4cSs h|]}|djƒ’qS)r)Úlower)Ú.0Úgr$r$r%ú ęs z*LDAPTest.test_group_dns..)rzrrtr°rrr{r|rˇZ group_dnsrFrGrHrU)rr3r$r$r%Útest_group_dnsŕs  zLDAPTest.test_group_dnsc Csr|jdddtdtjƒdtddƒƒ|jjdd d d ƒ}|j|jj d d d dhƒdS)Nrxzuid=%(user)s,ou=people,o=testrčzou=groups,o=testréręrEryr3r4rFrGrHrU) rzrrtr°rrr{r|rˇrö)rr3r$r$r%Útest_group_namesís  zLDAPTest.test_group_namescCsď|jdddtdtjƒdtddƒdd td ƒd d d gddiƒ|jjddddƒ}|jjddddƒ}|j|j ƒ|j|j ƒ|j|j ƒ|j |j ƒ|j |j ƒ|j |j ƒdS)Nrxzuid=%(user)s,ou=people,o=testrčzou=groups,o=testréręrEÚUSER_FLAGS_BY_GROUPÚ is_activezcn=active_gon,ou=groups,o=testÚis_staffzcn=empty_gon,ou=groups,o=testzcn=staff_gon,ou=groups,o=testÚ is_superuserz!cn=superuser_gon,ou=groups,o=testryr3r4r;) rzrrtr°rrrr{r’rrrrˆ)rr3r;r$r$r%Útest_dn_group_membership÷s    z!LDAPTest.test_dn_group_membershipcCsƒ|jdddtdtjƒdtddƒdd td ƒd gd d iƒ|jtƒ|jj ddddƒWdQRXdS)Nrxzuid=%(user)s,ou=people,o=testrčzou=groups,o=testréręrErrzcn=active_gon,ou=groups,o=testrrz!cn=superuser_gon,ou=groups,o=testryr3r4) rzrrtr°rrrăr rr{)rr$r$r%Útest_user_flags_misconfigureds   z&LDAPTest.test_user_flags_misconfiguredcCsÝ|jdddtdtjƒdtƒdddd d d d iƒ|jjd dddƒ}|jjd dddƒ}|j|jƒ|j|j ƒ|j|j ƒ|j |jƒ|j |j ƒ|j |j ƒdS)Nrxzuid=%(user)s,ou=people,o=testrčzou=groups,o=testrérrzcn=active_px,ou=groups,o=testrzcn=staff_px,ou=groups,o=testrz cn=superuser_px,ou=groups,o=testryr3r4r;) rzrrtr°rrr{r’rrrrˆ)rr3r;r$r$r%Útest_posix_memberships   zLDAPTest.test_posix_membershipcCsÝ|jdddtdtjƒdtƒdddd d d d iƒ|jjd dddƒ}|jjd dddƒ}|j|jƒ|j|j ƒ|j|j ƒ|j |jƒ|j |j ƒ|j |j ƒdS)Nrxzuid=%(user)s,ou=people,o=testrčzou=groups,o=testrérrzcn=active_nis,ou=groups,o=testrzcn=staff_nis,ou=groups,o=testrz!cn=superuser_nis,ou=groups,o=testryr3r4r;) rzrrtr°rrr{r’rrrrˆ)rr3r;r$r$r%Útest_nis_membership3s   zLDAPTest.test_nis_membershipc Cs˝|jdddtdtjƒdtddƒdd d d d iƒ|jjd d ddƒ}|jjd dddƒ}|j|jƒ|j|j ƒ|j |jƒ|j |j ƒdS)Nrxzuid=%(user)s,ou=people,o=testrčzou=groups,o=testréręrErrzcn=parent_gon,ou=groups,o=testrryr3r4r;) rzrrtr°rrr{r’rrrˆ)rr3r;r$r$r%Útest_nested_dn_group_membershipIs  z(LDAPTest.test_nested_dn_group_membershipc Csf|jdddtdtjƒdtƒdddiƒ|jjd d d d ƒ}|j|jƒdS) Nrxzuid=%(user)s,ou=people,o=testrčzou=groups,o=testrérrzcn=active_px,ou=groups,o=testryr>r4) rzrrtr°rrr{rˆr)rr>r$r$r%Útest_posix_missing_attributes[s   z&LDAPTest.test_posix_missing_attributesc Csë|jdddtdtjƒdtddƒdd ƒ|jƒtjjd d ƒ}|j j |j ƒ}|j |j j |ƒd d hƒ|j |j j|ƒd d hƒ|j|j j|d ƒƒ|j|j j|dƒƒdS)Nrxzuid=%(user)s,ou=people,o=testrčzou=groups,o=testréręrEÚFIND_GROUP_PERMSTryr3z auth.add_userzauth.change_userr“)rzrrtr°rÚ _init_groupsr r†rŚrr˘r§r|Úget_group_permissionsÚget_all_permissionsr’Úhas_permÚhas_module_perms)rr3r$r$r%Útest_dn_group_permissionsis  ""z"LDAPTest.test_dn_group_permissionsc Csš|jdddddddtdtjƒd td d ƒd d ƒ|jƒtjjddƒ}|j j |j ƒ}|j |j j |ƒtƒƒdS)NrŔzuid=bob,ou=people,o=testrÁrŤrxzuid=%(user)s,ou=people,o=testrčzou=groups,o=testréręrEr Tryr3)rzrrtr°rr r r†rŚrr˘r§r|r Úset)rr3r$r$r%Ú!test_group_permissions_ldap_errorzs  z*LDAPTest.test_group_permissions_ldap_errorc Csĺ|jdddtdtjƒdtddƒdd ƒ|jƒtjjd d ƒ}|j j |j ƒ}|j |j j |ƒtƒƒ|j |j j|ƒtƒƒ|j|j j|d ƒƒ|j|j j|d ƒƒdS)Nrxzuid=%(user)s,ou=people,o=testrčzou=groups,o=testréręrEr Tryr;z auth.add_userr“)rzrrtr°rr r r†rŚrr˘r§r|r rr rˆrr)rr;r$r$r%Útest_empty_group_permissionsŠs  z%LDAPTest.test_empty_group_permissionsc Csč|jdddtdtjdƒdtƒddƒ|jƒtjjd d ƒ}|j j |j ƒ}|j |j j |ƒd d hƒ|j |j j|ƒd d hƒ|j|j j|d ƒƒ|j|j j|d ƒƒdS)Nrxzuid=%(user)s,ou=people,o=testrčzou=groups,o=testz(objectClass=posixGroup)rér Tryr3z auth.add_userzauth.change_userr“)rzrrtr°rr r r†rŚrr˘r§r|r r r’rr)rr3r$r$r%Útest_posix_group_permissions›s     ""z%LDAPTest.test_posix_group_permissionsc Cs=|jdddtdtjdƒdtƒddƒ|jƒ|jj|jd tj d dfgƒ|jj|j d tj d d gfgƒt j jd d ƒ}|jj|jƒ}|j|jj|ƒddhƒ|j|jj|ƒddhƒ|j|jj|dƒƒ|j|jj|dƒƒdS)Nrxzuid=%(user)s,ou=people,o=testrčzou=groups,o=testz(objectClass=posixGroup)rér Trr6rAr3ryz auth.add_userzauth.change_userr“)rzrrtr°rr rrZmodify_sr3Z MOD_DELETEr@ZMOD_ADDr r†rŚrr˘r§r|r r r’rr)rr3r$r$r%Ú#test_posix_group_permissions_no_gid­s     ),""z,LDAPTest.test_posix_group_permissions_no_gidc Csč|jdddtdtjdƒdtƒddƒ|jƒtjjd d ƒ}|j j |j ƒ}|j |j j |ƒd d hƒ|j |j j|ƒd d hƒ|j|j j|d ƒƒ|j|j j|d ƒƒdS)Nrxzuid=%(user)s,ou=people,o=testrčzou=groups,o=testz(objectClass=nisNetgroup)rér Tryr3z auth.add_userzauth.change_userr“)rzrrtr°rr r r†rŚrr˘r§r|r r r’rr)rr3r$r$r%Útest_nis_group_permissionsÁs     ""z#LDAPTest.test_nis_group_permissionsc Csy|jdddtdtjƒdtddƒdd ƒ|jƒtjjd d ƒ}|j |j j |ƒt ƒƒdS) Nrxzuid=%(user)s,ou=people,o=testrčzou=groups,o=testréręrEr Tryr3) rzrrtr°rr r r†rŚr|rr r)rr3r$r$r%Útest_foreign_user_permissionsÓs  z&LDAPTest.test_foreign_user_permissionsc Cs"|jdddtdtjƒdtddƒdd d d ƒ|jƒtjjd d ƒj }tjjd d ƒj }xyt dƒD]k}|j j |ƒ}|j |j j|ƒddhƒ|j j |ƒ}|j |j j|ƒtƒƒq„W|j |jjƒddddddgƒdS)Nrxzuid=%(user)s,ou=people,o=testrčzou=groups,o=testréręrEr TZ CACHE_GROUPSryr3r;éz auth.add_userzauth.change_userrr‚rą)rzrrtr°rr r r†rŚr§Úrangerr˘r|r rrrr„)rZalice_idZbob_idÚir3r;r$r$r%Útest_group_cacheŕs(   #  zLDAPTest.test_group_cachec Csł|jdddtdtjdƒdtƒddƒ|jtjjƒd ƒ|j j d d d d ƒ}|jtjjƒd ƒ|jt |j j ƒƒt tjj ƒƒƒdS)Nrxzuid=%(user)s,ou=people,o=testrčzou=groups,o=testz(objectClass=posixGroup)réÚ MIRROR_GROUPSTrryr3r4rÖ)rzrrtr°rr|rr†r‡rr{rr/Úall)rr3r$r$r%Útest_group_mirroring˙s    zLDAPTest.test_group_mirroringc CsÇ|jdddtdtjdƒdtddƒd d ƒ|jjd d d d ƒ}|jtt j j ƒj ddd ƒƒddddddhƒ|jt|j j ƒƒtt j j ƒƒƒdS)Nrxzuid=%(user)s,ou=people,o=testrčzou=groups,o=testz(objectClass=groupOfNames)réręrErTryr3r4r!ÚflatrFrGrHrUrTrV)rzrrtr°rrr{r|rrr†rÚ values_listr/)rr3r$r$r%Útest_nested_group_mirrorings   !  z$LDAPTest.test_nested_group_mirroringc Csý|jdddtdtjdƒdtƒddd gƒi}x=d d „td d ƒDƒD]}tjjd|ƒ||6szALDAPTest.test_group_mirroring_whitelist_update..rrľr!r3rPryr4rT)rzrrtr°rrrr†rŚrrŇr/rr{r|r )rr/r!r3r$r$r%Ú%test_group_mirroring_whitelist_update,s     #z.LDAPTest.test_group_mirroring_whitelist_updatec Csý|jdddtdtjdƒdtƒddd gƒi}x=d d „td d ƒDƒD]}tjjd|ƒ||.rrľr!r3rOryr4rT)rzrrtr°rrrr†rŚrrŇr/rr{r|r )rr/r!r3r$r$r%Ú#test_group_mirroring_whitelist_noopBs     #z,LDAPTest.test_group_mirroring_whitelist_noopc Csý|jdddtdtjdƒdtƒddd gƒi}x=d d „td d ƒDƒD]}tjjd|ƒ||.rrľr!r3rPryr4rTrO)rzrrtr°rrrr†rŚrrŇr/rr{r|r )rr/r!r3r$r$r%Ú%test_group_mirroring_blacklist_updateXs     #z.LDAPTest.test_group_mirroring_blacklist_updatec Csý|jdddtdtjdƒdtƒddd gƒi}x=d d „td d ƒDƒD]}tjjd|ƒ||.rrľr!r3rOryr4rT)rzrrtr°rrrr†rŚrrŇr/rr{r|r )rr/r!r3r$r$r%Ú#test_group_mirroring_blacklist_noopns     #z,LDAPTest.test_group_mirroring_blacklist_noopc Cs‚|jdddtdtjƒdtddƒdd d d ƒ|jƒtjjd d ƒ}|j |j j |ƒd dhƒdS)Nrxzuid=%(user)s,ou=people,o=testrčzou=groups,o=testréręrEr TÚAUTHORIZE_ALL_USERSryr3z auth.add_userzauth.change_user) rzrrtr°rr r r†rŚr|rr )rr3r$r$r%Útest_authorize_external_users„s  z&LDAPTest.test_authorize_external_usersc CsŽ|jdtdtjdƒdtdtjƒdtddƒd d d d ƒ|jƒtjjd d ƒ}|j |j j |ƒt ƒƒdS)NrŻzou=people,o=testz(uid=%(user)s)rčzou=groups,o=testréręrEr Tr)ryz not-in-ldap) rzrrtr°rr r r†rŚr|rr r)rr3r$r$r%Útest_authorize_external_unknown’s  z(LDAPTest.test_authorize_external_unknowncCsţ|jddƒ|jjdƒ}|jjdƒ}|j|ƒ|j|jdƒ|j|jdƒ|j|jƒ|j |j ƒ|j |j ƒ|j|ƒ|j|jdƒ|j|jdƒ|j|jƒ|j |j ƒ|j |j ƒdS)Nrxzuid=%(user)s,ou=people,o=testr3r;rť) rzrrŇr­r|rÇrČr’rrˆrr)rr3r;r$r$r%Útest_create_without_auth˘s    z!LDAPTest.test_create_without_authcCso|jddddddddd id td tjƒd tƒd ddddddiƒtjjddƒtjjddƒ|jj dƒ}|jj dƒ}|j |ƒ|j |j dƒ|j |j dƒ|j|jƒ|j|jƒ|j|jƒ|j |ƒ|j |j dƒ|j |j dƒ|j|jƒ|j|jƒ|j|jƒdS)Nrxzuid=%(user)s,ou=people,o=testrćFržrÇr7rČr9rčzou=groups,o=testrérrzcn=active_gon,ou=groups,o=testrzcn=staff_gon,ou=groups,o=testrz!cn=superuser_gon,ou=groups,o=testryr3r;r8r:r<r=)rzrrtr°rr r†rŚrrŇr­r|rÇrČr’rrrrˆ)rr3r;r$r$r%Útest_populate_without_authˇs2     z#LDAPTest.test_populate_without_authcCs3|jddƒ|jjdƒ}|j|ƒdS)Nrxzuid=%(user)s,ou=people,o=testrŤ)rzrrŇr™)rrŤr$r$r%Útest_populate_bogus_userŘs z!LDAPTest.test_populate_bogus_usercCsY|jddddƒ|j|jjƒ|jjddddƒ|j|jjƒdS)Nrxzuid=%(user)s,ou=people,o=testÚ START_TLSFryr3r4)rzrˆrrÚ tls_enabledrr{)rr$r$r%Útest_start_tls_missingás  zLDAPTest.test_start_tls_missingcCsY|jddddƒ|j|jjƒ|jjddddƒ|j|jjƒdS)Nrxzuid=%(user)s,ou=people,o=testr/Tryr3r4)rzrˆrrr0rr{r’)rr$r$r%Útest_start_tlsës  zLDAPTest.test_start_tlscCs<|jdtdtjdƒƒ|jjddddƒdS)z: Make sure we're not phased by referrals. rŻzou=people,o=testz(uid=%(user)s)ryr3r4N)rzrrtr°rr{)rr$r$r%Útest_null_search_resultsős z!LDAPTest.test_null_search_resultsc Cs‘|jdttdtjdƒtdtjdƒƒƒ|jjddddƒ}|j|ƒ|j|j j ƒdd d d d d d gƒdS) NrŻzou=groups,o=testz(uid=%(user)s)zou=people,o=testryr3r4rr‚ÚsearchÚresult) rzrrrtr°rr{r­r|rrr„)rr3r$r$r%Útest_union_searchs   zLDAPTest.test_union_searchcCsU|jddƒ|jjddddƒ}|j|ƒ|j|jjƒgƒdS)Nrxzuid=%(user)s,ou=people,o=testryr3r4rť)rzrr{r™r|rrr„)rr3r$r$r%Útest_deny_empty_passwords   z!LDAPTest.test_deny_empty_passwordcCsa|jddddƒ|jjddddƒ}|j|ƒ|j|jjƒd d gƒdS) Nrxzuid=%(user)s,ou=people,o=testÚPERMIT_EMPTY_PASSWORDTryr3r4rťrr‚)rzrr{r™r|rrr„)rr3r$r$r%Útest_permit_empty_passwords   z#LDAPTest.test_permit_empty_passwordcCsa|jddddƒ|jjddddƒ}|j|ƒ|j|jjƒdd gƒdS) Nrxzuid=%(user)s,ou=people,o=testr8Tryr3r4rr‚)rzrr{r™r|rrr„)rr3r$r$r%Útest_permit_null_password)s   z"LDAPTest.test_permit_null_passwordc Cs%|jdddtdtjƒdtddƒdd ƒ|jƒ|jjd d d d ƒ}tj |tj ƒ}tj |ƒ}|j jj |j j_ |j|ƒ|j|jj|ƒd dhƒ|j|jj|ƒd dhƒ|j|jj|d ƒƒ|j|jj|dƒƒdS)Nrxzuid=%(user)s,ou=people,o=testrčzou=groups,o=testréręrEr Tryr3r4z auth.add_userzauth.change_userr“)rzrrtr°rr rr{ÚpickleÚdumpsÚHIGHEST_PROTOCOLÚloadsrˇÚsettingsr­r|r r r’rr)rZalice0Zpickledr3r$r$r%Ú test_pickle7s   ""zLDAPTest.test_picklecCsrtdtjdddgƒ}|j|jƒ|j|jjddƒddtjdddgfifgƒdS)Nzou=people,o=testz (uid=alice)rÔrŐr€Trą)rrtr°Úexecuterrr|r„)rr4r$r$r%Útest_search_attrlistLs zLDAPTest.test_search_attrlistc sŽG‡fdd†dtjƒ‰ˆƒ|_|jddƒtjddƒ-}tjdƒ|jjdd d d ƒWdQRX|jt|ƒd ƒ|j|d j t ƒdS) Ncs%eZdZ‡‡fdd†Z‡S)z>LDAPTest.test_get_or_create_user_deprecated..MyBackendcstˆ|ƒj||ƒS)N)ÚsuperÚget_or_create_user)rryrˇ)rŽÚ __class__r$r%rDVszQLDAPTest.test_get_or_create_user_deprecated..MyBackend.get_or_create_user)r'r(r)rDr$)rŽ)rEr%rŽUs rŽrxzuid=%(user)s,ou=people,o=testÚrecordTÚalwaysryr3r4rr) rrsrzrkÚcatch_warningsÚ simplefilterr{r|ÚlenrgÚDeprecationWarning)rÚwr$)rŽr%Ú"test_get_or_create_user_deprecatedTs   z+LDAPTest.test_get_or_create_user_deprecatedc Cs‘Gdd„dtjƒ}|ƒ|_|jddƒtjddƒ-}tjdƒ|jjdd d d ƒWdQRX|jt|ƒd ƒdS) Nc@seZdZdS)zPLDAPTest.test_custom_backend_without_get_or_create_no_warning..MyBackendN)r'r(r)r$r$r$r%rŽcs rŽrxzuid=%(user)s,ou=people,o=testrFTrGryr3r4r) rrsrzrkrHrIr{r|rJ)rrŽrLr$r$r%Ú4test_custom_backend_without_get_or_create_no_warningbs   z=LDAPTest.test_custom_backend_without_get_or_create_no_warningcspG‡fdd†dtjƒ‰ˆƒ|_|jddƒ|jjddddƒ}|j|jjdƒdS) Ncs%eZdZ‡‡fdd†Z‡S)zGLDAPTest.test_override_authenticate_access_ldap_user..MyBackendcs"d|_tˆ|ƒj||ƒS)NÚbar)ÚfoorCÚauthenticate_ldap_user)rrˇr4)rŽrEr$r%rQps z^LDAPTest.test_override_authenticate_access_ldap_user..MyBackend.authenticate_ldap_user)r'r(r)rQr$)rŽ)rEr%rŽos rŽrxzuid=%(user)s,ou=people,o=testryr3r4rO)rrsrzr{r|rˇrP)rr‹r$)rŽr%Ú+test_override_authenticate_access_ldap_userns  z4LDAPTest.test_override_authenticate_access_ldap_usercKst||j_dS)N)rrr?)rr r$r$r%rz}szLDAPTest._init_settingscCstjjddƒtjjddƒg}tjjddƒ}|jj|Œtjjddƒ}|jj|Œtjjddƒ}|jj|ŒdS)NÚcodenameZadd_userZ change_userr!rFr@rQ)rr†rrrŚÚ permissionsÚadd)rrTrFr@rQr$r$r%r €szLDAPTest._init_groupsN)†r'r(r)Útopr.r/r0r1r3r;rZdresslerr>r@rBrCrDrFrGrHrIrJrKrLrMrNrOrPrQrRrSrTrUrVÚdictrjÚ classmethodrernrorurwr}r…rŒrr rrtr°r”r–ršr›rœrĄr¤r¨rŠrŞrŹrŽrłr¸ršrźr˝ržrżrĹrĆrÉrĘrËrÎrŃrÓr×rŘrßrärĺrçrírđrńrňrórôrőr÷rřrůrţr˙rrrrrr rrrrrrrrrr!r$r%r'r(r*r+r,r-r.r1r2r3r6r7r9r:r@rBrMrNrRrzr r$r$r$r%r+Fsź                                                                     **                                                       !        r+)-Ú __future__rrrrÚcopyrrXr;ÚunittestrkrtÚdjango.contrib.auth.modelsrrr Údjango.core.cacher Údjango.core.exceptionsr Ú django.testr Údjango.test.utilsr Údjango.utils.encodingrrWrZdjango_auth_ldap.configrrrrrrrrÚmodelsrriÚ ImportErrorZ LDAPSettingsrÚskipIfr+r$r$r$r%Ús,"     :   "django-auth-ldap-1.4.0/tests/__pycache__/models.cpython-34.pyc0000644000076600000240000000245613255034645024722 0ustar psagersstaff00000000000000î |`NZ™ă@s\ddlmZmZmZmZddlmZddlmZGdd„deƒZ dS)é)Úabsolute_importÚdivisionÚprint_functionÚunicode_literals)ÚAbstractBaseUser)Úmodelsc@seZdZejddddddƒZejƒZdZdd„Z d d „Z d d „Z d d„Z e e e ƒZdS)ÚTestUserÚ max_lengthé(ÚuniqueTÚdb_indexÚ identifiercCs|jS)N)r )ÚselfŠrú8/Users/psagers/Projects/django-auth-ldap/tests/models.pyÚ get_full_name szTestUser.get_full_namecCs|jS)N)r )rrrrÚget_short_nameszTestUser.get_short_namecCsdS)NZAlicer)rrrrÚget_first_nameszTestUser.get_first_namecCstdƒ‚dS)NzOops...)Ú Exception)rÚvaluerrrÚset_first_nameszTestUser.set_first_nameN)Ú__name__Ú __module__Ú __qualname__rÚ CharFieldr Ú IntegerFieldZ uid_numberÚUSERNAME_FIELDrrrrÚpropertyÚ first_namerrrrrs      rN) Ú __future__rrrrÚdjango.contrib.auth.modelsrÚ django.dbrrrrrrÚs"django-auth-ldap-1.4.0/tests/__pycache__/settings.cpython-35.pyc0000644000076600000240000000155113255034675025276 0ustar psagersstaff00000000000000 ČĂËY“ă@s€ddlmZmZmZmZdddiiZgZdZdZdZ dZ dZ d Z dZ gZdZddgZdS)é)Úabsolute_importÚdivisionÚprint_functionÚunicode_literalsÚdefaultÚENGINEzdjango.db.backends.sqlite3ÚUTCzen-usFTz2nt56v8)moa)37ta5z7dd=if-@y#k@l7+t8lct*c8m730lpd=soúdjango.contrib.authúdjango.contrib.contenttypesúdjango.contrib.sessionsÚtestsz auth.Userz$django_auth_ldap.backend.LDAPBackendz)django.contrib.auth.backends.ModelBackendN)r r r r )Ú __future__rrrrÚ DATABASESÚ ALLOWED_HOSTSÚ TIME_ZONEÚ LANGUAGE_CODEÚUSE_I18NÚUSE_L10NÚUSE_TZÚ SECRET_KEYÚINSTALLED_APPSÚMIDDLEWARE_CLASSESÚAUTH_USER_MODELÚAUTHENTICATION_BACKENDSŠrrú:/Users/psagers/Projects/django-auth-ldap/tests/settings.pyÚs""django-auth-ldap-1.4.0/tests/__pycache__/settings.cpython-34.pyc0000644000076600000240000000155513255034645025276 0ustar psagersstaff00000000000000î ČĂËY“ă@s‚ddlmZmZmZmZiidd6d6ZgZdZdZdZ dZ dZ d Z dZ gZdZddgZdS)é)Úabsolute_importÚdivisionÚprint_functionÚunicode_literalszdjango.db.backends.sqlite3ÚENGINEÚdefaultÚUTCzen-usFTz2nt56v8)moa)37ta5z7dd=if-@y#k@l7+t8lct*c8m730lpd=soúdjango.contrib.authúdjango.contrib.contenttypesúdjango.contrib.sessionsÚtestsz auth.Userz$django_auth_ldap.backend.LDAPBackendz)django.contrib.auth.backends.ModelBackendN)r r r r )Ú __future__rrrrÚ DATABASESÚ ALLOWED_HOSTSÚ TIME_ZONEÚ LANGUAGE_CODEÚUSE_I18NÚUSE_L10NÚUSE_TZÚ SECRET_KEYÚINSTALLED_APPSÚMIDDLEWARE_CLASSESÚAUTH_USER_MODELÚAUTHENTICATION_BACKENDSŠrrú:/Users/psagers/Projects/django-auth-ldap/tests/settings.pyÚs$"django-auth-ldap-1.4.0/tests/__pycache__/settings.cpython-36.pyc0000644000076600000240000000150113255034721025262 0ustar psagersstaff000000000000003 ČĂËY“ă@sXddlmZmZmZmZdddiiZgZdZdZdZ dZ dZ d Z dZ gZdZddgZdS)é)Úabsolute_importÚdivisionÚprint_functionÚunicode_literalsÚdefaultÚENGINEzdjango.db.backends.sqlite3ÚUTCzen-usFTz2nt56v8)moa)37ta5z7dd=if-@y#k@l7+t8lct*c8m730lpd=soúdjango.contrib.authúdjango.contrib.contenttypesúdjango.contrib.sessionsÚtestsz auth.Userz$django_auth_ldap.backend.LDAPBackendz)django.contrib.auth.backends.ModelBackendN)r r r r )Ú __future__rrrrÚ DATABASESÚ ALLOWED_HOSTSÚ TIME_ZONEÚ LANGUAGE_CODEÚUSE_I18NÚUSE_L10NÚUSE_TZÚ SECRET_KEYÚINSTALLED_APPSÚMIDDLEWARE_CLASSESÚAUTH_USER_MODELÚAUTHENTICATION_BACKENDSŠrrú:/Users/psagers/Projects/django-auth-ldap/tests/settings.pyÚs" django-auth-ldap-1.4.0/tests/__pycache__/__init__.cpython-36.pyc0000644000076600000240000000021513155540727025171 0ustar psagersstaff000000000000003 —?¤Yă@sdS)NŠrrrú:/Users/psagers/Projects/django-auth-ldap/tests/__init__.pyÚsdjango-auth-ldap-1.4.0/tests/__pycache__/__init__.cpython-35.pyc0000644000076600000240000000021513155541015025157 0ustar psagersstaff00000000000000 —?¤Yă@sdS)NŠrrrú:/Users/psagers/Projects/django-auth-ldap/tests/__init__.pyÚsdjango-auth-ldap-1.4.0/tests/__pycache__/__init__.cpython-34.pyc0000644000076600000240000000021513155540773025170 0ustar psagersstaff00000000000000î —?¤Yă@sdS)NŠrrrú:/Users/psagers/Projects/django-auth-ldap/tests/__init__.pyÚsdjango-auth-ldap-1.4.0/tests/settings.py0000644000076600000240000000122313162741710020777 0ustar psagersstaff00000000000000from __future__ import absolute_import, division, print_function, unicode_literals DATABASES = { 'default': { 'ENGINE': 'django.db.backends.sqlite3', } } ALLOWED_HOSTS = [] TIME_ZONE = 'UTC' LANGUAGE_CODE = 'en-us' USE_I18N = False USE_L10N = False USE_TZ = True SECRET_KEY = 'nt56v8)moa)37ta5z7dd=if-@y#k@l7+t8lct*c8m730lpd=so' INSTALLED_APPS = ( 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'tests', ) MIDDLEWARE_CLASSES = [] AUTH_USER_MODEL = 'auth.User' AUTHENTICATION_BACKENDS = [ 'django_auth_ldap.backend.LDAPBackend', 'django.contrib.auth.backends.ModelBackend', ] django-auth-ldap-1.4.0/tests/__init__.pyc0000644000076600000240000000022113155540542021040 0ustar psagersstaff00000000000000ó —?¤Yc@sdS(N((((s:/Users/psagers/Projects/django-auth-ldap/tests/__init__.pytsdjango-auth-ldap-1.4.0/tests/tests.py0000644000076600000240000017477313253750522020330 0ustar psagersstaff00000000000000# coding: utf-8 # Copyright (c) 2009, Peter Sagerson # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # # - Redistributions of source code must retain the above copyright notice, this # list of conditions and the following disclaimer. # # - Redistributions in binary form must reproduce the above copyright notice, # this list of conditions and the following disclaimer in the documentation # and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. from __future__ import absolute_import, division, print_function, unicode_literals from copy import deepcopy import logging import pickle import unittest import warnings import ldap from django.contrib.auth.models import Group, Permission, User from django.core.cache import cache from django.core.exceptions import ImproperlyConfigured from django.test import TestCase from django.test.utils import override_settings from django.utils.encoding import force_str from django_auth_ldap import backend from django_auth_ldap.config import ( GroupOfNamesType, LDAPGroupQuery, LDAPSearch, LDAPSearchUnion, MemberDNGroupType, NestedMemberDNGroupType, NISGroupType, PosixGroupType ) from .models import TestUser try: import mockldap except ImportError: mockldap = None class TestSettings(backend.LDAPSettings): """ A replacement for backend.LDAPSettings that does not load settings from django.conf. """ def __init__(self, **kwargs): for name, default in self.defaults.items(): value = kwargs.get(name, default) setattr(self, name, value) @unittest.skipIf(mockldap is None, "django_auth_ldap tests require the mockldap package.") class LDAPTest(TestCase): top = ("o=test", {"o": "test"}) people = ("ou=people,o=test", {"ou": "people"}) groups = ("ou=groups,o=test", {"ou": "groups"}) moregroups = ("ou=moregroups,o=test", {"ou": "moregroups"}) mirror_groups = ("ou=mirror_groups,o=test", {"ou": "mirror_groups"}) alice = ("uid=alice,ou=people,o=test", { "uid": ["alice"], "objectClass": ["person", "organizationalPerson", "inetOrgPerson", "posixAccount"], "userPassword": ["password"], "uidNumber": ["1000"], "gidNumber": ["1000"], "givenName": ["Alice"], "sn": ["Adams"] }) bob = ("uid=bob,ou=people,o=test", { "uid": ["bob"], "objectClass": ["person", "organizationalPerson", "inetOrgPerson", "posixAccount"], "userPassword": ["password"], "uidNumber": ["1001"], "gidNumber": ["50"], "givenName": ["Robert"], "sn": ["Barker"] }) dressler = (force_str("uid=dreßler,ou=people,o=test"), { "uid": [force_str("dreßler")], "objectClass": ["person", "organizationalPerson", "inetOrgPerson", "posixAccount"], "userPassword": ["password"], "uidNumber": ["1002"], "gidNumber": ["50"], "givenName": ["Wolfgang"], "sn": [force_str("Dreßler")] }) nobody = ("uid=nobody,ou=people,o=test", { "uid": ["nobody"], "objectClass": ["person", "organizationalPerson", "inetOrgPerson", "posixAccount"], "userPassword": ["password"], "binaryAttr": ["\xb2"] # Invalid UTF-8 }) # posixGroup objects active_px = ("cn=active_px,ou=groups,o=test", { "cn": ["active_px"], "objectClass": ["posixGroup"], "gidNumber": ["1000"], "memberUid": [], }) staff_px = ("cn=staff_px,ou=groups,o=test", { "cn": ["staff_px"], "objectClass": ["posixGroup"], "gidNumber": ["1001"], "memberUid": ["alice"], }) superuser_px = ("cn=superuser_px,ou=groups,o=test", { "cn": ["superuser_px"], "objectClass": ["posixGroup"], "gidNumber": ["1002"], "memberUid": ["alice"], }) # groupOfNames groups empty_gon = ("cn=empty_gon,ou=groups,o=test", { "cn": ["empty_gon"], "objectClass": ["groupOfNames"], "member": [] }) active_gon = ("cn=active_gon,ou=groups,o=test", { "cn": ["active_gon"], "objectClass": ["groupOfNames"], "member": ["uid=alice,ou=people,o=test"] }) staff_gon = ("cn=staff_gon,ou=groups,o=test", { "cn": ["staff_gon"], "objectClass": ["groupOfNames"], "member": ["uid=alice,ou=people,o=test"] }) superuser_gon = ("cn=superuser_gon,ou=groups,o=test", { "cn": ["superuser_gon"], "objectClass": ["groupOfNames"], "member": ["uid=alice,ou=people,o=test"] }) other_gon = ("cn=other_gon,ou=moregroups,o=test", { "cn": ["other_gon"], "objectClass": ["groupOfNames"], "member": ["uid=bob,ou=people,o=test"] }) # groupOfNames objects for LDAPGroupQuery testing alice_gon = ("cn=alice_gon,ou=query_groups,o=test", { "cn": ["alice_gon"], "objectClass": ["groupOfNames"], "member": ["uid=alice,ou=people,o=test"] }) mutual_gon = ("cn=mutual_gon,ou=query_groups,o=test", { "cn": ["mutual_gon"], "objectClass": ["groupOfNames"], "member": ["uid=alice,ou=people,o=test", "uid=bob,ou=people,o=test"] }) bob_gon = ("cn=bob_gon,ou=query_groups,o=test", { "cn": ["bob_gon"], "objectClass": ["groupOfNames"], "member": ["uid=bob,ou=people,o=test"] }) # groupOfNames objects for selective group mirroring. mirror1 = ("cn=mirror1,ou=mirror_groups,o=test", { "cn": ["mirror1"], "objectClass": ["groupOfNames"], "member": ["uid=alice,ou=people,o=test"] }) mirror2 = ("cn=mirror2,ou=mirror_groups,o=test", { "cn": ["mirror2"], "objectClass": ["groupOfNames"], "member": [] }) mirror3 = ("cn=mirror3,ou=mirror_groups,o=test", { "cn": ["mirror3"], "objectClass": ["groupOfNames"], "member": ["uid=alice,ou=people,o=test"] }) mirror4 = ("cn=mirror4,ou=mirror_groups,o=test", { "cn": ["mirror4"], "objectClass": ["groupOfNames"], "member": [] }) # nisGroup objects active_nis = ("cn=active_nis,ou=groups,o=test", { "cn": ["active_nis"], "objectClass": ["nisNetgroup"], "nisNetgroupTriple": ["(,alice,)"] }) staff_nis = ("cn=staff_nis,ou=groups,o=test", { "cn": ["staff_nis"], "objectClass": ["nisNetgroup"], "nisNetgroupTriple": ["(,alice,)"], }) superuser_nis = ("cn=superuser_nis,ou=groups,o=test", { "cn": ["superuser_nis"], "objectClass": ["nisNetgroup"], "nisNetgroupTriple": ["(,alice,)"], }) # Nested groups with a circular reference parent_gon = ("cn=parent_gon,ou=groups,o=test", { "cn": ["parent_gon"], "objectClass": ["groupOfNames"], "member": ["cn=nested_gon,ou=groups,o=test"] }) nested_gon = ("CN=nested_gon,ou=groups,o=test", { "cn": ["nested_gon"], "objectClass": ["groupOfNames"], "member": [ "uid=alice,ou=people,o=test", "cn=circular_gon,ou=groups,o=test" ] }) circular_gon = ("cn=circular_gon,ou=groups,o=test", { "cn": ["circular_gon"], "objectClass": ["groupOfNames"], "member": ["cn=parent_gon,ou=groups,o=test"] }) directory = dict([ top, people, groups, moregroups, mirror_groups, alice, bob, dressler, nobody, active_px, staff_px, superuser_px, empty_gon, active_gon, staff_gon, superuser_gon, other_gon, alice_gon, mutual_gon, bob_gon, mirror1, mirror2, mirror3, mirror4, active_nis, staff_nis, superuser_nis, parent_gon, nested_gon, circular_gon ]) @classmethod def configure_logger(cls): logger = logging.getLogger('django_auth_ldap') formatter = logging.Formatter("LDAP auth - %(levelname)s - %(message)s") handler = logging.StreamHandler() handler.setLevel(logging.DEBUG) handler.setFormatter(formatter) logger.addHandler(handler) logger.setLevel(logging.CRITICAL) @classmethod def setUpClass(cls): cls.configure_logger() cls.mockldap = mockldap.MockLdap(cls.directory) warnings.filterwarnings( 'ignore', category=UnicodeWarning, module='mockldap.ldapobject' ) @classmethod def tearDownClass(cls): del cls.mockldap def setUp(self): cache.clear() self.mockldap.start() self.ldapobj = self.mockldap['ldap://localhost'] self.backend = backend.LDAPBackend() self.backend.ldap # Force global configuration def tearDown(self): self.mockldap.stop() del self.ldapobj # # Tests # def test_options(self): self._init_settings( USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test', CONNECTION_OPTIONS={'opt1': 'value1'} ) self.backend.authenticate(username='alice', password='password') self.assertEqual(self.ldapobj.get_option('opt1'), 'value1') def test_callable_server_uri(self): self._init_settings( SERVER_URI=lambda: 'ldap://ldap.example.com', USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test' ) self.backend.authenticate(username='alice', password='password') ldapobj = self.mockldap['ldap://ldap.example.com'] self.assertEqual( ldapobj.methods_called(with_args=True), [('initialize', ('ldap://ldap.example.com',), {}), ('simple_bind_s', ('uid=alice,ou=people,o=test', 'password'), {})] ) def test_simple_bind(self): self._init_settings( USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test' ) user_count = User.objects.count() user = self.backend.authenticate(username='alice', password='password') self.assertFalse(user.has_usable_password()) self.assertEqual(user.username, 'alice') self.assertEqual(User.objects.count(), user_count + 1) self.assertEqual( self.ldapobj.methods_called(), ['initialize', 'simple_bind_s'] ) def test_default_settings(self): class MyBackend(backend.LDAPBackend): default_settings = { 'USER_DN_TEMPLATE': 'uid=%(user)s,ou=people,o=test', } self.backend = MyBackend() user_count = User.objects.count() user = self.backend.authenticate(username='alice', password='password') self.assertFalse(user.has_usable_password()) self.assertEqual(user.username, 'alice') self.assertEqual(User.objects.count(), user_count + 1) self.assertEqual( self.ldapobj.methods_called(), ['initialize', 'simple_bind_s'] ) @override_settings(AUTH_LDAP_USER_SEARCH=LDAPSearch("ou=people,o=test", ldap.SCOPE_SUBTREE, '(uid=%(user)s)')) def test_login_with_multiple_auth_backends(self): auth = self.client.login(username='alice', password='password') self.assertTrue(auth) @override_settings(AUTH_LDAP_USER_SEARCH=LDAPSearch("ou=people,o=test", ldap.SCOPE_SUBTREE, '(uid=%(user)s)')) def test_bad_login_with_multiple_auth_backends(self): auth = self.client.login(username='invalid', password='i_do_not_exist') self.assertFalse(auth) def test_simple_bind_escaped(self): """ Bind with a username that requires escaping. """ self._init_settings( USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test' ) user = self.backend.authenticate(username='alice,1', password='password') self.assertIsNone(user) self.assertEqual( self.ldapobj.methods_called(with_args=True), [('initialize', ('ldap://localhost',), {}), ('simple_bind_s', ('uid=alice\\,1,ou=people,o=test', 'password'), {})] ) def test_new_user_lowercase(self): self._init_settings( USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test' ) user_count = User.objects.count() user = self.backend.authenticate(username='Alice', password='password') self.assertFalse(user.has_usable_password()) self.assertEqual(user.username, 'alice') self.assertEqual(User.objects.count(), user_count + 1) self.assertEqual( self.ldapobj.methods_called(), ['initialize', 'simple_bind_s'] ) def test_deepcopy(self): self._init_settings( USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test' ) user = self.backend.authenticate(username='Alice', password='password') user = deepcopy(user) @override_settings(AUTH_USER_MODEL='tests.TestUser') def test_auth_custom_user(self): self._init_settings( USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test', USER_ATTR_MAP={'uid_number': 'uidNumber'}, ) user = self.backend.authenticate(username='Alice', password='password') self.assertIsInstance(user, TestUser) @override_settings(AUTH_USER_MODEL='tests.TestUser') def test_get_custom_user(self): self._init_settings( USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test', USER_ATTR_MAP={'uid_number': 'uidNumber'}, ) user = self.backend.authenticate(username='Alice', password='password') user = self.backend.get_user(user.id) self.assertIsInstance(user, TestUser) @override_settings(AUTH_USER_MODEL='tests.TestUser') def test_get_custom_field(self): self._init_settings( USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test', USER_ATTR_MAP={ 'uid_number': 'uidNumber', }, USER_QUERY_FIELD='uid_number', ) alice = TestUser.objects.create(identifier='abcdef', uid_number=1000) user = self.backend.authenticate(username='Alice', password='password') self.assertIsInstance(user, TestUser) self.assertEqual(user.pk, alice.pk) def test_new_user_whitespace(self): self._init_settings( USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test' ) user_count = User.objects.count() user = self.backend.authenticate(username=' alice', password='password') user = self.backend.authenticate(username='alice ', password='password') self.assertFalse(user.has_usable_password()) self.assertEqual(user.username, 'alice') self.assertEqual(User.objects.count(), user_count + 1) def test_simple_bind_bad_user(self): self._init_settings( USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test' ) user_count = User.objects.count() user = self.backend.authenticate(username='evil_alice', password='password') self.assertIsNone(user) self.assertEqual(User.objects.count(), user_count) self.assertEqual( self.ldapobj.methods_called(), ['initialize', 'simple_bind_s'] ) def test_simple_bind_bad_password(self): self._init_settings( USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test' ) user_count = User.objects.count() user = self.backend.authenticate(username='alice', password='bogus') self.assertIsNone(user) self.assertEqual(User.objects.count(), user_count) self.assertEqual( self.ldapobj.methods_called(), ['initialize', 'simple_bind_s'] ) def test_existing_user(self): self._init_settings( USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test' ) User.objects.create(username='alice') user_count = User.objects.count() user = self.backend.authenticate(username='alice', password='password') # Make sure we only created one user self.assertIsNotNone(user) self.assertEqual(User.objects.count(), user_count) def test_existing_user_insensitive(self): self._init_settings( USER_SEARCH=LDAPSearch( "ou=people,o=test", ldap.SCOPE_SUBTREE, '(uid=%(user)s)' ) ) # mockldap doesn't handle case-insensitive matching properly. self.ldapobj.search_s.seed('ou=people,o=test', ldap.SCOPE_SUBTREE, '(uid=Alice)', None)([self.alice]) User.objects.create(username='alice') user = self.backend.authenticate(username='Alice', password='password') self.assertIsNotNone(user) self.assertEqual(user.username, 'alice') self.assertEqual(User.objects.count(), 1) def test_convert_username(self): class MyBackend(backend.LDAPBackend): def ldap_to_django_username(self, username): return 'ldap_%s' % username def django_to_ldap_username(self, username): return username[5:] self.backend = MyBackend() self._init_settings( USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test' ) user_count = User.objects.count() user1 = self.backend.authenticate(username='alice', password='password') user2 = self.backend.get_user(user1.pk) self.assertEqual(User.objects.count(), user_count + 1) self.assertEqual(user1.username, 'ldap_alice') self.assertEqual(user1.ldap_user._username, 'alice') self.assertEqual(user1.ldap_username, 'alice') self.assertEqual(user2.username, 'ldap_alice') self.assertEqual(user2.ldap_user._username, 'alice') self.assertEqual(user2.ldap_username, 'alice') def test_search_bind(self): self._init_settings( USER_SEARCH=LDAPSearch( "ou=people,o=test", ldap.SCOPE_SUBTREE, '(uid=%(user)s)' ) ) user_count = User.objects.count() user = self.backend.authenticate(username='alice', password='password') self.assertIsNotNone(user) self.assertEqual(User.objects.count(), user_count + 1) self.assertEqual( self.ldapobj.methods_called(), ['initialize', 'simple_bind_s', 'search_s', 'simple_bind_s'] ) def test_search_bind_escaped(self): """ Search for a username that requires escaping. """ self._init_settings( USER_SEARCH=LDAPSearch( "ou=people,o=test", ldap.SCOPE_SUBTREE, '(uid=%(user)s)' ) ) user = self.backend.authenticate(username='alice*', password='password') self.assertIsNone(user) self.assertEqual( self.ldapobj.methods_called(with_args=True), [('initialize', ('ldap://localhost',), {}), ('simple_bind_s', ('', ''), {}), ('search_s', ('ou=people,o=test', ldap.SCOPE_SUBTREE, '(uid=alice\\2a)', None), {})] ) def test_search_bind_no_user(self): self._init_settings( USER_SEARCH=LDAPSearch( "ou=people,o=test", ldap.SCOPE_SUBTREE, '(cn=%(user)s)' ) ) user = self.backend.authenticate(username='alice', password='password') self.assertIsNone(user) self.assertEqual( self.ldapobj.methods_called(), ['initialize', 'simple_bind_s', 'search_s'] ) def test_search_bind_multiple_users(self): self._init_settings( USER_SEARCH=LDAPSearch( "ou=people,o=test", ldap.SCOPE_SUBTREE, '(uid=*)' ) ) user = self.backend.authenticate(username='alice', password='password') self.assertIsNone(user) self.assertEqual( self.ldapobj.methods_called(), ['initialize', 'simple_bind_s', 'search_s'] ) def test_search_bind_bad_password(self): self._init_settings( USER_SEARCH=LDAPSearch( "ou=people,o=test", ldap.SCOPE_SUBTREE, '(uid=%(user)s)' ) ) user = self.backend.authenticate(username='alice', password='bogus') self.assertIsNone(user) self.assertEqual( self.ldapobj.methods_called(), ['initialize', 'simple_bind_s', 'search_s', 'simple_bind_s'] ) def test_search_bind_with_credentials(self): self._init_settings( BIND_DN='uid=bob,ou=people,o=test', BIND_PASSWORD='password', USER_SEARCH=LDAPSearch( "ou=people,o=test", ldap.SCOPE_SUBTREE, '(uid=%(user)s)' ) ) user = self.backend.authenticate(username='alice', password='password') self.assertIsNotNone(user) self.assertIsNotNone(user.ldap_user) self.assertEqual(user.ldap_user.dn, self.alice[0]) self.assertEqual(user.ldap_user.attrs, ldap.cidict.cidict(self.alice[1])) self.assertEqual( self.ldapobj.methods_called(), ['initialize', 'simple_bind_s', 'search_s', 'simple_bind_s'] ) def test_search_bind_with_bad_credentials(self): self._init_settings( BIND_DN='uid=bob,ou=people,o=test', BIND_PASSWORD='bogus', USER_SEARCH=LDAPSearch( "ou=people,o=test", ldap.SCOPE_SUBTREE, '(uid=%(user)s)' ) ) user = self.backend.authenticate(username='alice', password='password') self.assertIsNone(user) self.assertEqual( self.ldapobj.methods_called(), ['initialize', 'simple_bind_s'] ) def test_unicode_user(self): self._init_settings( USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test', USER_ATTR_MAP={'first_name': 'givenName', 'last_name': 'sn'} ) user = self.backend.authenticate(username='dreßler', password='password') self.assertIsNotNone(user) self.assertEqual(user.username, 'dreßler') self.assertEqual(user.last_name, 'Dreßler') def test_cidict(self): self._init_settings( USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test', ) user = self.backend.authenticate(username="alice", password="password") self.assertIsInstance(user.ldap_user.attrs, ldap.cidict.cidict) def test_populate_user(self): self._init_settings( USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test', USER_ATTR_MAP={'first_name': 'givenName', 'last_name': 'sn'} ) user = self.backend.authenticate(username='alice', password='password') self.assertEqual(user.username, 'alice') self.assertEqual(user.first_name, 'Alice') self.assertEqual(user.last_name, 'Adams') # init, bind as user, bind anonymous, lookup user attrs self.assertEqual( self.ldapobj.methods_called(), ['initialize', 'simple_bind_s', 'simple_bind_s', 'search_s'] ) def test_populate_user_with_missing_attribute(self): self._init_settings( USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test', USER_ATTR_MAP={ 'first_name': 'givenName', 'last_name': 'sn', 'email': 'mail', } ) user = self.backend.authenticate(username='alice', password='password') self.assertEqual(user.username, 'alice') self.assertEqual(user.first_name, 'Alice') self.assertEqual(user.last_name, 'Adams') self.assertEqual(user.email, '') @override_settings(AUTH_USER_MODEL='tests.TestUser') def test_authenticate_with_buggy_setter_raises_exception(self): self._init_settings( USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test', USER_ATTR_MAP={ 'first_name': 'givenName', 'uid_number': 'uidNumber', }, ) with self.assertRaisesMessage(Exception, 'Oops...'): self.backend.authenticate(username='alice', password='password') @override_settings(AUTH_USER_MODEL='tests.TestUser') def test_populate_user_with_buggy_setter_raises_exception(self): self._init_settings( USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test', USER_ATTR_MAP={ 'first_name': 'givenName', 'uid_number': 'uidNumber', }, ) with self.assertRaisesMessage(Exception, 'Oops...'): self.backend.populate_user('alice') def test_populate_with_attrlist(self): self._init_settings( USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test', USER_ATTR_MAP={'first_name': 'givenName', 'last_name': 'sn'}, USER_ATTRLIST=['*', '+'], ) user = self.backend.authenticate(username='alice', password='password') self.assertEqual(user.username, 'alice') # init, bind as user, bind anonymous, lookup user attrs self.assertEqual( self.ldapobj.methods_called(), ['initialize', 'simple_bind_s', 'simple_bind_s', 'search_s'] ) self.assertEqual( self.ldapobj.methods_called(with_args=True)[3], ('search_s', ('uid=alice,ou=people,o=test', ldap.SCOPE_BASE, '(objectClass=*)', ['*', '+']), {}) ) def test_bind_as_user(self): self._init_settings( USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test', USER_ATTR_MAP={'first_name': 'givenName', 'last_name': 'sn'}, BIND_AS_AUTHENTICATING_USER=True, ) user = self.backend.authenticate(username='alice', password='password') self.assertEqual(user.username, 'alice') self.assertEqual(user.first_name, 'Alice') self.assertEqual(user.last_name, 'Adams') # init, bind as user, lookup user attrs self.assertEqual( self.ldapobj.methods_called(), ['initialize', 'simple_bind_s', 'search_s'] ) def test_signal_populate_user(self): self._init_settings( USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test' ) def handle_populate_user(sender, **kwargs): self.assertIn('user', kwargs) self.assertIn('ldap_user', kwargs) kwargs['user'].populate_user_handled = True backend.populate_user.connect(handle_populate_user) user = self.backend.authenticate(username='alice', password='password') self.assertTrue(user.populate_user_handled) backend.populate_user.disconnect(handle_populate_user) def test_auth_signal_ldap_error(self): self._init_settings( BIND_DN='uid=bob,ou=people,o=test', BIND_PASSWORD='bogus', USER_SEARCH=LDAPSearch( "ou=people,o=test", ldap.SCOPE_SUBTREE, '(uid=%(user)s)' ) ) def handle_ldap_error(sender, **kwargs): self.assertEqual(kwargs['context'], 'authenticate') raise kwargs['exception'] backend.ldap_error.connect(handle_ldap_error) with self.assertRaises(ldap.LDAPError): self.backend.authenticate(username='alice', password='password') backend.ldap_error.disconnect(handle_ldap_error) def test_populate_signal_ldap_error(self): self._init_settings( BIND_DN='uid=bob,ou=people,o=test', BIND_PASSWORD='bogus', USER_SEARCH=LDAPSearch( "ou=people,o=test", ldap.SCOPE_SUBTREE, '(uid=%(user)s)' ) ) user = self.backend.populate_user('alice') self.assertIsNone(user) def test_no_update_existing(self): self._init_settings( USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test', USER_ATTR_MAP={'first_name': 'givenName', 'last_name': 'sn'}, ALWAYS_UPDATE_USER=False ) User.objects.create(username='alice', first_name='Alicia', last_name='Astro') alice = self.backend.authenticate(username='alice', password='password') bob = self.backend.authenticate(username='bob', password='password') self.assertEqual(alice.first_name, 'Alicia') self.assertEqual(alice.last_name, 'Astro') self.assertEqual(bob.first_name, 'Robert') self.assertEqual(bob.last_name, 'Barker') def test_require_group(self): self._init_settings( USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test', GROUP_SEARCH=LDAPSearch('ou=groups,o=test', ldap.SCOPE_SUBTREE, '(objectClass=groupOfNames)'), GROUP_TYPE=MemberDNGroupType(member_attr='member'), REQUIRE_GROUP="cn=active_gon,ou=groups,o=test" ) alice = self.backend.authenticate(username='alice', password='password') bob = self.backend.authenticate(username='bob', password='password') self.assertIsNotNone(alice) self.assertIsNone(bob) self.assertEqual( self.ldapobj.methods_called(), ['initialize', 'simple_bind_s', 'simple_bind_s', 'compare_s', 'initialize', 'simple_bind_s', 'simple_bind_s', 'compare_s'] ) def test_simple_group_query(self): self._init_settings( USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test', GROUP_SEARCH=LDAPSearch('ou=query_groups,o=test', ldap.SCOPE_SUBTREE, '(objectClass=groupOfNames)'), GROUP_TYPE=MemberDNGroupType(member_attr='member'), ) alice = self.backend.authenticate(username='alice', password='password') query = LDAPGroupQuery('cn=alice_gon,ou=query_groups,o=test') self.assertTrue(query.resolve(alice.ldap_user)) def test_negated_group_query(self): self._init_settings( USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test', GROUP_SEARCH=LDAPSearch('ou=query_groups,o=test', ldap.SCOPE_SUBTREE, '(objectClass=groupOfNames)'), GROUP_TYPE=MemberDNGroupType(member_attr='member'), ) alice = self.backend.authenticate(username='alice', password='password') query = ~LDAPGroupQuery('cn=alice_gon,ou=query_groups,o=test') self.assertFalse(query.resolve(alice.ldap_user)) def test_or_group_query(self): self._init_settings( USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test', GROUP_SEARCH=LDAPSearch('ou=query_groups,o=test', ldap.SCOPE_SUBTREE, '(objectClass=groupOfNames)'), GROUP_TYPE=MemberDNGroupType(member_attr='member'), ) alice = self.backend.authenticate(username='alice', password='password') bob = self.backend.authenticate(username='bob', password='password') query = ( LDAPGroupQuery('cn=alice_gon,ou=query_groups,o=test') | LDAPGroupQuery('cn=bob_gon,ou=query_groups,o=test') ) self.assertTrue(query.resolve(alice.ldap_user)) self.assertTrue(query.resolve(bob.ldap_user)) def test_and_group_query(self): self._init_settings( USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test', GROUP_SEARCH=LDAPSearch('ou=query_groups,o=test', ldap.SCOPE_SUBTREE, '(objectClass=groupOfNames)'), GROUP_TYPE=MemberDNGroupType(member_attr='member'), ) alice = self.backend.authenticate(username='alice', password='password') bob = self.backend.authenticate(username='bob', password='password') query = ( LDAPGroupQuery('cn=alice_gon,ou=query_groups,o=test') & LDAPGroupQuery('cn=mutual_gon,ou=query_groups,o=test') ) self.assertTrue(query.resolve(alice.ldap_user)) self.assertFalse(query.resolve(bob.ldap_user)) def test_nested_group_query(self): self._init_settings( USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test', GROUP_SEARCH=LDAPSearch('ou=query_groups,o=test', ldap.SCOPE_SUBTREE, '(objectClass=groupOfNames)'), GROUP_TYPE=MemberDNGroupType(member_attr='member'), ) alice = self.backend.authenticate(username='alice', password='password') bob = self.backend.authenticate(username='bob', password='password') query = ( ( LDAPGroupQuery('cn=alice_gon,ou=query_groups,o=test') & LDAPGroupQuery('cn=mutual_gon,ou=query_groups,o=test') ) | LDAPGroupQuery('cn=bob_gon,ou=query_groups,o=test') ) self.assertTrue(query.resolve(alice.ldap_user)) self.assertTrue(query.resolve(bob.ldap_user)) def test_require_group_as_group_query(self): query = ( LDAPGroupQuery('cn=alice_gon,ou=query_groups,o=test') & LDAPGroupQuery('cn=mutual_gon,ou=query_groups,o=test') ) self._init_settings( USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test', GROUP_SEARCH=LDAPSearch('ou=query_groups,o=test', ldap.SCOPE_SUBTREE, '(objectClass=groupOfNames)'), GROUP_TYPE=MemberDNGroupType(member_attr='member'), REQUIRE_GROUP=query ) alice = self.backend.authenticate(username='alice', password='password') bob = self.backend.authenticate(username='bob', password='password') self.assertIsNotNone(alice) self.assertIsNone(bob) def test_group_union(self): self._init_settings( USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test', GROUP_SEARCH=LDAPSearchUnion( LDAPSearch('ou=groups,o=test', ldap.SCOPE_SUBTREE, '(objectClass=groupOfNames)'), LDAPSearch('ou=moregroups,o=test', ldap.SCOPE_SUBTREE, '(objectClass=groupOfNames)') ), GROUP_TYPE=MemberDNGroupType(member_attr='member'), REQUIRE_GROUP="cn=other_gon,ou=moregroups,o=test" ) alice = self.backend.authenticate(username='alice', password='password') bob = self.backend.authenticate(username='bob', password='password') self.assertIsNone(alice) self.assertIsNotNone(bob) self.assertEqual(bob.ldap_user.group_names, {'other_gon'}) def test_nested_group_union(self): self._init_settings( USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test', GROUP_SEARCH=LDAPSearchUnion( LDAPSearch('ou=groups,o=test', ldap.SCOPE_SUBTREE, '(objectClass=groupOfNames)'), LDAPSearch('ou=moregroups,o=test', ldap.SCOPE_SUBTREE, '(objectClass=groupOfNames)') ), GROUP_TYPE=NestedMemberDNGroupType(member_attr='member'), REQUIRE_GROUP="cn=other_gon,ou=moregroups,o=test" ) alice = self.backend.authenticate(username='alice', password='password') bob = self.backend.authenticate(username='bob', password='password') self.assertIsNone(alice) self.assertIsNotNone(bob) self.assertEqual(bob.ldap_user.group_names, {'other_gon'}) def test_denied_group(self): self._init_settings( USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test', GROUP_SEARCH=LDAPSearch('ou=groups,o=test', ldap.SCOPE_SUBTREE), GROUP_TYPE=MemberDNGroupType(member_attr='member'), DENY_GROUP="cn=active_gon,ou=groups,o=test" ) alice = self.backend.authenticate(username='alice', password='password') bob = self.backend.authenticate(username='bob', password='password') self.assertIsNone(alice) self.assertIsNotNone(bob) self.assertEqual( self.ldapobj.methods_called(), ['initialize', 'simple_bind_s', 'simple_bind_s', 'compare_s', 'initialize', 'simple_bind_s', 'simple_bind_s', 'compare_s'] ) def test_group_dns(self): self._init_settings( USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test', GROUP_SEARCH=LDAPSearch('ou=groups,o=test', ldap.SCOPE_SUBTREE), GROUP_TYPE=MemberDNGroupType(member_attr='member'), ) alice = self.backend.authenticate(username='alice', password='password') self.assertEqual( alice.ldap_user.group_dns, {g[0].lower() for g in [self.active_gon, self.staff_gon, self.superuser_gon, self.nested_gon]} ) def test_group_names(self): self._init_settings( USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test', GROUP_SEARCH=LDAPSearch('ou=groups,o=test', ldap.SCOPE_SUBTREE), GROUP_TYPE=MemberDNGroupType(member_attr='member'), ) alice = self.backend.authenticate(username='alice', password='password') self.assertEqual(alice.ldap_user.group_names, {'active_gon', 'staff_gon', 'superuser_gon', 'nested_gon'}) def test_dn_group_membership(self): self._init_settings( USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test', GROUP_SEARCH=LDAPSearch('ou=groups,o=test', ldap.SCOPE_SUBTREE), GROUP_TYPE=MemberDNGroupType(member_attr='member'), USER_FLAGS_BY_GROUP={ 'is_active': LDAPGroupQuery("cn=active_gon,ou=groups,o=test"), 'is_staff': ["cn=empty_gon,ou=groups,o=test", "cn=staff_gon,ou=groups,o=test"], 'is_superuser': "cn=superuser_gon,ou=groups,o=test" } ) alice = self.backend.authenticate(username='alice', password='password') bob = self.backend.authenticate(username='bob', password='password') self.assertTrue(alice.is_active) self.assertTrue(alice.is_staff) self.assertTrue(alice.is_superuser) self.assertFalse(bob.is_active) self.assertFalse(bob.is_staff) self.assertFalse(bob.is_superuser) def test_user_flags_misconfigured(self): self._init_settings( USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test', GROUP_SEARCH=LDAPSearch('ou=groups,o=test', ldap.SCOPE_SUBTREE), GROUP_TYPE=MemberDNGroupType(member_attr='member'), USER_FLAGS_BY_GROUP={ 'is_active': LDAPGroupQuery("cn=active_gon,ou=groups,o=test"), 'is_staff': [], 'is_superuser': "cn=superuser_gon,ou=groups,o=test" } ) with self.assertRaises(ImproperlyConfigured): self.backend.authenticate(username='alice', password='password') def test_posix_membership(self): self._init_settings( USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test', GROUP_SEARCH=LDAPSearch('ou=groups,o=test', ldap.SCOPE_SUBTREE), GROUP_TYPE=PosixGroupType(), USER_FLAGS_BY_GROUP={ 'is_active': "cn=active_px,ou=groups,o=test", 'is_staff': "cn=staff_px,ou=groups,o=test", 'is_superuser': "cn=superuser_px,ou=groups,o=test" } ) alice = self.backend.authenticate(username='alice', password='password') bob = self.backend.authenticate(username='bob', password='password') self.assertTrue(alice.is_active) self.assertTrue(alice.is_staff) self.assertTrue(alice.is_superuser) self.assertFalse(bob.is_active) self.assertFalse(bob.is_staff) self.assertFalse(bob.is_superuser) def test_nis_membership(self): self._init_settings( USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test', GROUP_SEARCH=LDAPSearch('ou=groups,o=test', ldap.SCOPE_SUBTREE), GROUP_TYPE=NISGroupType(), USER_FLAGS_BY_GROUP={ 'is_active': "cn=active_nis,ou=groups,o=test", 'is_staff': "cn=staff_nis,ou=groups,o=test", 'is_superuser': "cn=superuser_nis,ou=groups,o=test" } ) alice = self.backend.authenticate(username='alice', password='password') bob = self.backend.authenticate(username='bob', password='password') self.assertTrue(alice.is_active) self.assertTrue(alice.is_staff) self.assertTrue(alice.is_superuser) self.assertFalse(bob.is_active) self.assertFalse(bob.is_staff) self.assertFalse(bob.is_superuser) def test_nested_dn_group_membership(self): self._init_settings( USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test', GROUP_SEARCH=LDAPSearch('ou=groups,o=test', ldap.SCOPE_SUBTREE), GROUP_TYPE=NestedMemberDNGroupType(member_attr='member'), USER_FLAGS_BY_GROUP={ 'is_active': "cn=parent_gon,ou=groups,o=test", 'is_staff': "cn=parent_gon,ou=groups,o=test", } ) alice = self.backend.authenticate(username='alice', password='password') bob = self.backend.authenticate(username='bob', password='password') self.assertTrue(alice.is_active) self.assertTrue(alice.is_staff) self.assertFalse(bob.is_active) self.assertFalse(bob.is_staff) def test_posix_missing_attributes(self): self._init_settings( USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test', GROUP_SEARCH=LDAPSearch('ou=groups,o=test', ldap.SCOPE_SUBTREE), GROUP_TYPE=PosixGroupType(), USER_FLAGS_BY_GROUP={ 'is_active': "cn=active_px,ou=groups,o=test" } ) nobody = self.backend.authenticate(username='nobody', password='password') self.assertFalse(nobody.is_active) def test_dn_group_permissions(self): self._init_settings( USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test', GROUP_SEARCH=LDAPSearch('ou=groups,o=test', ldap.SCOPE_SUBTREE), GROUP_TYPE=MemberDNGroupType(member_attr='member'), FIND_GROUP_PERMS=True ) self._init_groups() alice = User.objects.create(username='alice') alice = self.backend.get_user(alice.pk) self.assertEqual(self.backend.get_group_permissions(alice), {"auth.add_user", "auth.change_user"}) self.assertEqual(self.backend.get_all_permissions(alice), {"auth.add_user", "auth.change_user"}) self.assertTrue(self.backend.has_perm(alice, "auth.add_user")) self.assertTrue(self.backend.has_module_perms(alice, "auth")) def test_group_permissions_ldap_error(self): self._init_settings( BIND_DN='uid=bob,ou=people,o=test', BIND_PASSWORD='bogus', USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test', GROUP_SEARCH=LDAPSearch('ou=groups,o=test', ldap.SCOPE_SUBTREE), GROUP_TYPE=MemberDNGroupType(member_attr='member'), FIND_GROUP_PERMS=True ) self._init_groups() alice = User.objects.create(username='alice') alice = self.backend.get_user(alice.pk) self.assertEqual(self.backend.get_group_permissions(alice), set()) def test_empty_group_permissions(self): self._init_settings( USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test', GROUP_SEARCH=LDAPSearch('ou=groups,o=test', ldap.SCOPE_SUBTREE), GROUP_TYPE=MemberDNGroupType(member_attr='member'), FIND_GROUP_PERMS=True ) self._init_groups() bob = User.objects.create(username='bob') bob = self.backend.get_user(bob.pk) self.assertEqual(self.backend.get_group_permissions(bob), set()) self.assertEqual(self.backend.get_all_permissions(bob), set()) self.assertFalse(self.backend.has_perm(bob, "auth.add_user")) self.assertFalse(self.backend.has_module_perms(bob, "auth")) def test_posix_group_permissions(self): self._init_settings( USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test', GROUP_SEARCH=LDAPSearch('ou=groups,o=test', ldap.SCOPE_SUBTREE, '(objectClass=posixGroup)'), GROUP_TYPE=PosixGroupType(), FIND_GROUP_PERMS=True ) self._init_groups() alice = User.objects.create(username='alice') alice = self.backend.get_user(alice.pk) self.assertEqual(self.backend.get_group_permissions(alice), {"auth.add_user", "auth.change_user"}) self.assertEqual(self.backend.get_all_permissions(alice), {"auth.add_user", "auth.change_user"}) self.assertTrue(self.backend.has_perm(alice, "auth.add_user")) self.assertTrue(self.backend.has_module_perms(alice, "auth")) def test_posix_group_permissions_no_gid(self): self._init_settings( USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test', GROUP_SEARCH=LDAPSearch('ou=groups,o=test', ldap.SCOPE_SUBTREE, '(objectClass=posixGroup)'), GROUP_TYPE=PosixGroupType(), FIND_GROUP_PERMS=True ) self._init_groups() self.ldapobj.modify_s(self.alice[0], [(ldap.MOD_DELETE, 'gidNumber', None)]) self.ldapobj.modify_s(self.active_px[0], [(ldap.MOD_ADD, 'memberUid', ['alice'])]) alice = User.objects.create(username='alice') alice = self.backend.get_user(alice.pk) self.assertEqual(self.backend.get_group_permissions(alice), {"auth.add_user", "auth.change_user"}) self.assertEqual(self.backend.get_all_permissions(alice), {"auth.add_user", "auth.change_user"}) self.assertTrue(self.backend.has_perm(alice, "auth.add_user")) self.assertTrue(self.backend.has_module_perms(alice, "auth")) def test_nis_group_permissions(self): self._init_settings( USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test', GROUP_SEARCH=LDAPSearch('ou=groups,o=test', ldap.SCOPE_SUBTREE, '(objectClass=nisNetgroup)'), GROUP_TYPE=NISGroupType(), FIND_GROUP_PERMS=True ) self._init_groups() alice = User.objects.create(username='alice') alice = self.backend.get_user(alice.pk) self.assertEqual(self.backend.get_group_permissions(alice), {"auth.add_user", "auth.change_user"}) self.assertEqual(self.backend.get_all_permissions(alice), {"auth.add_user", "auth.change_user"}) self.assertTrue(self.backend.has_perm(alice, "auth.add_user")) self.assertTrue(self.backend.has_module_perms(alice, "auth")) def test_foreign_user_permissions(self): self._init_settings( USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test', GROUP_SEARCH=LDAPSearch('ou=groups,o=test', ldap.SCOPE_SUBTREE), GROUP_TYPE=MemberDNGroupType(member_attr='member'), FIND_GROUP_PERMS=True ) self._init_groups() alice = User.objects.create(username='alice') self.assertEqual(self.backend.get_group_permissions(alice), set()) def test_group_cache(self): self._init_settings( USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test', GROUP_SEARCH=LDAPSearch('ou=groups,o=test', ldap.SCOPE_SUBTREE), GROUP_TYPE=MemberDNGroupType(member_attr='member'), FIND_GROUP_PERMS=True, CACHE_GROUPS=True ) self._init_groups() alice_id = User.objects.create(username='alice').pk bob_id = User.objects.create(username='bob').pk # Check permissions twice for each user for i in range(2): alice = self.backend.get_user(alice_id) self.assertEqual( self.backend.get_group_permissions(alice), {"auth.add_user", "auth.change_user"} ) bob = self.backend.get_user(bob_id) self.assertEqual(self.backend.get_group_permissions(bob), set()) # Should have executed one LDAP search per user self.assertEqual( self.ldapobj.methods_called(), ['initialize', 'simple_bind_s', 'search_s', 'initialize', 'simple_bind_s', 'search_s'] ) def test_group_mirroring(self): self._init_settings( USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test', GROUP_SEARCH=LDAPSearch('ou=groups,o=test', ldap.SCOPE_SUBTREE, '(objectClass=posixGroup)'), GROUP_TYPE=PosixGroupType(), MIRROR_GROUPS=True, ) self.assertEqual(Group.objects.count(), 0) alice = self.backend.authenticate(username='alice', password='password') self.assertEqual(Group.objects.count(), 3) self.assertEqual(set(alice.groups.all()), set(Group.objects.all())) def test_nested_group_mirroring(self): self._init_settings( USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test', GROUP_SEARCH=LDAPSearch('ou=groups,o=test', ldap.SCOPE_SUBTREE, '(objectClass=groupOfNames)'), GROUP_TYPE=NestedMemberDNGroupType(member_attr='member'), MIRROR_GROUPS=True, ) alice = self.backend.authenticate(username='alice', password='password') self.assertEqual( set(Group.objects.all().values_list('name', flat=True)), {'active_gon', 'staff_gon', 'superuser_gon', 'nested_gon', 'parent_gon', 'circular_gon'} ) self.assertEqual(set(alice.groups.all()), set(Group.objects.all())) # # When selectively mirroring groups, there are eight scenarios for any # given user/group pair: # # (is-member-in-LDAP, not-member-in-LDAP) # x (is-member-in-Django, not-member-in-Django) # x (synced, not-synced) # # The four test cases below take these scenarios four at a time for each of # the two settings. def test_group_mirroring_whitelist_update(self): self._init_settings( USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test', GROUP_SEARCH=LDAPSearch('ou=mirror_groups,o=test', ldap.SCOPE_SUBTREE, '(objectClass=groupOfNames)'), GROUP_TYPE=GroupOfNamesType(), MIRROR_GROUPS=['mirror1', 'mirror2'] ) groups = {} for name in ('mirror{}'.format(i) for i in range(1, 5)): groups[name] = Group.objects.create(name=name) alice = self.backend.populate_user('alice') alice.groups.set([groups['mirror2'], groups['mirror4']]) alice = self.backend.authenticate(username='alice', password='password') self.assertEqual( set(alice.groups.values_list("name", flat=True)), {'mirror1', 'mirror4'} ) def test_group_mirroring_whitelist_noop(self): self._init_settings( USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test', GROUP_SEARCH=LDAPSearch('ou=mirror_groups,o=test', ldap.SCOPE_SUBTREE, '(objectClass=groupOfNames)'), GROUP_TYPE=GroupOfNamesType(), MIRROR_GROUPS=['mirror1', 'mirror2'] ) groups = {} for name in ('mirror{}'.format(i) for i in range(1, 5)): groups[name] = Group.objects.create(name=name) alice = self.backend.populate_user('alice') alice.groups.set([groups['mirror1'], groups['mirror3']]) alice = self.backend.authenticate(username='alice', password='password') self.assertEqual( set(alice.groups.values_list("name", flat=True)), {'mirror1', 'mirror3'} ) def test_group_mirroring_blacklist_update(self): self._init_settings( USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test', GROUP_SEARCH=LDAPSearch('ou=mirror_groups,o=test', ldap.SCOPE_SUBTREE, '(objectClass=groupOfNames)'), GROUP_TYPE=GroupOfNamesType(), MIRROR_GROUPS_EXCEPT=['mirror1', 'mirror2'] ) groups = {} for name in ('mirror{}'.format(i) for i in range(1, 5)): groups[name] = Group.objects.create(name=name) alice = self.backend.populate_user('alice') alice.groups.set([groups['mirror2'], groups['mirror4']]) alice = self.backend.authenticate(username='alice', password='password') self.assertEqual( set(alice.groups.values_list("name", flat=True)), {'mirror2', 'mirror3'} ) def test_group_mirroring_blacklist_noop(self): self._init_settings( USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test', GROUP_SEARCH=LDAPSearch('ou=mirror_groups,o=test', ldap.SCOPE_SUBTREE, '(objectClass=groupOfNames)'), GROUP_TYPE=GroupOfNamesType(), MIRROR_GROUPS_EXCEPT=['mirror1', 'mirror2'] ) groups = {} for name in ('mirror{}'.format(i) for i in range(1, 5)): groups[name] = Group.objects.create(name=name) alice = self.backend.populate_user('alice') alice.groups.set([groups['mirror1'], groups['mirror3']]) alice = self.backend.authenticate(username='alice', password='password') self.assertEqual( set(alice.groups.values_list("name", flat=True)), {'mirror1', 'mirror3'} ) def test_authorize_external_users(self): self._init_settings( USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test', GROUP_SEARCH=LDAPSearch('ou=groups,o=test', ldap.SCOPE_SUBTREE), GROUP_TYPE=MemberDNGroupType(member_attr='member'), FIND_GROUP_PERMS=True, AUTHORIZE_ALL_USERS=True ) self._init_groups() alice = User.objects.create(username='alice') self.assertEqual(self.backend.get_group_permissions(alice), {"auth.add_user", "auth.change_user"}) def test_authorize_external_unknown(self): self._init_settings( USER_SEARCH=LDAPSearch( "ou=people,o=test", ldap.SCOPE_SUBTREE, '(uid=%(user)s)' ), GROUP_SEARCH=LDAPSearch('ou=groups,o=test', ldap.SCOPE_SUBTREE), GROUP_TYPE=MemberDNGroupType(member_attr='member'), FIND_GROUP_PERMS=True, AUTHORIZE_ALL_USERS=True ) self._init_groups() alice = User.objects.create(username='not-in-ldap') self.assertEqual(self.backend.get_group_permissions(alice), set()) def test_create_without_auth(self): self._init_settings( USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test', ) alice = self.backend.populate_user('alice') bob = self.backend.populate_user('bob') self.assertIsNotNone(alice) self.assertEqual(alice.first_name, "") self.assertEqual(alice.last_name, "") self.assertTrue(alice.is_active) self.assertFalse(alice.is_staff) self.assertFalse(alice.is_superuser) self.assertIsNotNone(bob) self.assertEqual(bob.first_name, "") self.assertEqual(bob.last_name, "") self.assertTrue(bob.is_active) self.assertFalse(bob.is_staff) self.assertFalse(bob.is_superuser) def test_populate_without_auth(self): self._init_settings( USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test', ALWAYS_UPDATE_USER=False, USER_ATTR_MAP={'first_name': 'givenName', 'last_name': 'sn'}, GROUP_SEARCH=LDAPSearch('ou=groups,o=test', ldap.SCOPE_SUBTREE), GROUP_TYPE=GroupOfNamesType(), USER_FLAGS_BY_GROUP={ 'is_active': "cn=active_gon,ou=groups,o=test", 'is_staff': "cn=staff_gon,ou=groups,o=test", 'is_superuser': "cn=superuser_gon,ou=groups,o=test" } ) User.objects.create(username='alice') User.objects.create(username='bob') alice = self.backend.populate_user('alice') bob = self.backend.populate_user('bob') self.assertIsNotNone(alice) self.assertEqual(alice.first_name, "Alice") self.assertEqual(alice.last_name, "Adams") self.assertTrue(alice.is_active) self.assertTrue(alice.is_staff) self.assertTrue(alice.is_superuser) self.assertIsNotNone(bob) self.assertEqual(bob.first_name, "Robert") self.assertEqual(bob.last_name, "Barker") self.assertFalse(bob.is_active) self.assertFalse(bob.is_staff) self.assertFalse(bob.is_superuser) def test_populate_bogus_user(self): self._init_settings( USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test', ) bogus = self.backend.populate_user('bogus') self.assertIsNone(bogus) def test_start_tls_missing(self): self._init_settings( USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test', START_TLS=False, ) self.assertFalse(self.ldapobj.tls_enabled) self.backend.authenticate(username='alice', password='password') self.assertFalse(self.ldapobj.tls_enabled) def test_start_tls(self): self._init_settings( USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test', START_TLS=True, ) self.assertFalse(self.ldapobj.tls_enabled) self.backend.authenticate(username='alice', password='password') self.assertTrue(self.ldapobj.tls_enabled) def test_null_search_results(self): """ Make sure we're not phased by referrals. """ self._init_settings( USER_SEARCH=LDAPSearch( "ou=people,o=test", ldap.SCOPE_SUBTREE, '(uid=%(user)s)' ) ) self.backend.authenticate(username='alice', password='password') def test_union_search(self): self._init_settings( USER_SEARCH=LDAPSearchUnion( LDAPSearch("ou=groups,o=test", ldap.SCOPE_SUBTREE, '(uid=%(user)s)'), LDAPSearch("ou=people,o=test", ldap.SCOPE_SUBTREE, '(uid=%(user)s)'), ) ) alice = self.backend.authenticate(username='alice', password='password') self.assertIsNotNone(alice) self.assertEqual( self.ldapobj.methods_called(), ['initialize', 'simple_bind_s', 'search', 'search', 'result', 'result', 'simple_bind_s'] ) def test_deny_empty_password(self): self._init_settings( USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test', ) alice = self.backend.authenticate(username='alice', password='') self.assertIsNone(alice) self.assertEqual(self.ldapobj.methods_called(), []) def test_permit_empty_password(self): self._init_settings( USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test', PERMIT_EMPTY_PASSWORD=True, ) alice = self.backend.authenticate(username='alice', password='') self.assertIsNone(alice) self.assertEqual( self.ldapobj.methods_called(), ['initialize', 'simple_bind_s'] ) def test_permit_null_password(self): self._init_settings( USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test', PERMIT_EMPTY_PASSWORD=True, ) alice = self.backend.authenticate(username='alice', password=None) self.assertIsNone(alice) self.assertEqual( self.ldapobj.methods_called(), ['initialize', 'simple_bind_s'] ) def test_pickle(self): self._init_settings( USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test', GROUP_SEARCH=LDAPSearch('ou=groups,o=test', ldap.SCOPE_SUBTREE), GROUP_TYPE=MemberDNGroupType(member_attr='member'), FIND_GROUP_PERMS=True ) self._init_groups() alice0 = self.backend.authenticate(username='alice', password='password') pickled = pickle.dumps(alice0, pickle.HIGHEST_PROTOCOL) alice = pickle.loads(pickled) alice.ldap_user.backend.settings = alice0.ldap_user.backend.settings self.assertIsNotNone(alice) self.assertEqual(self.backend.get_group_permissions(alice), {"auth.add_user", "auth.change_user"}) self.assertEqual(self.backend.get_all_permissions(alice), {"auth.add_user", "auth.change_user"}) self.assertTrue(self.backend.has_perm(alice, "auth.add_user")) self.assertTrue(self.backend.has_module_perms(alice, "auth")) def test_search_attrlist(self): search = LDAPSearch("ou=people,o=test", ldap.SCOPE_SUBTREE, '(uid=alice)', ['*', '+']) search.execute(self.ldapobj) self.assertEqual( self.ldapobj.methods_called(with_args=True), [('search_s', ('ou=people,o=test', ldap.SCOPE_SUBTREE, '(uid=alice)', ['*', '+']), {})] ) def test_get_or_create_user_deprecated(self): class MyBackend(backend.LDAPBackend): def get_or_create_user(self, username, ldap_user): return super(MyBackend, self).get_or_create_user(username, ldap_user) self.backend = MyBackend() self._init_settings(USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test') with warnings.catch_warnings(record=True) as w: warnings.simplefilter('always') self.backend.authenticate(username='alice', password='password') self.assertEqual(len(w), 1) self.assertEqual(w[0].category, DeprecationWarning) def test_custom_backend_without_get_or_create_no_warning(self): class MyBackend(backend.LDAPBackend): pass self.backend = MyBackend() self._init_settings(USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test') with warnings.catch_warnings(record=True) as w: warnings.simplefilter('always') self.backend.authenticate(username='alice', password='password') self.assertEqual(len(w), 0) def test_override_authenticate_access_ldap_user(self): class MyBackend(backend.LDAPBackend): def authenticate_ldap_user(self, ldap_user, password): ldap_user.foo = 'bar' return super(MyBackend, self).authenticate_ldap_user(ldap_user, password) self.backend = MyBackend() self._init_settings(USER_DN_TEMPLATE='uid=%(user)s,ou=people,o=test') user = self.backend.authenticate(username='alice', password='password') self.assertEqual(user.ldap_user.foo, 'bar') # # Utilities # def _init_settings(self, **kwargs): self.backend.settings = TestSettings(**kwargs) def _init_groups(self): permissions = [ Permission.objects.get(codename="add_user"), Permission.objects.get(codename="change_user") ] active_gon = Group.objects.create(name='active_gon') active_gon.permissions.add(*permissions) active_px = Group.objects.create(name='active_px') active_px.permissions.add(*permissions) active_nis = Group.objects.create(name='active_nis') active_nis.permissions.add(*permissions) django-auth-ldap-1.4.0/MANIFEST.in0000644000076600000240000000016713223460174017167 0ustar psagersstaff00000000000000include README.md LICENSE CHANGES include tox.ini recursive-include docs * recursive-include tests * prune docs/build django-auth-ldap-1.4.0/docs/0000755000076600000240000000000013255035702016355 5ustar psagersstaff00000000000000django-auth-ldap-1.4.0/docs/Makefile0000644000076600000240000000115413162741710020016 0ustar psagersstaff00000000000000# Minimal makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = python -msphinx SPHINXPROJ = django-auth-ldap SOURCEDIR = source BUILDDIR = build # Put it first so that "make" without argument is like "make help". help: @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) .PHONY: help Makefile # Catch-all target: route all unknown targets to Sphinx using the new # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). %: Makefile @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)django-auth-ldap-1.4.0/docs/ext/0000755000076600000240000000000013255035702017155 5ustar psagersstaff00000000000000django-auth-ldap-1.4.0/docs/ext/daldocs.py0000644000076600000240000000032513151036722021136 0ustar psagersstaff00000000000000""" Extra stuff for the django-auth-ldap Sphinx docs. """ def setup(app): app.add_crossref_type( directivename="setting", rolename="setting", indextemplate="pair: %s; setting", ) django-auth-ldap-1.4.0/docs/source/0000755000076600000240000000000013255035702017655 5ustar psagersstaff00000000000000django-auth-ldap-1.4.0/docs/source/install.rst0000644000076600000240000000260013253750522022055 0ustar psagersstaff00000000000000Installation ============ Install the package with pip: .. code-block:: sh $ pip install django-auth-ldap It requires `python-ldap`_ >= 3.0. You'll need the `OpenLDAP`_ libraries and headers available on your system. To use the auth backend in a Django project, add ``'django_auth_ldap.backend.LDAPBackend'`` to :django:setting:`AUTHENTICATION_BACKENDS`. Do not add anything to :django:setting:`INSTALLED_APPS`. .. code-block:: python AUTHENTICATION_BACKENDS = [ 'django_auth_ldap.backend.LDAPBackend', ] :class:`~django_auth_ldap.backend.LDAPBackend` should work with custom user models, but it does assume that a database is present. .. note:: :class:`~django_auth_ldap.backend.LDAPBackend` does not inherit from :class:`~django.contrib.auth.backends.ModelBackend`. It is possible to use :class:`~django_auth_ldap.backend.LDAPBackend` exclusively by configuring it to draw group membership from the LDAP server. However, if you would like to assign permissions to individual users or add users to groups within Django, you'll need to have both backends installed: .. code-block:: python AUTHENTICATION_BACKENDS = [ 'django_auth_ldap.backend.LDAPBackend', 'django.contrib.auth.backends.ModelBackend', ] .. _`python-ldap`: https://pypi.python.org/pypi/python-ldap .. _`OpenLDAP`: https://www.openldap.org/ django-auth-ldap-1.4.0/docs/source/authentication.rst0000644000076600000240000001537713223460174023443 0ustar psagersstaff00000000000000Authentication ============== Server Config ------------- If your LDAP server isn't running locally on the default port, you'll want to start by setting :setting:`AUTH_LDAP_SERVER_URI` to point to your server. The value of this setting can be anything that your LDAP library supports. For instance, openldap may allow you to give a comma- or space-separated list of URIs to try in sequence. .. code-block:: python AUTH_LDAP_SERVER_URI = "ldap://ldap.example.com" If your server location is even more dynamic than this, you may provide a function (or any callable object) that returns the URI. You should assume that this will be called on every request, so if it's an expensive operation, some caching is in order. .. code-block:: python from my_module import find_my_ldap_server AUTH_LDAP_SERVER_URI = find_my_ldap_server If you need to configure any python-ldap options, you can set :setting:`AUTH_LDAP_GLOBAL_OPTIONS` and/or :setting:`AUTH_LDAP_CONNECTION_OPTIONS`. For example, disabling referrals is not uncommon:: import ldap AUTH_LDAP_CONNECTION_OPTIONS = { ldap.OPT_REFERRALS: 0 } Search/Bind ----------- Now that you can talk to your LDAP server, the next step is to authenticate a username and password. There are two ways to do this, called search/bind and direct bind. The first one involves connecting to the LDAP server either anonymously or with a fixed account and searching for the distinguished name of the authenticating user. Then we can attempt to bind again with the user's password. The second method is to derive the user's DN from his username and attempt to bind as the user directly. Because LDAP searches appear elsewhere in the configuration, the :class:`~django_auth_ldap.config.LDAPSearch` class is provided to encapsulate search information. In this case, the filter parameter should contain the placeholder ``%(user)s``. A simple configuration for the search/bind approach looks like this (some defaults included for completeness):: import ldap from django_auth_ldap.config import LDAPSearch AUTH_LDAP_BIND_DN = "" AUTH_LDAP_BIND_PASSWORD = "" AUTH_LDAP_USER_SEARCH = LDAPSearch("ou=users,dc=example,dc=com", ldap.SCOPE_SUBTREE, "(uid=%(user)s)") This will perform an anonymous bind, search under ``"ou=users,dc=example,dc=com"`` for an object with a uid matching the user's name, and try to bind using that DN and the user's password. The search must return exactly one result or authentication will fail. If you can't search anonymously, you can set :setting:`AUTH_LDAP_BIND_DN` to the distinguished name of an authorized user and :setting:`AUTH_LDAP_BIND_PASSWORD` to the password. Search Unions ^^^^^^^^^^^^^ .. versionadded:: 1.1 If you need to search in more than one place for a user, you can use :class:`~django_auth_ldap.config.LDAPSearchUnion`. This takes multiple LDAPSearch objects and returns the union of the results. The precedence of the underlying searches is unspecified. .. code-block:: python import ldap from django_auth_ldap.config import LDAPSearch, LDAPSearchUnion AUTH_LDAP_USER_SEARCH = LDAPSearchUnion( LDAPSearch("ou=users,dc=example,dc=com", ldap.SCOPE_SUBTREE, "(uid=%(user)s)"), LDAPSearch("ou=otherusers,dc=example,dc=com", ldap.SCOPE_SUBTREE, "(uid=%(user)s)"), ) Direct Bind ----------- To skip the search phase, set :setting:`AUTH_LDAP_USER_DN_TEMPLATE` to a template that will produce the authenticating user's DN directly. This template should have one placeholder, ``%(user)s``. If the first example had used ``ldap.SCOPE_ONELEVEL``, the following would be a more straightforward (and efficient) equivalent:: AUTH_LDAP_USER_DN_TEMPLATE = "uid=%(user)s,ou=users,dc=example,dc=com" .. _customizing-authentication: Customizing Authentication -------------------------- .. versionadded:: 1.3 It is possible to further customize the authentication process by subclassing :class:`~django_auth_ldap.backend.LDAPBackend` and overriding :meth:`~django_auth_ldap.backend.LDAPBackend.authenticate_ldap_user`. The first argument is the unauthenticated :ref:`ldap_user `, the second is the supplied password. The intent is to give subclasses a simple pre- and post-authentication hook. If a subclass decides to proceed with the authentication, it must call the inherited implementation. It may then return either the authenticated user or ``None``. The behavior of any other return value--such as substituting a different user object--is undefined. :doc:`users` has more on managing Django user objects. Obviously, it is always safe to access ``ldap_user.dn`` before authenticating the user. Accessing ``ldap_user.attrs`` and others should be safe unless you're relying on special binding behavior, such as :setting:`AUTH_LDAP_BIND_AS_AUTHENTICATING_USER`. Notes ----- LDAP is fairly flexible when it comes to matching DNs. :class:`~django_auth_ldap.backend.LDAPBackend` makes an effort to accommodate this by forcing usernames to lower case when creating Django users and trimming whitespace when authenticating. Some LDAP servers are configured to allow users to bind without a password. As a precaution against false positives, :class:`~django_auth_ldap.backend.LDAPBackend` will summarily reject any authentication attempt with an empty password. You can disable this behavior by setting :setting:`AUTH_LDAP_PERMIT_EMPTY_PASSWORD` to True. By default, all LDAP operations are performed with the :setting:`AUTH_LDAP_BIND_DN` and :setting:`AUTH_LDAP_BIND_PASSWORD` credentials, not with the user's. Otherwise, the LDAP connection would be bound as the authenticating user during login requests and as the default credentials during other requests, so you might see inconsistent LDAP attributes depending on the nature of the Django view. If you're willing to accept the inconsistency in order to retrieve attributes while bound as the authenticating user, see :setting:`AUTH_LDAP_BIND_AS_AUTHENTICATING_USER`. By default, LDAP connections are unencrypted and make no attempt to protect sensitive information, such as passwords. When communicating with an LDAP server on localhost or on a local network, this might be fine. If you need a secure connection to the LDAP server, you can either use an ``ldaps://`` URL or enable the StartTLS extension. The latter is generally the preferred mechanism. To enable StartTLS, set :setting:`AUTH_LDAP_START_TLS` to ``True``:: AUTH_LDAP_START_TLS = True If :class:`~django_auth_ldap.backend.LDAPBackend` receives an :exc:`~ldap.LDAPError` from python_ldap, it will normally swallow it and log a warning. If you'd like to perform any special handling for these exceptions, you can add a signal handler to :data:`django_auth_ldap.backend.ldap_error`. The signal handler can handle the exception any way you like, including re-raising it or any other exception. django-auth-ldap-1.4.0/docs/source/index.rst0000644000076600000240000000162213253750522021521 0ustar psagersstaff00000000000000================================ Django Authentication Using LDAP ================================ This is a Django authentication backend that authenticates against an LDAP service. Configuration can be as simple as a single distinguished name template, but there are many rich configuration options for working with users, groups, and permissions. * Repository: https://bitbucket.org/illocution/django-auth-ldap * Documentation: https://django-auth-ldap.readthedocs.io/ * Mailing list: https://groups.google.com/group/django-auth-ldap This version is supported on Python 2.7 and 3.4+; and Django 1.11+. It requires `python-ldap `_ >= 3.0. .. toctree:: :maxdepth: 2 install authentication groups users permissions multiconfig logging performance example reference changes License ======= .. include:: ../../LICENSE django-auth-ldap-1.4.0/docs/source/reference.rst0000644000076600000240000004761213253750522022361 0ustar psagersstaff00000000000000Reference ========= Settings -------- .. setting:: AUTH_LDAP_ALWAYS_UPDATE_USER AUTH_LDAP_ALWAYS_UPDATE_USER ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Default: ``True`` If ``True``, the fields of a :class:`~django.contrib.auth.models.User` object will be updated with the latest values from the LDAP directory every time the user logs in. Otherwise the :class:`~django.contrib.auth.models.User` object will only be populated when it is automatically created. .. setting:: AUTH_LDAP_AUTHORIZE_ALL_USERS AUTH_LDAP_AUTHORIZE_ALL_USERS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Default: ``False`` If ``True``, :class:`~django_auth_ldap.backend.LDAPBackend` will be able furnish permissions for any Django user, regardless of which backend authenticated it. .. setting:: AUTH_LDAP_BIND_AS_AUTHENTICATING_USER AUTH_LDAP_BIND_AS_AUTHENTICATING_USER ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Default: ``False`` If ``True``, authentication will leave the LDAP connection bound as the authenticating user, rather than forcing it to re-bind with the default credentials after authentication succeeds. This may be desirable if you do not have global credentials that are able to access the user's attributes. django-auth-ldap never stores the user's password, so this only applies to requests where the user is authenticated. Thus, the downside to this setting is that LDAP results may vary based on whether the user was authenticated earlier in the Django view, which could be surprising to code not directly concerned with authentication. .. setting:: AUTH_LDAP_BIND_DN AUTH_LDAP_BIND_DN ~~~~~~~~~~~~~~~~~ Default: ``''`` (Empty string) The distinguished name to use when binding to the LDAP server (with :setting:`AUTH_LDAP_BIND_PASSWORD`). Use the empty string (the default) for an anonymous bind. To authenticate a user, we will bind with that user's DN and password, but for all other LDAP operations, we will be bound as the DN in this setting. For example, if :setting:`AUTH_LDAP_USER_DN_TEMPLATE` is not set, we'll use this to search for the user. If :setting:`AUTH_LDAP_FIND_GROUP_PERMS` is ``True``, we'll also use it to determine group membership. .. setting:: AUTH_LDAP_BIND_PASSWORD AUTH_LDAP_BIND_PASSWORD ~~~~~~~~~~~~~~~~~~~~~~~ Default: ``''`` (Empty string) The password to use with :setting:`AUTH_LDAP_BIND_DN`. .. setting:: AUTH_LDAP_CACHE_GROUPS AUTH_LDAP_CACHE_GROUPS ~~~~~~~~~~~~~~~~~~~~~~ Default: ``False`` If ``True``, LDAP group membership will be cached using Django's cache framework. The cache timeout can be customized with :setting:`AUTH_LDAP_GROUP_CACHE_TIMEOUT`. .. setting:: AUTH_LDAP_CONNECTION_OPTIONS AUTH_LDAP_CONNECTION_OPTIONS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Default: ``{}`` A dictionary of options to pass to each connection to the LDAP server via ``LDAPObject.set_option()``. Keys are `ldap.OPT_* `_ constants. .. setting:: AUTH_LDAP_DENY_GROUP AUTH_LDAP_DENY_GROUP ~~~~~~~~~~~~~~~~~~~~~~~ Default: ``None`` The distinguished name of a group; authentication will fail for any user that belongs to this group. .. setting:: AUTH_LDAP_FIND_GROUP_PERMS AUTH_LDAP_FIND_GROUP_PERMS ~~~~~~~~~~~~~~~~~~~~~~~~~~ Default: ``False`` If ``True``, :class:`~django_auth_ldap.backend.LDAPBackend` will furnish group permissions based on the LDAP groups the authenticated user belongs to. :setting:`AUTH_LDAP_GROUP_SEARCH` and :setting:`AUTH_LDAP_GROUP_TYPE` must also be set. .. setting:: AUTH_LDAP_GLOBAL_OPTIONS AUTH_LDAP_GLOBAL_OPTIONS ~~~~~~~~~~~~~~~~~~~~~~~~ Default: ``{}`` A dictionary of options to pass to ``ldap.set_option()``. Keys are `ldap.OPT_* `_ constants. .. note:: Due to its global nature, this setting ignores the :doc:`settings prefix `. Regardless of how many backends are installed, this setting is referenced once by its default name at the time we load the ldap module. .. setting:: AUTH_LDAP_GROUP_CACHE_TIMEOUT AUTH_LDAP_GROUP_CACHE_TIMEOUT ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Default: ``None`` If :setting:`AUTH_LDAP_CACHE_GROUPS` is ``True``, this is the cache timeout for group memberships. If ``None``, the global cache timeout will be used. .. setting:: AUTH_LDAP_GROUP_SEARCH AUTH_LDAP_GROUP_SEARCH ~~~~~~~~~~~~~~~~~~~~~~ Default: ``None`` An :class:`~django_auth_ldap.config.LDAPSearch` object that finds all LDAP groups that users might belong to. If your configuration makes any references to LDAP groups, this and :setting:`AUTH_LDAP_GROUP_TYPE` must be set. .. setting:: AUTH_LDAP_GROUP_TYPE AUTH_LDAP_GROUP_TYPE ~~~~~~~~~~~~~~~~~~~~ Default: ``None`` An :class:`~django_auth_ldap.config.LDAPGroupType` instance describing the type of group returned by :setting:`AUTH_LDAP_GROUP_SEARCH`. .. setting:: AUTH_LDAP_MIRROR_GROUPS AUTH_LDAP_MIRROR_GROUPS ~~~~~~~~~~~~~~~~~~~~~~~ Default: ``None`` If ``True``, :class:`~django_auth_ldap.backend.LDAPBackend` will mirror a user's LDAP group membership in the Django database. Any time a user authenticates, we will create all of their LDAP groups as Django groups and update their Django group membership to exactly match their LDAP group membership. If the LDAP server has nested groups, the Django database will end up with a flattened representation. This can also be a list or other collection of group names, in which case we'll only mirror those groups and leave the rest alone. This is ignored if :setting:`AUTH_LDAP_MIRROR_GROUPS_EXCEPT` is set. .. setting:: AUTH_LDAP_MIRROR_GROUPS_EXCEPT AUTH_LDAP_MIRROR_GROUPS_EXCEPT ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Default: ``None`` If this is not ``None``, it must be a list or other collection of group names. This will enable group mirroring, except that we'll never change the membership of the indicated groups. :setting:`AUTH_LDAP_MIRROR_GROUPS` is ignored in this case. .. setting:: AUTH_LDAP_PERMIT_EMPTY_PASSWORD AUTH_LDAP_PERMIT_EMPTY_PASSWORD ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Default: ``False`` If ``False`` (the default), authentication with an empty password will fail immediately, without any LDAP communication. This is a secure default, as some LDAP servers are configured to allow binds to succeed with no password, perhaps at a reduced level of access. If you need to make use of this LDAP feature, you can change this setting to ``True``. .. setting:: AUTH_LDAP_REQUIRE_GROUP AUTH_LDAP_REQUIRE_GROUP ~~~~~~~~~~~~~~~~~~~~~~~ Default: ``None`` The distinguished name of a group; authentication will fail for any user that does not belong to this group. This can also be an :class:`~django_auth_ldap.config.LDAPGroupQuery` instance. .. setting:: AUTH_LDAP_SERVER_URI AUTH_LDAP_SERVER_URI ~~~~~~~~~~~~~~~~~~~~ Default: ``'ldap://localhost'`` The URI of the LDAP server. This can be any URI that is supported by your underlying LDAP libraries. .. setting:: AUTH_LDAP_START_TLS AUTH_LDAP_START_TLS ~~~~~~~~~~~~~~~~~~~ Default: ``False`` If ``True``, each connection to the LDAP server will call :meth:`~ldap.LDAPObject.start_tls_s` to enable TLS encryption over the standard LDAP port. There are a number of configuration options that can be given to :setting:`AUTH_LDAP_GLOBAL_OPTIONS` that affect the TLS connection. For example, :data:`ldap.OPT_X_TLS_REQUIRE_CERT` can be set to :data:`ldap.OPT_X_TLS_NEVER` to disable certificate verification, perhaps to allow self-signed certificates. .. setting:: AUTH_LDAP_USER_QUERY_FIELD AUTH_LDAP_USER_QUERY_FIELD ~~~~~~~~~~~~~~~~~~~~~~~~~~~ Default: ``None`` The field on the user model used to query the authenticating user in the database. If unset, uses the value of ``USERNAME_FIELD`` of the model class. When set, the value used to query is obtained through the :setting:`AUTH_LDAP_USER_ATTR_MAP`. .. setting:: AUTH_LDAP_USER_ATTRLIST AUTH_LDAP_USER_ATTRLIST ~~~~~~~~~~~~~~~~~~~~~~~ Default: ``None`` A list of attribute names to load for the authenticated user. Normally, you can ignore this and the LDAP server will send back all of the attributes of the directory entry. One reason you might need to override this is to get operational attributes, which are not normally included:: AUTH_LDAP_USER_ATTRLIST = ['*', '+'] .. setting:: AUTH_LDAP_USER_ATTR_MAP AUTH_LDAP_USER_ATTR_MAP ~~~~~~~~~~~~~~~~~~~~~~~ Default: ``{}`` A mapping from :class:`~django.contrib.auth.models.User` field names to LDAP attribute names. A users's :class:`~django.contrib.auth.models.User` object will be populated from his LDAP attributes at login. .. setting:: AUTH_LDAP_USER_DN_TEMPLATE AUTH_LDAP_USER_DN_TEMPLATE ~~~~~~~~~~~~~~~~~~~~~~~~~~ Default: ``None`` A string template that describes any user's distinguished name based on the username. This must contain the placeholder ``%(user)s``. .. setting:: AUTH_LDAP_USER_FLAGS_BY_GROUP AUTH_LDAP_USER_FLAGS_BY_GROUP ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Default: ``{}`` A mapping from boolean :class:`~django.contrib.auth.models.User` field names to distinguished names of LDAP groups. The corresponding field is set to ``True`` or ``False`` according to whether the user is a member of the group. Values may be strings for simple group membership tests or :class:`~django_auth_ldap.config.LDAPGroupQuery` instances for more complex cases. .. setting:: AUTH_LDAP_USER_SEARCH AUTH_LDAP_USER_SEARCH ~~~~~~~~~~~~~~~~~~~~~ Default: ``None`` An :class:`~django_auth_ldap.config.LDAPSearch` object that will locate a user in the directory. The filter parameter should contain the placeholder ``%(user)s`` for the username. It must return exactly one result for authentication to succeed. Module Properties ----------------- .. module:: django_auth_ldap .. data:: version The library's current version number as a 3-tuple. .. data:: version_string The library's current version number as a string. Configuration ------------- .. module:: django_auth_ldap.config .. class:: LDAPSearch .. method:: __init__(base_dn, scope, filterstr='(objectClass=*)') :param str base_dn: The distinguished name of the search base. :param int scope: One of ``ldap.SCOPE_*``. :param str filterstr: An optional filter string (e.g. '(objectClass=person)'). In order to be valid, ``filterstr`` must be enclosed in parentheses. .. class:: LDAPSearchUnion .. versionadded:: 1.1 .. method:: __init__(\*searches) :param searches: Zero or more LDAPSearch objects. The result of the overall search is the union (by DN) of the results of the underlying searches. The precedence of the underlying results and the ordering of the final results are both undefined. :type searches: :class:`LDAPSearch` .. class:: LDAPGroupType The base class for objects that will determine group membership for various LDAP grouping mechanisms. Implementations are provided for common group types or you can write your own. See the source code for subclassing notes. .. method:: __init__(name_attr='cn') By default, LDAP groups will be mapped to Django groups by taking the first value of the cn attribute. You can specify a different attribute with ``name_attr``. .. class:: PosixGroupType A concrete subclass of :class:`~django_auth_ldap.config.LDAPGroupType` that handles the ``posixGroup`` object class. This checks for both primary group and group membership. .. method:: __init__(name_attr='cn') .. class:: NISGroupType A concrete subclass of :class:`~django_auth_ldap.config.LDAPGroupType` that handles the ``nisNetgroup`` object class. .. method:: __init__(name_attr='cn') .. class:: MemberDNGroupType A concrete subclass of :class:`~django_auth_ldap.config.LDAPGroupType` that handles grouping mechanisms wherein the group object contains a list of its member DNs. .. method:: __init__(member_attr, name_attr='cn') :param str member_attr: The attribute on the group object that contains a list of member DNs. 'member' and 'uniqueMember' are common examples. .. class:: NestedMemberDNGroupType Similar to :class:`~django_auth_ldap.config.MemberDNGroupType`, except this allows groups to contain other groups as members. Group hierarchies will be traversed to determine membership. .. method:: __init__(member_attr, name_attr='cn') As above. .. class:: GroupOfNamesType A concrete subclass of :class:`~django_auth_ldap.config.MemberDNGroupType` that handles the ``groupOfNames`` object class. Equivalent to ``MemberDNGroupType('member')``. .. method:: __init__(name_attr='cn') .. class:: NestedGroupOfNamesType A concrete subclass of :class:`~django_auth_ldap.config.NestedMemberDNGroupType` that handles the ``groupOfNames`` object class. Equivalent to ``NestedMemberDNGroupType('member')``. .. method:: __init__(name_attr='cn') .. class:: GroupOfUniqueNamesType A concrete subclass of :class:`~django_auth_ldap.config.MemberDNGroupType` that handles the ``groupOfUniqueNames`` object class. Equivalent to ``MemberDNGroupType('uniqueMember')``. .. method:: __init__(name_attr='cn') .. class:: NestedGroupOfUniqueNamesType A concrete subclass of :class:`~django_auth_ldap.config.NestedMemberDNGroupType` that handles the ``groupOfUniqueNames`` object class. Equivalent to ``NestedMemberDNGroupType('uniqueMember')``. .. method:: __init__(name_attr='cn') .. class:: ActiveDirectoryGroupType A concrete subclass of :class:`~django_auth_ldap.config.MemberDNGroupType` that handles Active Directory groups. Equivalent to ``MemberDNGroupType('member')``. .. method:: __init__(name_attr='cn') .. class:: NestedActiveDirectoryGroupType A concrete subclass of :class:`~django_auth_ldap.config.NestedMemberDNGroupType` that handles Active Directory groups. Equivalent to ``NestedMemberDNGroupType('member')``. .. method:: __init__(name_attr='cn') .. class:: OrganizationalRoleGroupType A concrete subclass of :class:`~django_auth_ldap.config.MemberDNGroupType` that handles the ``organizationalRole`` object class. Equivalent to ``MemberDNGroupType('roleOccupant')``. .. method:: __init__(name_attr='cn') .. class:: NestedOrganizationalRoleGroupType A concrete subclass of :class:`~django_auth_ldap.config.NestedMemberDNGroupType` that handles the ``organizationalRole`` object class. Equivalent to ``NestedMemberDNGroupType('roleOccupant')``. .. method:: __init__(name_attr='cn') .. class:: LDAPGroupQuery Represents a compound query for group membership. This can be used to construct an arbitrarily complex group membership query with AND, OR, and NOT logical operators. Construct primitive queries with a group DN as the only argument. These queries can then be combined with the ``&``, ``|``, and ``~`` operators. This is used by certain settings, including :setting:`AUTH_LDAP_REQUIRE_GROUP` and :setting:`AUTH_LDAP_USER_FLAGS_BY_GROUP`. An example is shown in :ref:`limiting-access`. .. method:: __init__(group_dn) :param str group_dn: The distinguished name of a group to test for membership. Backend ------- .. module:: django_auth_ldap.backend .. data:: populate_user This is a Django signal that is sent when clients should perform additional customization of a :class:`~django.contrib.auth.models.User` object. It is sent after a user has been authenticated and the backend has finished populating it, and just before it is saved. The client may take this opportunity to populate additional model fields, perhaps based on ``ldap_user.attrs``. This signal has two keyword arguments: ``user`` is the :class:`~django.contrib.auth.models.User` object and ``ldap_user`` is the same as ``user.ldap_user``. The sender is the :class:`~django_auth_ldap.backend.LDAPBackend` class. .. data:: ldap_error This is a Django signal that is sent when we receive an :exc:`ldap.LDAPError` exception. The signal has three keyword arguments: - ``context``: one of ``'authenticate'``, ``'get_group_permissions'``, or ``'populate_user'``, indicating which API was being called when the exception was caught. - ``user``: the Django user being processed (if available). - ``exception``: the :exc:`~ldap.LDAPError` object itself. The sender is the :class:`~django_auth_ldap.backend.LDAPBackend` class (or subclass). .. class:: LDAPBackend :class:`~django_auth_ldap.backend.LDAPBackend` has one method that may be called directly and several that may be overridden in subclasses. .. data:: settings_prefix A prefix for all of our Django settings. By default, this is ``'AUTH_LDAP_'``, but subclasses can override this. When different subclasses use different prefixes, they can both be installed and operate independently. .. data:: default_settings A dictionary of default settings. This is empty in :class:`~django_auth_ldap.backend.LDAPBackend`, but subclasses can populate this with values that will override the built-in defaults. Note that the keys should omit the ``'AUTH_LDAP_'`` prefix. .. method:: populate_user(username) Populates the Django user for the given LDAP username. This connects to the LDAP directory with the default credentials and attempts to populate the indicated Django user as if they had just logged in. :setting:`AUTH_LDAP_ALWAYS_UPDATE_USER` is ignored (assumed ``True``). .. method:: get_user_model(self) Returns the user model that :meth:`~django_auth_ldap.backend.LDAPBackend.get_or_build_user` will instantiate. By default, custom user models will be respected. Subclasses would most likely override this in order to substitute a :ref:`proxy model `. .. method:: authenticate_ldap_user(self, ldap_user, password) Given an LDAP user object and password, authenticates the user and returns a Django user object. See :ref:`customizing-authentication`. .. method:: get_or_build_user(self, username, ldap_user) Given a username and an LDAP user object, this must return a valid Django user model instance. The ``username`` argument has already been passed through :meth:`~django_auth_ldap.backend.LDAPBackend.ldap_to_django_username`. You can get information about the LDAP user via ``ldap_user.dn`` and ``ldap_user.attrs``. The return value must be the same as :meth:`~django.db.models.query.QuerySet.get_or_create`--an (instance, created) two-tuple--although the instance does not need to be saved yet. The default implementation looks for the username with a case-insensitive query; if it's not found, the model returned by :meth:`~django_auth_ldap.backend.LDAPBackend.get_user_model` will be created with the lowercased username. New users will not be saved to the database until after the :data:`django_auth_ldap.backend.populate_user` signal has been sent. A subclass may override this to associate LDAP users to Django users any way it likes. .. method:: get_or_create_user(self, username, ldap_user) .. warning:: Deprecated. This is supported for backwards-compatibility, but will be removed in a future version. Like :meth:`get_or_build_user`, but always returns a saved model instance. If you're overriding this, please convert to the new method. .. method:: ldap_to_django_username(username) Returns a valid Django username based on the given LDAP username (which is what the user enters). By default, ``username`` is returned unchanged. This can be overridden by subclasses. .. method:: django_to_ldap_username(username) The inverse of :meth:`~django_auth_ldap.backend.LDAPBackend.ldap_to_django_username`. If this is not symmetrical to :meth:`~django_auth_ldap.backend.LDAPBackend.ldap_to_django_username`, the behavior is undefined. django-auth-ldap-1.4.0/docs/source/conf.py0000644000076600000240000001303213255035541021154 0ustar psagersstaff00000000000000#!/usr/bin/env python3 # -*- coding: utf-8 -*- # # django-auth-ldap documentation build configuration file, created by # sphinx-quickstart on Mon Sep 25 08:38:18 2017. # # This file is execfile()d with the current directory set to its # containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. import os import sys sys.path.insert(0, os.path.abspath('../ext')) # -- General configuration ------------------------------------------------ # If your documentation needs a minimal Sphinx version, state it here. # # needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ 'sphinx.ext.intersphinx', 'daldocs', ] # Add any paths that contain templates here, relative to this directory. # templates_path = ['_templates'] # The suffix(es) of source filenames. # You can specify multiple suffix as a list of string: # # source_suffix = ['.rst', '.md'] source_suffix = '.rst' # The master toctree document. master_doc = 'index' # General information about the project. project = 'django-auth-ldap' copyright = '2009, Peter Sagerson' author = 'Peter Sagerson' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. version = '1.4' # The full version, including alpha/beta/rc tags. release = '1.4.0' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. # # This is also used if you do content translation via gettext catalogs. # Usually you set "language" from the command line for these cases. language = None # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. # This patterns also effect to html_static_path and html_extra_path exclude_patterns = [] # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # If true, `todo` and `todoList` produce output, else they produce nothing. todo_include_todos = False # -- Options for HTML output ---------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. # html_theme = 'default' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. # # html_theme_options = {} # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". # html_static_path = ['_static'] # Custom sidebar templates, must be a dictionary that maps document names # to template names. # # This is required for the alabaster theme # refs: https://alabaster.readthedocs.io/en/latest/installation.html#sidebars # html_sidebars = { # '**': [ # 'about.html', # 'navigation.html', # 'relations.html', # needs 'show_related': True theme option to display # 'searchbox.html', # ] # } # -- Options for HTMLHelp output ------------------------------------------ # Output file base name for HTML help builder. htmlhelp_basename = 'django-auth-ldapdoc' # -- Options for LaTeX output --------------------------------------------- latex_elements = { # The paper size ('letterpaper' or 'a4paper'). # # 'papersize': 'letterpaper', # The font size ('10pt', '11pt' or '12pt'). # # 'pointsize': '10pt', # Additional stuff for the LaTeX preamble. # # 'preamble': '', # Latex figure (float) alignment # # 'figure_align': 'htbp', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ (master_doc, 'django-auth-ldap.tex', 'django-auth-ldap Documentation', 'Peter Sagerson', 'manual'), ] # -- Options for manual page output --------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ (master_doc, 'django-auth-ldap', 'django-auth-ldap Documentation', [author], 1) ] # -- Options for Texinfo output ------------------------------------------- # Grouping the document tree into Texinfo files. List of tuples # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ (master_doc, 'django-auth-ldap', 'django-auth-ldap Documentation', author, 'django-auth-ldap', 'One line description of project.', 'Miscellaneous'), ] # Example configuration for intersphinx: refer to the Python standard library. # intersphinx_mapping = {'https://docs.python.org/': None} intersphinx_mapping = { 'python': ('https://docs.python.org/', None), 'django': ('https://docs.djangoproject.com/en/dev/', 'https://docs.djangoproject.com/en/dev/_objects/'), 'pythonldap': ('https://python-ldap.org/en/latest/', None), } django-auth-ldap-1.4.0/docs/source/multiconfig.rst0000644000076600000240000000335113151036722022727 0ustar psagersstaff00000000000000Multiple LDAP Configs ===================== .. versionadded:: 1.1 You've probably noticed that all of the settings for this backend have the prefix AUTH_LDAP\_. This is the default, but it can be customized by subclasses of :class:`~django_auth_ldap.backend.LDAPBackend`. The main reason you would want to do this is to create two backend subclasses that reference different collections of settings and thus operate independently. For example, you might have two separate LDAP servers that you want to authenticate against. A short example should demonstrate this: .. code-block:: python # mypackage.ldap from django_auth_ldap.backend import LDAPBackend class LDAPBackend1(LDAPBackend): settings_prefix = "AUTH_LDAP_1_" class LDAPBackend2(LDAPBackend): settings_prefix = "AUTH_LDAP_2_" .. code-block:: python # settings.py AUTH_LDAP_1_SERVER_URI = "ldap://ldap1.example.com" AUTH_LDAP_1_USER_DN_TEMPLATE = "uid=%(user)s,ou=users,dc=example,dc=com" AUTH_LDAP_2_SERVER_URI = "ldap://ldap2.example.com" AUTH_LDAP_2_USER_DN_TEMPLATE = "uid=%(user)s,ou=users,dc=example,dc=com" AUTHENTICATION_BACKENDS = ( "mypackage.ldap.LDAPBackend1", "mypackage.ldap.LDAPBackend2", ) All of the usual rules apply: Django will attempt to authenticate a user with each backend in turn until one of them succeeds. When a particular backend successfully authenticates a user, that user will be linked to the backend for the duration of their session. .. note:: Due to its global nature, :setting:`AUTH_LDAP_GLOBAL_OPTIONS` ignores the settings prefix. Regardless of how many backends are installed, this setting is referenced once by its default name at the time we load the ldap module. django-auth-ldap-1.4.0/docs/source/permissions.rst0000644000076600000240000000771513151036722022772 0ustar psagersstaff00000000000000Permissions =========== Groups are useful for more than just populating the user's ``is_*`` fields. :class:`~django_auth_ldap.backend.LDAPBackend` would not be complete without some way to turn a user's LDAP group memberships into Django model permissions. In fact, there are two ways to do this. Ultimately, both mechanisms need some way to map LDAP groups to Django groups. Implementations of :class:`~django_auth_ldap.config.LDAPGroupType` will have an algorithm for deriving the Django group name from the LDAP group. Clients that need to modify this behavior can subclass the :class:`~django_auth_ldap.config.LDAPGroupType` class. All of the built-in implementations take a ``name_attr`` argument to ``__init__``, which specifies the LDAP attribute from which to take the Django group name. By default, the ``cn`` attribute is used. Using Groups Directly --------------------- The least invasive way to map group permissions is to set :setting:`AUTH_LDAP_FIND_GROUP_PERMS` to ``True``. :class:`~django_auth_ldap.backend.LDAPBackend` will then find all of the LDAP groups that a user belongs to, map them to Django groups, and load the permissions for those groups. You will need to create the Django groups and associate permissions yourself, generally through the admin interface. To minimize traffic to the LDAP server, :class:`~django_auth_ldap.backend.LDAPBackend` can make use of Django's cache framework to keep a copy of a user's LDAP group memberships. To enable this feature, set :setting:`AUTH_LDAP_CACHE_GROUPS` to ``True``. You can also set :setting:`AUTH_LDAP_GROUP_CACHE_TIMEOUT` to override the timeout of cache entries (in seconds). .. code-block:: python AUTH_LDAP_CACHE_GROUPS = True AUTH_LDAP_GROUP_CACHE_TIMEOUT = 300 Group Mirroring --------------- The second way to turn LDAP group memberships into permissions is to mirror the groups themselves. This approach has some important disadvantages and should be avoided if possible. For one thing, membership will only be updated when the user authenticates, which may be especially inappropriate for sites with long session timeouts. If :setting:`AUTH_LDAP_MIRROR_GROUPS` is ``True``, then every time a user logs in, :class:`~django_auth_ldap.backend.LDAPBackend` will update the database with the user's LDAP groups. Any group that doesn't exist will be created and the user's Django group membership will be updated to exactly match their LDAP group membership. If the LDAP server has nested groups, the Django database will end up with a flattened representation. For group mirroring to have any effect, you of course need :class:`~django.contrib.auth.backends.ModelBackend` installed as an authentication backend. By default, we assume that LDAP is the sole authority on group membership; if you remove a user from a group in LDAP, they will be removed from the corresponding Django group the next time they log in. It is also possible to have django-auth-ldap ignore some Django groups, presumably because they are managed manually or through some other mechanism. If :setting:`AUTH_LDAP_MIRROR_GROUPS` is a list of group names, we will manage these groups and no others. If :setting:`AUTH_LDAP_MIRROR_GROUPS_EXCEPT` is a list of group names, we will manage all groups except those named; :setting:`AUTH_LDAP_MIRROR_GROUPS` is ignored in this case. Non-LDAP Users -------------- :class:`~django_auth_ldap.backend.LDAPBackend` has one more feature pertaining to permissions, which is the ability to handle authorization for users that it did not authenticate. For example, you might be using :class:`~django.contrib.auth.backends.RemoteUserBackend` to map externally authenticated users to Django users. By setting :setting:`AUTH_LDAP_AUTHORIZE_ALL_USERS`, :class:`~django_auth_ldap.backend.LDAPBackend` will map these users to LDAP users in the normal way in order to provide authorization information. Note that this does *not* work with :setting:`AUTH_LDAP_MIRROR_GROUPS`; group mirroring is a feature of authentication, not authorization. django-auth-ldap-1.4.0/docs/source/.spell.utf-8.add.spl0000644000076600000240000000272313151036722023265 0ustar psagersstaff00000000000000VIMspell2˙šabcdefgilmnoprstuwcdlptutivedirectorygrouptype€dmhandleFrinic]eiphstr]sthackendws]nonftigsri]b]cjnango'˜ss€mployeenumUbilterst]reirtlogUgvennaDmoupofnuames€@tniquenenxac]t&idoap]1]2b6gosackend€F1F2bjecFtearch€unioFncalho&seoymtberd4n]hdelbackenFdpackacgeostedagmcroupofnuame>sniqueÉne}mtsehtbnrt]ujectcslasFs›ielevehl]gheruser]sho]ylebotinu]msixgroupemoteusere]ntutleveFlarrttleaKmbclassin linst]diquemËeernsampe 'f]8w]w¨ADGLMNPRSaegopsuctiveDirectoryGroupTypeN?sroupOfNUame7sniqueTNDAPB/GOSackend?1?2bjec?tearchUnio?neomberD-NdelBacken?destedAEGMĽeosi-xemoteUseˇrtarrtTL?SeamHandle?rddmployeeNumbeirtLoggvenNa=moupOfNUamCeniqueSNbjectCSlaCsĂiosixGrou?petLeve?lniqueM(edjango-auth-ldap-1.4.0/docs/source/users.rst0000644000076600000240000001456713253750522021567 0ustar psagersstaff00000000000000User objects ============ Authenticating against an external source is swell, but Django's auth module is tightly bound to a user model. When a user logs in, we have to create a model object to represent them in the database. Because the LDAP search is case-insensitive, the default implementation also searches for existing Django users with an iexact query and new users are created with lowercase usernames. See :meth:`~django_auth_ldap.backend.LDAPBackend.get_or_build_user` if you'd like to override this behavior. See :meth:`~django_auth_ldap.backend.LDAPBackend.get_user_model` if you'd like to substitute a proxy model. By default, lookups on existing users are done using the user model's :attr:`~django.contrib.auth.models.CustomUser.USERNAME_FIELD`. To lookup by a different field, use :setting:`AUTH_LDAP_USER_LOOKUP_FIELD`. When set, the username field is ignored. When using the default for lookups, the only required field for a user is the username. The default :class:`~django.contrib.auth.models.User` model can be picky about the characters allowed in usernames, so :class:`~django_auth_ldap.backend.LDAPBackend` includes a pair of hooks, :meth:`~django_auth_ldap.backend.LDAPBackend.ldap_to_django_username` and :meth:`~django_auth_ldap.backend.LDAPBackend.django_to_ldap_username`, to translate between LDAP usernames and Django usernames. You may need this, for example, if your LDAP names have periods in them. You can subclass :class:`~django_auth_ldap.backend.LDAPBackend` to implement these hooks; by default the username is not modified. :class:`~django.contrib.auth.models.User` objects that are authenticated by :class:`~django_auth_ldap.backend.LDAPBackend` will have an :attr:`ldap_username` attribute with the original (LDAP) username. :attr:`~django.contrib.auth.models.User.username` (or :meth:`~django.contrib.auth.models.AbstractBaseUser.get_username`) will, of course, be the Django username. .. note:: Users created by :class:`~django_auth_ldap.backend.LDAPBackend` will have an unusable password set. This will only happen when the user is created, so if you set a valid password in Django, the user will be able to log in through :class:`~django.contrib.auth.backends.ModelBackend` (if configured) even if they are rejected by LDAP. This is not generally recommended, but could be useful as a fail-safe for selected users in case the LDAP server is unavailable. Populating Users ---------------- You can perform arbitrary population of your user models by adding listeners to the :mod:`Django signal `: :data:`django_auth_ldap.backend.populate_user`. This signal is sent after the user object has been constructed (but not necessarily saved) and any configured attribute mapping has been applied (see below). You can use this to propagate information from the LDAP directory to the user object any way you like. If you need the user object to exist in the database at this point, you can save it in your signal handler or override :meth:`~django_auth_ldap.backend.LDAPBackend.get_or_build_user`. In either case, the user instance will be saved automatically after the signal handlers are run. If you need an attribute that isn't included by default in the LDAP search results, see :setting:`AUTH_LDAP_USER_ATTRLIST`. Easy Attributes --------------- If you just want to copy a few attribute values directly from the user's LDAP directory entry to their Django user, the setting, :setting:`AUTH_LDAP_USER_ATTR_MAP`, makes it easy. This is a dictionary that maps user model keys, respectively, to (case-insensitive) LDAP attribute names:: AUTH_LDAP_USER_ATTR_MAP = {"first_name": "givenName", "last_name": "sn"} Only string fields can be mapped to attributes. Boolean fields can be defined by group membership:: AUTH_LDAP_USER_FLAGS_BY_GROUP = { "is_active": "cn=active,ou=groups,dc=example,dc=com", "is_staff": ( LDAPGroupQuery("cn=staff,ou=groups,dc=example,dc=com") | LDAPGroupQuery("cn=admin,ou=groups,dc=example,dc=com") ), "is_superuser": "cn=superuser,ou=groups,dc=example,dc=com" } Values in this dictionary may be simple DNs (as strings), lists or tuples of DNs, or :class:`~django_auth_ldap.config.LDAPGroupQuery` instances. Lists are converted to queries joined by ``|``. Remember that if these settings don't do quite what you want, you can always use the signals described in the previous section to implement your own logic. Updating Users -------------- By default, all mapped user fields will be updated each time the user logs in. To disable this, set :setting:`AUTH_LDAP_ALWAYS_UPDATE_USER` to ``False``. If you need to populate a user outside of the authentication process—for example, to create associated model objects before the user logs in for the first time—you can call :meth:`django_auth_ldap.backend.LDAPBackend.populate_user`. You'll need an instance of :class:`~django_auth_ldap.backend.LDAPBackend`, which you should feel free to create yourself. :meth:`~django_auth_ldap.backend.LDAPBackend.populate_user` returns the :class:`~django.contrib.auth.models.User` or `None` if the user could not be found in LDAP. .. code-block:: python from django_auth_ldap.backend import LDAPBackend user = LDAPBackend().populate_user('alice') if user is None: raise Exception('No user named alice') .. _ldap_user: Direct Attribute Access ----------------------- If you need to access multi-value attributes or there is some other reason that the above is inadequate, you can also access the user's raw LDAP attributes. ``user.ldap_user`` is an object with four public properties. The group properties are, of course, only valid if groups are configured. * ``dn``: The user's distinguished name. * ``attrs``: The user's LDAP attributes as a dictionary of lists of string values. The dictionaries are modified to use case-insensitive keys. * ``group_dns``: The set of groups that this user belongs to, as DNs. * ``group_names``: The set of groups that this user belongs to, as simple names. These are the names that will be used if :setting:`AUTH_LDAP_MIRROR_GROUPS` is used. Python-ldap returns all attribute values as utf8-encoded strings. For convenience, this module will try to decode all values into Unicode strings. Any string that can not be successfully decoded will be left as-is; this may apply to binary values such as Active Directory's objectSid. django-auth-ldap-1.4.0/docs/source/logging.rst0000644000076600000240000000112713151036722022034 0ustar psagersstaff00000000000000Logging ======= :class:`~django_auth_ldap.backend.LDAPBackend` uses the standard logging module to log debug and warning messages to the logger named ``'django_auth_ldap'``. If you need debug messages to help with configuration issues, you should add a handler to this logger. Note that this logger is initialized with a level of NOTSET, so you may need to change the level of the logger in order to get debug messages. .. code-block:: python import logging logger = logging.getLogger('django_auth_ldap') logger.addHandler(logging.StreamHandler()) logger.setLevel(logging.DEBUG) django-auth-ldap-1.4.0/docs/source/example.rst0000644000076600000240000000462213155540144022046 0ustar psagersstaff00000000000000Example Configuration ===================== Here is a complete example configuration from :file:`settings.py` that exercises nearly all of the features. In this example, we're authenticating against a global pool of users in the directory, but we have a special area set aside for Django groups (ou=django,ou=groups,dc=example,dc=com). Remember that most of this is optional if you just need simple authentication. Some default settings and arguments are included for completeness. .. code-block:: python import ldap from django_auth_ldap.config import LDAPSearch, GroupOfNamesType # Baseline configuration. AUTH_LDAP_SERVER_URI = "ldap://ldap.example.com" AUTH_LDAP_BIND_DN = "cn=django-agent,dc=example,dc=com" AUTH_LDAP_BIND_PASSWORD = "phlebotinum" AUTH_LDAP_USER_SEARCH = LDAPSearch("ou=users,dc=example,dc=com", ldap.SCOPE_SUBTREE, "(uid=%(user)s)") # or perhaps: # AUTH_LDAP_USER_DN_TEMPLATE = "uid=%(user)s,ou=users,dc=example,dc=com" # Set up the basic group parameters. AUTH_LDAP_GROUP_SEARCH = LDAPSearch("ou=django,ou=groups,dc=example,dc=com", ldap.SCOPE_SUBTREE, "(objectClass=groupOfNames)" ) AUTH_LDAP_GROUP_TYPE = GroupOfNamesType(name_attr="cn") # Simple group restrictions AUTH_LDAP_REQUIRE_GROUP = "cn=enabled,ou=django,ou=groups,dc=example,dc=com" AUTH_LDAP_DENY_GROUP = "cn=disabled,ou=django,ou=groups,dc=example,dc=com" # Populate the Django user from the LDAP directory. AUTH_LDAP_USER_ATTR_MAP = { "first_name": "givenName", "last_name": "sn", "email": "mail" } AUTH_LDAP_USER_FLAGS_BY_GROUP = { "is_active": "cn=active,ou=django,ou=groups,dc=example,dc=com", "is_staff": "cn=staff,ou=django,ou=groups,dc=example,dc=com", "is_superuser": "cn=superuser,ou=django,ou=groups,dc=example,dc=com" } # This is the default, but I like to be explicit. AUTH_LDAP_ALWAYS_UPDATE_USER = True # Use LDAP group membership to calculate group permissions. AUTH_LDAP_FIND_GROUP_PERMS = True # Cache group memberships for an hour to minimize LDAP traffic AUTH_LDAP_CACHE_GROUPS = True AUTH_LDAP_GROUP_CACHE_TIMEOUT = 3600 # Keep ModelBackend around for per-user permissions and maybe a local # superuser. AUTHENTICATION_BACKENDS = ( 'django_auth_ldap.backend.LDAPBackend', 'django.contrib.auth.backends.ModelBackend', ) django-auth-ldap-1.4.0/docs/source/changes.rst0000644000076600000240000000006213151036722022013 0ustar psagersstaff00000000000000Change Log ========== .. include:: ../../CHANGES django-auth-ldap-1.4.0/docs/source/performance.rst0000644000076600000240000000324213155540144022711 0ustar psagersstaff00000000000000Performance =========== :class:`~django_auth_ldap.backend.LDAPBackend` is carefully designed not to require a connection to the LDAP service for every request. Of course, this depends heavily on how it is configured. If LDAP traffic or latency is a concern for your deployment, this section has a few tips on minimizing it, in decreasing order of impact. #. **Cache groups**. If :setting:`AUTH_LDAP_FIND_GROUP_PERMS` is ``True``, the default behavior is to reload a user's group memberships on every request. This is the safest behavior, as any membership change takes effect immediately, but it is expensive. If possible, set :setting:`AUTH_LDAP_CACHE_GROUPS` to ``True`` to remove most of this traffic. #. **Don't access user.ldap_user.***. Except for ``ldap_user.dn``, these properties are only cached on a per-request basis. If you can propagate LDAP attributes to a :class:`~django.contrib.auth.models.User`, they will only be updated at login. ``user.ldap_user.attrs`` triggers an LDAP connection for every request in which it's accessed. #. **Use simpler group types**. Some grouping mechanisms are more expensive than others. This will often be outside your control, but it's important to note that the extra functionality of more complex group types like :class:`~django_auth_ldap.config.NestedGroupOfNamesType` is not free and will generally require a greater number and complexity of LDAP queries. #. **Use direct binding**. Binding with :setting:`AUTH_LDAP_USER_DN_TEMPLATE` is a little bit more efficient than relying on :setting:`AUTH_LDAP_USER_SEARCH`. Specifically, it saves two LDAP operations (one bind and one search) per login. django-auth-ldap-1.4.0/docs/source/.spell.utf-8.add0000644000076600000240000000144113151036722022464 0ustar psagersstaff00000000000000LDAP AUTH ldap django auth config LDAPSearch DN username LDAPSearchUnion ou dc otherusers ONELEVEL backend LDAPBackend usernames uid localhost StartTLS ldaps DNs s contrib BACKENDS APPS www org backends ModelBackend LDAPGroupType PosixGroupType MemberDNGroupType NestedMemberDNGroupType groupOfNames groupOfUniqueNames GroupOfNamesType NestedGroupOfNamesType GroupOfUniqueNamesType NestedGroupOfUniqueNamesType ActiveDirectoryGroupType NestedActiveDirectoryGroupType objectClass cn API posixGroup Django's iexact meth attr dn attrs dns utf8 objectSid alice Django RemoteUserBackend init admin NOTSET getLogger addHandler StreamHandler setLevel py phlebotinum givenName sn employeeNumber LDAPObject tls users's filterstr subclassing uniqueMember Configs mypackage LDAPBackend1 LDAPBackend2 ldap1 ldap2 django-auth-ldap-1.4.0/docs/source/groups.rst0000644000076600000240000001036013151036722021724 0ustar psagersstaff00000000000000Working With Groups =================== Types of Groups --------------- Working with groups in LDAP can be a tricky business, mostly because there are so many different kinds. This module includes an extensible API for working with any kind of group and includes implementations for the most common ones. :class:`~django_auth_ldap.config.LDAPGroupType` is a base class whose concrete subclasses can determine group membership for particular grouping mechanisms. Four built-in subclasses cover most grouping mechanisms: * :class:`~django_auth_ldap.config.PosixGroupType` * :class:`~django_auth_ldap.config.NISGroupType` * :class:`~django_auth_ldap.config.MemberDNGroupType` * :class:`~django_auth_ldap.config.NestedMemberDNGroupType` posixGroup and nisNetgroup objects are somewhat specialized, so they get their own classes. The other two cover mechanisms whereby a group object stores a list of its members as distinguished names. This includes groupOfNames, groupOfUniqueNames, and Active Directory groups, among others. The nested variant allows groups to contain other groups, to as many levels as you like. For convenience and readability, several trivial subclasses of the above are provided: * :class:`~django_auth_ldap.config.GroupOfNamesType` * :class:`~django_auth_ldap.config.NestedGroupOfNamesType` * :class:`~django_auth_ldap.config.GroupOfUniqueNamesType` * :class:`~django_auth_ldap.config.NestedGroupOfUniqueNamesType` * :class:`~django_auth_ldap.config.ActiveDirectoryGroupType` * :class:`~django_auth_ldap.config.NestedActiveDirectoryGroupType` * :class:`~django_auth_ldap.config.OrganizationalRoleGroupType` * :class:`~django_auth_ldap.config.NestedOrganizationalRoleGroupType` Finding Groups -------------- To get started, you'll need to provide some basic information about your LDAP groups. :setting:`AUTH_LDAP_GROUP_SEARCH` is an :class:`~django_auth_ldap.config.LDAPSearch` object that identifies the set of relevant group objects. That is, all groups that users might belong to as well as any others that we might need to know about (in the case of nested groups, for example). :setting:`AUTH_LDAP_GROUP_TYPE` is an instance of the class corresponding to the type of group that will be returned by :setting:`AUTH_LDAP_GROUP_SEARCH`. All groups referenced elsewhere in the configuration must be of this type and part of the search results. .. code-block:: python import ldap from django_auth_ldap.config import LDAPSearch, GroupOfNamesType AUTH_LDAP_GROUP_SEARCH = LDAPSearch("ou=groups,dc=example,dc=com", ldap.SCOPE_SUBTREE, "(objectClass=groupOfNames)" ) AUTH_LDAP_GROUP_TYPE = GroupOfNamesType() .. _limiting-access: Limiting Access --------------- The simplest use of groups is to limit the users who are allowed to log in. If :setting:`AUTH_LDAP_REQUIRE_GROUP` is set, then only users who are members of that group will successfully authenticate. :setting:`AUTH_LDAP_DENY_GROUP` is the reverse: if given, members of this group will be rejected. .. code-block:: python AUTH_LDAP_REQUIRE_GROUP = "cn=enabled,ou=groups,dc=example,dc=com" AUTH_LDAP_DENY_GROUP = "cn=disabled,ou=groups,dc=example,dc=com" However, these two settings alone may not be enough to satisfy your needs. In such cases, you can use the :class:`~django_auth_ldap.config.LDAPGroupQuery` object to perform more complex matches against a user's groups. For example: .. code-block:: python from django_auth_ldap.config import LDAPGroupQuery AUTH_LDAP_REQUIRE_GROUP = ( ( LDAPGroupQuery("cn=enabled,ou=groups,dc=example,dc=com") | LDAPGroupQuery("cn=also_enabled,ou=groups,dc=example,dc=com") ) & ~LDAPGroupQuery("cn=disabled,ou=groups,dc=example,dc=com") ) It is important to note a couple features of the example above. First and foremost, this handles the case of both `AUTH_LDAP_REQUIRE_GROUP` and `AUTH_LDAP_DENY_GROUP` in one setting. Second, you can use three operators on these queries: ``&``, ``|``, and ``~``: ``and``, ``or``, and ``not``, respectively. When groups are configured, you can always get the list of a user's groups from ``user.ldap_user.group_dns`` or ``user.ldap_user.group_names``. More advanced uses of groups are covered in the next two sections. django-auth-ldap-1.4.0/docs/make.bat0000644000076600000240000000146213162741710017765 0ustar psagersstaff00000000000000@ECHO OFF pushd %~dp0 REM Command file for Sphinx documentation if "%SPHINXBUILD%" == "" ( set SPHINXBUILD=python -msphinx ) set SOURCEDIR=source set BUILDDIR=build set SPHINXPROJ=django-auth-ldap if "%1" == "" goto help %SPHINXBUILD% >NUL 2>NUL if errorlevel 9009 ( echo. echo.The Sphinx module was not found. Make sure you have Sphinx installed, echo.then set the SPHINXBUILD environment variable to point to the full echo.path of the 'sphinx-build' executable. Alternatively you may add the echo.Sphinx directory to PATH. echo. echo.If you don't have Sphinx installed, grab it from echo.http://sphinx-doc.org/ exit /b 1 ) %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% goto end :help %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% :end popd django-auth-ldap-1.4.0/README.md0000644000076600000240000001423113253750522016707 0ustar psagersstaff00000000000000# django-auth-ldap This is a Django authentication backend that authenticates against an LDAP service. Configuration can be as simple as a single distinguished name template, but there are many rich configuration options for working with users, groups, and permissions. This version is supported on Python 2.7 and 3.4+; and Django 1.11+. It requires [python-ldap][] >= 3.0. * Repository: https://bitbucket.org/illocution/django-auth-ldap * Documentation: https://django-auth-ldap.readthedocs.io/ * Mailing list: https://groups.google.com/group/django-auth-ldap ## History and Status As with so many efforts of this type, this is a minor side project that I spun out of a private Django deployment. The BSD license, in many ways a formality, is also a pretty accurate description of the project's motivation and status: "Here's a thing I did. I found it useful, maybe you will too. Do whatever you like with it." Although this library has long done everything I need it to, I continue to maintain it as a small contribution to the Django community. Important bugs I try to process fairly quickly. Less urgent issues may queue up until I can break away from other work. ## Contributing If you'd like to report an issue or contribute a feature, but you're not sure how to proceed, start with the [mailing list][]. This may clear up some misunderstandings or provide a gut check on how feasible the idea is. If you have something concrete you'd like to contribute, the best approach is to send a well-formed pull request, complete with tests and documentation, as needed. Pull requests that lack tests or documentation or that break existing tests will probably not be taken very seriously. Pull requests should also be focused: trying to do more than one thing in a single request will make it more difficult to process. If you have a bug or feature request that you can't or don't wish to fix or implement, you can try [logging an issue][issues]. Serious bugs should get taken care of quickly, but less urgent issues may or may not attract any attention. It just depends on whether anyone else finds it interesting enough to do something about. There's no harm in creating an issue and then submitting a pull request to resolve it. This can be a good way to start a conversation and can serve as an anchor point if the initial pull request turns out not to be the best approach. Here are a few dos and don'ts to help us all save some time. * **Don't** move fast and break stuff. * **Do** propose incremental fixes or improvements that solve well-defined problems with minimal collatoral effects. * **Do** feel free to do a bit of syntactic cleanup, especially when it comes to leaving behind obsolete Python or Django versions. This project goes back at least to Python 2.3 and Django 1.0; youngins may find some lingering anachronisms disorienting. * **Don't** do a bunch of semantic cleanup without a clear and compelling reason. The phrase "I don't see how this could break anything" is a confession of the ignorance and uncertainty under which we all labor, not a proof of correctness. * **Do** reach out if you'd like a feature or change and you're not sure how to proceed. ## Development To get set up for development, activate your virtualenv and use pip to install from requirements-dev.txt: % pip install -r requirements-dev.txt To run the tests: % django-admin test --settings tests.settings To run the full test suite in a range of environments, run [tox][] from the root of the project: % tox This includes some static analysis to detect potential runtime errors and style issues. ## Mercurial django-auth-ldap uses [Mercurial][hg] for source control. If you're more familiar with Git, Mercurial is similar in many ways, but there are a few important differences to keep in mind. Mercurial branches are more or less permanent and thus not very good for feature work or pull requests. If you want to work on multiple features at once, use [bookmarks][hg-bookmark] or [topics][hg-topic] instead (Bitbucket may not recognize topics yet). The default bookmark is called ``@`` (similar to git's master branch). % hg up @ % hg bookmark new-feature (make changes) % hg ci % hg push -B new-feature Local Mercurial clones and Bitbucket forks are all (typically) [non-publishing][hg-non-publishing] repositories. This means that new changesets remain in draft mode and can be modified in a safe and principled manner with the [evolve][hg-evolve-ext] extension. I make heavy use of [changeset evolution][hg-evolution] and frequently rely it to process pull requests while keeping the history clean and linear. If you're setting up Mercurial for the first time, I recommend you make sure you have the latest version and install [hg-evolve][pypi-evolve] with it. Here's a sample ~/.hgrc to get started: [ui] username = Your Name ignore = ~/.hgignore style = phases [extensions] color = evolve = histedit = rebase = shelve = topic = [alias] glog = log --graph You should also go to the Labs section of your bitbucket.org account settings and turn on evolution support. Changeset evolution is a big topic, but one of the most useful things to know is that it's safe to amend existing draft changesets even if they've already been shared with other non-publishing repositories: % hg up @ % hg bookmark new-feature (make changes) % hg ci % hg push -B new-feature (incorporate feedback) % hg amend % hg push [python-ldap]: https://pypi.python.org/pypi/python-ldap [mailing list]: https://groups.google.com/group/django-auth-ldap [issues]: https://bitbucket.org/illocution/django-auth-ldap/issues?status=new&status=open [tox]: https://tox.readthedocs.io/ [hg]: https://www.mercurial-scm.org/ [hg-bookmark]: https://www.mercurial-scm.org/wiki/Bookmarks [hg-topic]: https://www.mercurial-scm.org/doc/evolution/tutorials/topic-tutorial.html [hg-non-publishing]: https://www.mercurial-scm.org/wiki/Phases#Publishing_Repository [hg-evolve-ext]: https://www.mercurial-scm.org/wiki/EvolveExtension [hg-evolution]: https://www.mercurial-scm.org/doc/evolution/ [pypi-evolve]: https://pypi.python.org/pypi/hg-evolve django-auth-ldap-1.4.0/setup.py0000644000076600000240000001021413253750522017137 0ustar psagersstaff00000000000000#!/usr/bin/env python from __future__ import unicode_literals from setuptools import setup import django_auth_ldap long_description = """\ This is a Django authentication backend that authenticates against an LDAP service. Configuration can be as simple as a single distinguished name template, but there are many rich configuration options for working with users, groups, and permissions. This version is supported on Python 2.7 and 3.4+; and Django 1.11+. It requires `python-ldap `_ >= 3.0. * Repository: https://bitbucket.org/illocution/django-auth-ldap * Documentation: https://django-auth-ldap.readthedocs.io/ * Mailing list: https://groups.google.com/group/django-auth-ldap Following is an example configuration, just to whet your appetite:: import ldap from django_auth_ldap.config import LDAPSearch, GroupOfNamesType # Baseline configuration. AUTH_LDAP_SERVER_URI = "ldap://ldap.example.com" AUTH_LDAP_BIND_DN = "cn=django-agent,dc=example,dc=com" AUTH_LDAP_BIND_PASSWORD = "phlebotinum" AUTH_LDAP_USER_SEARCH = LDAPSearch("ou=users,dc=example,dc=com", ldap.SCOPE_SUBTREE, "(uid=%(user)s)") # or perhaps: # AUTH_LDAP_USER_DN_TEMPLATE = "uid=%(user)s,ou=users,dc=example,dc=com" # Set up the basic group parameters. AUTH_LDAP_GROUP_SEARCH = LDAPSearch("ou=django,ou=groups,dc=example,dc=com", ldap.SCOPE_SUBTREE, "(objectClass=groupOfNames)" ) AUTH_LDAP_GROUP_TYPE = GroupOfNamesType() # Simple group restrictions AUTH_LDAP_REQUIRE_GROUP = "cn=enabled,ou=django,ou=groups,dc=example,dc=com" AUTH_LDAP_DENY_GROUP = "cn=disabled,ou=django,ou=groups,dc=example,dc=com" # Populate the Django user from the LDAP directory. AUTH_LDAP_USER_ATTR_MAP = { "first_name": "givenName", "last_name": "sn", "email": "mail" } AUTH_LDAP_USER_FLAGS_BY_GROUP = { "is_active": "cn=active,ou=django,ou=groups,dc=example,dc=com", "is_staff": "cn=staff,ou=django,ou=groups,dc=example,dc=com", "is_superuser": "cn=superuser,ou=django,ou=groups,dc=example,dc=com" } # Use LDAP group membership to calculate group permissions. AUTH_LDAP_FIND_GROUP_PERMS = True # Cache group memberships for an hour to minimize LDAP traffic AUTH_LDAP_CACHE_GROUPS = True AUTH_LDAP_GROUP_CACHE_TIMEOUT = 3600 # Keep ModelBackend around for per-user permissions and maybe a local # superuser. AUTHENTICATION_BACKENDS = ( 'django_auth_ldap.backend.LDAPBackend', 'django.contrib.auth.backends.ModelBackend', ) """ setup( name="django-auth-ldap", version=django_auth_ldap.version_string, description="Django LDAP authentication backend", long_description=long_description, url="https://bitbucket.org/illocution/django-auth-ldap", author="Peter Sagerson", author_email="psagers@ignorare.net", license="BSD", packages=["django_auth_ldap"], classifiers=[ 'Development Status :: 5 - Production/Stable', "Environment :: Web Environment", "Programming Language :: Python", "Programming Language :: Python :: 2", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", "Framework :: Django", "Framework :: Django :: 1.11", "Framework :: Django :: 2.0", "Intended Audience :: Developers", "Intended Audience :: System Administrators", "License :: OSI Approved :: BSD License", "Topic :: Internet :: WWW/HTTP", "Topic :: System :: Systems Administration :: Authentication/Directory :: LDAP", "Topic :: Software Development :: Libraries :: Python Modules", ], keywords=["django", "ldap", "authentication", "auth"], python_requires=">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*", install_requires=[ 'Django >= 1.11', 'python-ldap >= 3.0', ], setup_requires=[ "setuptools >= 0.6c11", ], tests_require=[ "mockldap >= 0.2.7", ] ) django-auth-ldap-1.4.0/tox.ini0000644000076600000240000000131513253750522016742 0ustar psagersstaff00000000000000[flake8] max-line-length = 120 [tox] envlist = py36-static py{27,34,35,36}-django111 py{34,35,36}-django20 py{35,36}-djangomaster py36-coverage [base] deps = mockldap==0.3.0 [testenv] commands = python -m django test --settings tests.settings deps = {[base]deps} django111: Django>=1.11,<2.0 django20: Django>=2.0,<2.1 djangomaster: https://github.com/django/django/archive/master.tar.gz [testenv:py36-static] deps = flake8==3.4.1 commands = {envbindir}/flake8 skip_install = True [testenv:py36-coverage] deps = {[base]deps} coverage commands = python -m coverage run -m django test --settings tests.settings python -m coverage report django-auth-ldap-1.4.0/setup.cfg0000644000076600000240000000014613255035702017247 0ustar psagersstaff00000000000000[bdist_wheel] universal = 1 [metadata] license_file = LICENSE [egg_info] tag_build = tag_date = 0 django-auth-ldap-1.4.0/django_auth_ldap.egg-info/0000755000076600000240000000000013255035702022402 5ustar psagersstaff00000000000000django-auth-ldap-1.4.0/django_auth_ldap.egg-info/PKG-INFO0000644000076600000240000001027213255035702023501 0ustar psagersstaff00000000000000Metadata-Version: 1.2 Name: django-auth-ldap Version: 1.4.0 Summary: Django LDAP authentication backend Home-page: https://bitbucket.org/illocution/django-auth-ldap Author: Peter Sagerson Author-email: psagers@ignorare.net License: BSD Description: This is a Django authentication backend that authenticates against an LDAP service. Configuration can be as simple as a single distinguished name template, but there are many rich configuration options for working with users, groups, and permissions. This version is supported on Python 2.7 and 3.4+; and Django 1.11+. It requires `python-ldap `_ >= 3.0. * Repository: https://bitbucket.org/illocution/django-auth-ldap * Documentation: https://django-auth-ldap.readthedocs.io/ * Mailing list: https://groups.google.com/group/django-auth-ldap Following is an example configuration, just to whet your appetite:: import ldap from django_auth_ldap.config import LDAPSearch, GroupOfNamesType # Baseline configuration. AUTH_LDAP_SERVER_URI = "ldap://ldap.example.com" AUTH_LDAP_BIND_DN = "cn=django-agent,dc=example,dc=com" AUTH_LDAP_BIND_PASSWORD = "phlebotinum" AUTH_LDAP_USER_SEARCH = LDAPSearch("ou=users,dc=example,dc=com", ldap.SCOPE_SUBTREE, "(uid=%(user)s)") # or perhaps: # AUTH_LDAP_USER_DN_TEMPLATE = "uid=%(user)s,ou=users,dc=example,dc=com" # Set up the basic group parameters. AUTH_LDAP_GROUP_SEARCH = LDAPSearch("ou=django,ou=groups,dc=example,dc=com", ldap.SCOPE_SUBTREE, "(objectClass=groupOfNames)" ) AUTH_LDAP_GROUP_TYPE = GroupOfNamesType() # Simple group restrictions AUTH_LDAP_REQUIRE_GROUP = "cn=enabled,ou=django,ou=groups,dc=example,dc=com" AUTH_LDAP_DENY_GROUP = "cn=disabled,ou=django,ou=groups,dc=example,dc=com" # Populate the Django user from the LDAP directory. AUTH_LDAP_USER_ATTR_MAP = { "first_name": "givenName", "last_name": "sn", "email": "mail" } AUTH_LDAP_USER_FLAGS_BY_GROUP = { "is_active": "cn=active,ou=django,ou=groups,dc=example,dc=com", "is_staff": "cn=staff,ou=django,ou=groups,dc=example,dc=com", "is_superuser": "cn=superuser,ou=django,ou=groups,dc=example,dc=com" } # Use LDAP group membership to calculate group permissions. AUTH_LDAP_FIND_GROUP_PERMS = True # Cache group memberships for an hour to minimize LDAP traffic AUTH_LDAP_CACHE_GROUPS = True AUTH_LDAP_GROUP_CACHE_TIMEOUT = 3600 # Keep ModelBackend around for per-user permissions and maybe a local # superuser. AUTHENTICATION_BACKENDS = ( 'django_auth_ldap.backend.LDAPBackend', 'django.contrib.auth.backends.ModelBackend', ) Keywords: django,ldap,authentication,auth Platform: UNKNOWN Classifier: Development Status :: 5 - Production/Stable Classifier: Environment :: Web Environment Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.4 Classifier: Programming Language :: Python :: 3.5 Classifier: Programming Language :: Python :: 3.6 Classifier: Framework :: Django Classifier: Framework :: Django :: 1.11 Classifier: Framework :: Django :: 2.0 Classifier: Intended Audience :: Developers Classifier: Intended Audience :: System Administrators Classifier: License :: OSI Approved :: BSD License Classifier: Topic :: Internet :: WWW/HTTP Classifier: Topic :: System :: Systems Administration :: Authentication/Directory :: LDAP Classifier: Topic :: Software Development :: Libraries :: Python Modules Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.* django-auth-ldap-1.4.0/django_auth_ldap.egg-info/SOURCES.txt0000644000076600000240000000260013255035702024264 0ustar psagersstaff00000000000000CHANGES LICENSE MANIFEST.in README.md setup.cfg setup.py tox.ini django_auth_ldap/__init__.py django_auth_ldap/backend.py django_auth_ldap/config.py django_auth_ldap.egg-info/PKG-INFO django_auth_ldap.egg-info/SOURCES.txt django_auth_ldap.egg-info/dependency_links.txt django_auth_ldap.egg-info/requires.txt django_auth_ldap.egg-info/top_level.txt docs/Makefile docs/make.bat docs/ext/daldocs.py docs/source/.spell.utf-8.add docs/source/.spell.utf-8.add.spl docs/source/authentication.rst docs/source/changes.rst docs/source/conf.py docs/source/example.rst docs/source/groups.rst docs/source/index.rst docs/source/install.rst docs/source/logging.rst docs/source/multiconfig.rst docs/source/performance.rst docs/source/permissions.rst docs/source/reference.rst docs/source/users.rst tests/__init__.py tests/__init__.pyc tests/models.py tests/models.pyc tests/settings.py tests/settings.pyc tests/tests.py tests/tests.pyc tests/__pycache__/__init__.cpython-34.pyc tests/__pycache__/__init__.cpython-35.pyc tests/__pycache__/__init__.cpython-36.pyc tests/__pycache__/models.cpython-34.pyc tests/__pycache__/models.cpython-35.pyc tests/__pycache__/models.cpython-36.pyc tests/__pycache__/settings.cpython-34.pyc tests/__pycache__/settings.cpython-35.pyc tests/__pycache__/settings.cpython-36.pyc tests/__pycache__/tests.cpython-34.pyc tests/__pycache__/tests.cpython-35.pyc tests/__pycache__/tests.cpython-36.pycdjango-auth-ldap-1.4.0/django_auth_ldap.egg-info/requires.txt0000644000076600000240000000004213255035702024776 0ustar psagersstaff00000000000000Django >= 1.11 python-ldap >= 3.0 django-auth-ldap-1.4.0/django_auth_ldap.egg-info/top_level.txt0000644000076600000240000000002113255035702025125 0ustar psagersstaff00000000000000django_auth_ldap django-auth-ldap-1.4.0/django_auth_ldap.egg-info/dependency_links.txt0000644000076600000240000000000113255035702026450 0ustar psagersstaff00000000000000