Commits

Peter Sagerson committed fa69079

Major documentation refactor and cleanup.

Comments (0)

Files changed (26)

 ^build/
 ^dist/
-^docs/_build/
-^MANIFEST$
+^docs/build/
+^MANIFEST$
Empty file added.
+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 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 officially supported on Python >= 2.5, Django >= 1.3, and
+python-ldap >= 2.0. It is known to work on earlier versions (especially of
+Django) and backwards-compatibility is not broken needlessly, however users of
+older dependencies are urged to test their deployments carefully and be wary of
+updates.
+
+Full documentation can be found at http://packages.python.org/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_PROFILE_ATTR_MAP = {
+        "employee_number": "employeeNumber"
+    }
+
+    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"
+    }
+
+    AUTH_LDAP_PROFILE_FLAGS_BY_GROUP = {
+        "is_awesome": "cn=awesome,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',
+    )

README.rst

-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 requires at least Django 1.3 and python-ldap 2.0. Full
-documentation can be found at http://packages.python.org/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_PROFILE_ATTR_MAP = {
-        "employee_number": "employeeNumber"
-    }
-
-    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"
-    }
-
-    AUTH_LDAP_PROFILE_FLAGS_BY_GROUP = {
-        "is_awesome": "cn=awesome,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',
-    )

django_auth_ldap/backend.py

 
         return _LDAPConfig.get_ldap(options)
 
-
     #
     # The Django auth backend API
     #
         return self._connection
 
 
-
 class _LDAPUserGroups(object):
     """
     Represents the set of groups that a user belongs to.
 SPHINXOPTS	  =
 SPHINXBUILD	  = sphinx-build
 PAPER		  =
+BUILDDIR          = build
 
 # Internal variables.
-PAPEROPT_a4		= -D latex_paper_size=a4
+PAPEROPT_a4	= -D latex_paper_size=a4
 PAPEROPT_letter = -D latex_paper_size=letter
-ALLSPHINXOPTS	= -d _build/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
+ALLSPHINXOPTS	= -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source
 
 .PHONY: help clean html dirhtml pickle json htmlhelp qthelp latex changes linkcheck doctest
 
 	@echo "	 doctest   to run all doctests embedded in the documentation (if enabled)"
 
 clean:
-	-rm -rf _build
+	-rm -rf $(BUILDDIR)
 
 html:
-	$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) _build/html
+	$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
 	@echo
-	@echo "Build finished. The HTML pages are in _build/html."
+	@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
 
 dirhtml:
-	$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) _build/dirhtml
+	$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
 	@echo
-	@echo "Build finished. The HTML pages are in _build/dirhtml."
+	@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
 
 pickle:
-	$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) _build/pickle
+	$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
 	@echo
 	@echo "Build finished; now you can process the pickle files."
 
 json:
-	$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) _build/json
+	$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
 	@echo
 	@echo "Build finished; now you can process the JSON files."
 
 htmlhelp:
-	$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) _build/htmlhelp
+	$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
 	@echo
 	@echo "Build finished; now you can run HTML Help Workshop with the" \
-		  ".hhp project file in _build/htmlhelp."
+		  ".hhp project file in $(BUILDDIR)/htmlhelp."
 
 qthelp:
-	$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) _build/qthelp
+	$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
 	@echo
 	@echo "Build finished; now you can run "qcollectiongenerator" with the" \
-		  ".qhcp project file in _build/qthelp, like this:"
-	@echo "# qcollectiongenerator _build/qthelp/django-auth-ldap.qhcp"
+		  ".qhcp project file in $(BUILDDIR)/qthelp, like this:"
+	@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/django-auth-ldap.qhcp"
 	@echo "To view the help file:"
-	@echo "# assistant -collectionFile _build/qthelp/django-auth-ldap.qhc"
+	@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/django-auth-ldap.qhc"
 
 latex:
-	$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) _build/latex
+	$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
 	@echo
-	@echo "Build finished; the LaTeX files are in _build/latex."
+	@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
 	@echo "Run 'make all-pdf' or 'make all-ps' in that directory to" \
 		  "run these through (pdf)latex."
 
 changes:
-	$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) _build/changes
+	$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
 	@echo
-	@echo "The overview file is in _build/changes."
+	@echo "The overview file is in $(BUILDDIR)/changes."
 
 linkcheck:
-	$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) _build/linkcheck
+	$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
 	@echo
 	@echo "Link check complete; look for any errors in the above output " \
-		  "or in _build/linkcheck/output.txt."
+		  "or in $(BUILDDIR)/linkcheck/output.txt."
 
 doctest:
-	$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) _build/doctest
+	$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
 	@echo "Testing of doctests in the sources finished, look at the " \
-		  "results in _build/doctest/output.txt."
+		  "results in $(BUILDDIR)/doctest/output.txt."
 
 zip:
-	rm _build/html.zip || true
-	cd _build/html && zip -R ../html.zip '*' -x .buildinfo -x '_sources/*'
+	rm $(BUILDDIR)/html.zip || true
+	cd $(BUILDDIR)/html && zip -R ../html.zip '*' -x .buildinfo -x '_sources/*'

docs/_templates/globaltoc.html

+{#
+    basic/globaltoc.html
+    ~~~~~~~~~~~~~~~~~~~~
+
+    Sphinx sidebar template: global table of contents.
+
+    :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+    :license: BSD, see LICENSE for details.
+#}
+<h3><a href="{{ pathto(master_doc) }}">{{ _('Table Of Contents') }}</a></h3>
+{{ toctree(maxdepth=2) }}

docs/conf.py

-# -*- coding: utf-8 -*-
-#
-# django-auth-ldap documentation build configuration file, created by
-# sphinx-quickstart on Wed Sep 23 18:06:43 2009.
-#
-# 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.
-#sys.path.append(os.path.abspath('.'))
-
-# -- General configuration -----------------------------------------------------
-
-# Add any Sphinx extension module names here, as strings. They can be extensions
-# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
-extensions = []
-
-# Add any paths that contain templates here, relative to this directory.
-templates_path = ['_templates']
-
-# The suffix of source filenames.
-source_suffix = '.rst'
-
-# The encoding of source files.
-source_encoding = 'utf-8'
-
-# The master toctree document.
-master_doc = 'index'
-
-# General information about the project.
-project = u'django-auth-ldap'
-copyright = u'2009, 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.1'
-# The full version, including alpha/beta/rc tags.
-release = '1.1.2'
-
-# The language for content autogenerated by Sphinx. Refer to documentation
-# for a list of supported languages.
-#language = None
-
-# There are two options for replacing |today|: either, you set today to some
-# non-false value, then it is used:
-#today = ''
-# Else, today_fmt is used as the format for a strftime call.
-#today_fmt = '%B %d, %Y'
-
-# List of documents that shouldn't be included in the build.
-#unused_docs = []
-
-# List of directories, relative to source directory, that shouldn't be searched
-# for source files.
-exclude_trees = ['_build']
-
-# The reST default role (used for this markup: `text`) to use for all documents.
-#default_role = None
-
-# If true, '()' will be appended to :func: etc. cross-reference text.
-#add_function_parentheses = True
-
-# If true, the current module name will be prepended to all description
-# unit titles (such as .. function::).
-#add_module_names = True
-
-# If true, sectionauthor and moduleauthor directives will be shown in the
-# output. They are ignored by default.
-#show_authors = False
-
-# The name of the Pygments (syntax highlighting) style to use.
-pygments_style = 'sphinx'
-
-# A list of ignored prefixes for module index sorting.
-#modindex_common_prefix = []
-
-
-# -- Options for HTML output ---------------------------------------------------
-
-# The theme to use for HTML and HTML Help pages.  Major themes that come with
-# Sphinx are currently 'default' and 'sphinxdoc'.
-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 = {
-    "rightsidebar": True,
-}
-
-# Add any paths that contain custom themes here, relative to this directory.
-#html_theme_path = []
-
-# The name for this set of Sphinx documents.  If None, it defaults to
-# "<project> v<release> documentation".
-#html_title = None
-
-# A shorter title for the navigation bar.  Default is the same as html_title.
-#html_short_title = None
-
-# The name of an image file (relative to this directory) to place at the top
-# of the sidebar.
-#html_logo = None
-
-# The name of an image file (within the static path) to use as favicon of the
-# docs.  This file should be a Windows icon file (.ico) being 16x16 or 32x32
-# pixels large.
-#html_favicon = None
-
-# 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']
-
-# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
-# using the given strftime format.
-#html_last_updated_fmt = '%b %d, %Y'
-
-# If true, SmartyPants will be used to convert quotes and dashes to
-# typographically correct entities.
-#html_use_smartypants = True
-
-# Custom sidebar templates, maps document names to template names.
-#html_sidebars = {}
-
-# Additional templates that should be rendered to pages, maps page names to
-# template names.
-#html_additional_pages = {}
-
-# If false, no module index is generated.
-#html_use_modindex = True
-
-# If false, no index is generated.
-#html_use_index = True
-
-# If true, the index is split into individual pages for each letter.
-#html_split_index = False
-
-# If true, links to the reST sources are added to the pages.
-#html_show_sourcelink = True
-
-# If true, an OpenSearch description file will be output, and all pages will
-# contain a <link> tag referring to it.  The value of this option must be the
-# base URL from which the finished HTML is served.
-#html_use_opensearch = ''
-
-# If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml").
-#html_file_suffix = ''
-
-# Output file base name for HTML help builder.
-htmlhelp_basename = 'django-auth-ldapdoc'
-
-
-# -- Options for LaTeX output --------------------------------------------------
-
-# The paper size ('letter' or 'a4').
-#latex_paper_size = 'letter'
-
-# The font size ('10pt', '11pt' or '12pt').
-#latex_font_size = '10pt'
-
-# Grouping the document tree into LaTeX files. List of tuples
-# (source start file, target name, title, author, documentclass [howto/manual]).
-latex_documents = [
-  ('index', 'django-auth-ldap.tex', u'django-auth-ldap Documentation',
-   u'Peter Sagerson', 'manual'),
-]
-
-# The name of an image file (relative to this directory) to place at the top of
-# the title page.
-#latex_logo = None
-
-# For "manual" documents, if this is true, then toplevel headings are parts,
-# not chapters.
-#latex_use_parts = False
-
-# Additional stuff for the LaTeX preamble.
-#latex_preamble = ''
-
-# Documents to append as an appendix to all manuals.
-#latex_appendices = []
-
-# If false, no module index is generated.
-#latex_use_modindex = True

docs/ext/daldocs.py

+"""
+Extra stuff for the django-auth-ldap Sphinx docs.
+"""
+
+
+def setup(app):
+    app.add_crossref_type(
+        directivename="setting",
+        rolename="setting",
+        indextemplate="pair: %s; setting",
+    )

docs/index.rst

-================================
-Django authentication using LDAP
-================================
-
-This authentication backend enables a Django project to authenticate against any
-LDAP server. To use it, add :class:`django_auth_ldap.backend.LDAPBackend` to
-AUTHENTICATION_BACKENDS. It is not necessary to add `django_auth_ldap` to
-INSTALLED_APPLICATIONS unless you would like to run the unit tests. LDAP
-configuration can be as simple as a single distinguished name template, but
-there are many rich options for working with
-:class:`~django.contrib.auth.models.User` objects, groups, and permissions. This
-backend depends on the `python-ldap <http://www.python-ldap.org/>`_ module.
-
-.. 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',
-        )
-
-Version 1.1 of django-auth-ldap is tested with Django 1.3 and 1.4. Earlier
-versions may still work, but they are not supported.
-
-Configuring basic authentication
-================================
-
-If your LDAP server isn't running locally on the default port, you'll want to
-start by setting :ref:`AUTH_LDAP_SERVER_URI` to point to your server.
-
-.. code-block:: python
-
-    AUTH_LDAP_SERVER_URI = "ldap://ldap.example.com"
-
-That done, the first step is to authenticate a username and password against the
-LDAP service. There are two ways to do this, called search/bind and simply 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 :ref:`AUTH_LDAP_BIND_DN` to the distinguished name of
-an authorized user and :ref:`AUTH_LDAP_BIND_PASSWORD` to the password.
-
-(New in 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)"),
-    )
-
-To skip the search phase, set :ref:`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 previous 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"
-
-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.
-
-By default, all LDAP operations are performed with the :ref:`AUTH_LDAP_BIND_DN`
-and :ref:`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 would 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
-:ref:`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 :ref:`AUTH_LDAP_START_TLS` to ``True``::
-
-    AUTH_LDAP_START_TLS = True
-
-
-Working with 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.
-Three built-in subclasses cover most grouping mechanisms:
-
-    * :class:`~django_auth_ldap.config.PosixGroupType`
-    * :class:`~django_auth_ldap.config.MemberDNGroupType`
-    * :class:`~django_auth_ldap.config.NestedMemberDNGroupType`
-
-posixGroup objects are somewhat specialized, so they get their own class. 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`
-
-To get started, you'll need to provide some basic information about your LDAP
-groups. :ref:`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). :ref:`AUTH_LDAP_GROUP_TYPE` is an instance of the class
-corresponding to the type of group that will be returned by
-:ref:`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()
-
-The simplest use of groups is to limit the users who are allowed to log in. If
-:ref:`AUTH_LDAP_REQUIRE_GROUP` is set, then only users who are members of that
-group will successfully authenticate. :ref:`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"
-
-More advanced uses of groups are covered in the next two sections.
-
-
-User objects
-============
-
-Authenticating against an external source is swell, but Django's auth module is
-tightly bound to the :class:`django.contrib.auth.models.User` model. Thus, when
-a user logs in, we have to create a :class:`~django.contrib.auth.models.User`
-object to represent him in the database. Because the LDAP search is
-case-insenstive, 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_create_user` if you'd
-like to override this behavior.
-
-The only required field for a user is the username, which we obviously have. The
-:class:`~django.contrib.auth.models.User` model is 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'll 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:`~django.contrib.auth.models.User.ldap_username` attribute
-with the original (LDAP) username.
-:attr:`~django.contrib.auth.models.User.username` will, of course, be the Django
-username.
-
-LDAP directories tend to contain much more information about users that you may
-wish to propagate. A pair of settings, :ref:`AUTH_LDAP_USER_ATTR_MAP` and
-:ref:`AUTH_LDAP_PROFILE_ATTR_MAP`, serve to copy directory information into
-:class:`~django.contrib.auth.models.User` and profile objects. These are
-dictionaries that map user and profile model keys, respectively, to
-(case-insensitive) LDAP attribute names::
-
-    AUTH_LDAP_USER_ATTR_MAP = {"first_name": "givenName", "last_name": "sn"}
-    AUTH_LDAP_PROFILE_ATTR_MAP = {"home_directory": "homeDirectory"}
-
-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": "cn=staff,ou=groups,dc=example,dc=com",
-        "is_superuser": "cn=superuser,ou=groups,dc=example,dc=com"
-    }
-
-    AUTH_LDAP_PROFILE_FLAGS_BY_GROUP = {
-        "is_awesome": "cn=awesome,ou=django,ou=groups,dc=example,dc=com"
-    }
-
-By default, all mapped user fields will be updated each time the user logs in.
-To disable this, set :ref:`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 new
-:class:`~django.contrib.auth.models.User` or `None` if the user could not be
-found in LDAP.
-
-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
-      :ref:`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.
-
-If you would like to perform any additional population of user or profile
-objects, django_auth_ldap exposes two custom signals to help:
-:data:`~django_auth_ldap.backend.populate_user` and
-:data:`~django_auth_ldap.backend.populate_user_profile`. These are sent after
-the backend has finished populating the respective objects and before they are
-saved to the database. You can use this to propagate additional information from
-the LDAP directory to the user and profile objects any way you like.
-
-.. 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
-    he is 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.
-
-
-Permissions
-===========
-
-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.
-
-The least invasive way to map group permissions is to set
-:ref:`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
-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 :ref:`AUTH_LDAP_CACHE_GROUPS` to ``True``. You can also set
-:ref:`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
-
-The second way to turn LDAP group memberships into permissions is to mirror the
-groups themselves. If :ref:`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
-his LDAP group membership. Note that if the LDAP server has nested groups, the
-Django database will end up with a flattened representation.
-
-This approach has two main differences from :ref:`AUTH_LDAP_FIND_GROUP_PERMS`.
-First, :ref:`AUTH_LDAP_FIND_GROUP_PERMS` will query for LDAP group membership
-either for every request or according to the cache timeout. With group
-mirroring, membership will be updated when the user authenticates. This may not
-be appropriate for sites with long session timeouts. The second difference is
-that with :ref:`AUTH_LDAP_FIND_GROUP_PERMS`, there is no way for clients to
-determine a user's group memberships, only their permissions. If you want to
-make decisions based directly on group membership, you'll have to mirror the
-groups.
-
-: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 Django's RemoteUserBackend
-to map externally authenticated users to Django users. By setting
-:ref:`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 :ref:`AUTH_LDAP_MIRROR_GROUPS`; group mirroring is a
-feature of authentication, not authorization.
-
-
-Multiple LDAP Configs
-=====================
-
-(New in 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.
-
-
-Logging
-=======
-
-: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)
-
-More options
-============
-
-Miscellaneous settings for :class:`~django_auth_ldap.backend.LDAPBackend`:
-
-    * :ref:`AUTH_LDAP_GLOBAL_OPTIONS`: A dictionary of `options
-      <http://python-ldap.org/doc/html/ldap.html#options>`_ to pass to
-      python-ldap via ``ldap.set_option()``.
-    * :ref:`AUTH_LDAP_CONNECTION_OPTIONS`: A dictionary of `options
-      <http://python-ldap.org/doc/html/ldap.html#options>`_ to pass to each
-      LDAPObject instance via ``LDAPObject.set_option()``.
-
-
-Performance
-===========
-
-: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 :ref:`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
-       :ref:`AUTH_LDAP_CACHE_GROUPS` to ``True`` to remove most of this traffic.
-       Alternatively, you might consider using :ref:`AUTH_LDAP_MIRROR_GROUPS`
-       and relying on :class:`~django.contrib.auth.backends.ModelBackend` to
-       supply group permissions.
-    #. **Don't access user.ldap_user.***. These properties are only cached
-       on a per-request basis. If you can propagate LDAP attributes to a
-       :class:`~django.contrib.auth.models.User` or profile object, they will
-       only be updated at login. ``user.ldap_user.attrs`` triggers an LDAP
-       connection for every request in which it's accessed. If you're not using
-       :ref:`AUTH_LDAP_USER_DN_TEMPLATE`, then accessing ``user.ldap_user.dn``
-       will also trigger an LDAP connection.
-    #. **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
-       :ref:`AUTH_LDAP_USER_DN_TEMPLATE` is a little bit more efficient than
-       relying on :ref:`AUTH_LDAP_USER_SEARCH`. Specifically, it saves two LDAP
-       operations (one bind and one search) per login.
-
-
-Example 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_PROFILE_ATTR_MAP = {
-        "employee_number": "employeeNumber"
-    }
-
-    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"
-    }
-
-    AUTH_LDAP_PROFILE_FLAGS_BY_GROUP = {
-        "is_awesome": "cn=awesome,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',
-    )
-
-
-Reference
-=========
-
-Settings
---------
-
-.. _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.
-
-
-.. _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.
-
-
-.. _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.
-
-
-.. _AUTH_LDAP_BIND_DN:
-
-AUTH_LDAP_BIND_DN
-~~~~~~~~~~~~~~~~~
-
-Default: ``''`` (Empty string)
-
-The distinguished name to use when binding to the LDAP server (with
-:ref:`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 :ref:`AUTH_LDAP_USER_DN_TEMPLATE` is not set, we'll use
-this to search for the user. If :ref:`AUTH_LDAP_FIND_GROUP_PERMS` is ``True``,
-we'll also use it to determine group membership.
-
-
-.. _AUTH_LDAP_BIND_PASSWORD:
-
-AUTH_LDAP_BIND_PASSWORD
-~~~~~~~~~~~~~~~~~~~~~~~
-
-Default: ``''`` (Empty string)
-
-The password to use with :ref:`AUTH_LDAP_BIND_DN`.
-
-
-.. _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
-:ref:`AUTH_LDAP_GROUP_CACHE_TIMEOUT`.
-
-
-.. _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_*
-<http://python-ldap.org/doc/html/ldap.html#options>`_ constants.
-
-
-.. _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.
-
-
-.. _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.
-:ref:`AUTH_LDAP_GROUP_SEARCH` and :ref:`AUTH_LDAP_GROUP_TYPE` must also be set.
-
-
-.. _AUTH_LDAP_GLOBAL_OPTIONS:
-
-AUTH_LDAP_GLOBAL_OPTIONS
-~~~~~~~~~~~~~~~~~~~~~~~~
-
-Default: ``{}``
-
-A dictionary of options to pass to ``ldap.set_option()``. Keys are
-`ldap.OPT_* <http://python-ldap.org/doc/html/ldap.html#options>`_ constants.
-
-
-.. _AUTH_LDAP_GROUP_CACHE_TIMEOUT:
-
-AUTH_LDAP_GROUP_CACHE_TIMEOUT
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Default: ``None``
-
-If :ref:`AUTH_LDAP_CACHE_GROUPS` is ``True``, this is the cache timeout for
-group memberships. If ``None``, the global cache timeout will be used.
-
-
-.. _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 :ref:`AUTH_LDAP_GROUP_TYPE` must be set.
-
-
-.. _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 :ref:`AUTH_LDAP_GROUP_SEARCH`.
-
-
-.. _AUTH_LDAP_MIRROR_GROUPS:
-
-AUTH_LDAP_MIRROR_GROUPS
-~~~~~~~~~~~~~~~~~~~~~~~
-
-Default: ``False``
-
-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 his LDAP groups as Django groups and update his Django group
-membership to exactly match his LDAP group membership. If the LDAP server has
-nested groups, the Django database will end up with a flattened representation.
-
-
-.. _AUTH_LDAP_PROFILE_ATTR_MAP:
-
-AUTH_LDAP_PROFILE_ATTR_MAP
-~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Default: ``{}``
-
-A mapping from user profile field names to LDAP attribute names. A user's
-profile will be populated from his LDAP attributes at login.
-
-
-.. _AUTH_LDAP_PROFILE_FLAGS_BY_GROUP:
-
-AUTH_LDAP_PROFILE_FLAGS_BY_GROUP
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Default: ``{}``
-
-A mapping from boolean profile field names to distinguished names of LDAP
-groups. The corresponding field in a user's profile is set to ``True`` or
-``False`` according to whether the user is a member of the group.
-
-
-.. _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.
-
-
-.. _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.
-
-
-.. _AUTH_LDAP_START_TLS:
-
-AUTH_LDAP_START_TLS
-~~~~~~~~~~~~~~~~~~~
-
-Default: ``False``
-
-If ``True``, each connection to the LDAP server will call start_tls to enable
-TLS encryption over the standard LDAP port. There are a number of configuration
-options that can be given to :ref:`AUTH_LDAP_GLOBAL_OPTIONS` that affect the
-TLS connection. For example, ``ldap.OPT_X_TLS_REQUIRE_CERT`` can be set to
-``ldap.OPT_X_TLS_NEVER`` to disable certificate verification, perhaps to allow
-self-signed certificates.
-
-
-.. _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.
-
-
-.. _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``.
-
-
-.. _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.
-
-
-.. _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=*)')
-
-        * ``base_dn``: The distinguished name of the search base.
-        * ``scope``: One of ``ldap.SCOPE_*``.
-        * ``filterstr``: An optional filter string (e.g. '(objectClass=person)').
-          In order to be valid, ``filterstr`` must be enclosed in parentheses.
-
-.. class:: LDAPSearchUnion
-
-    (New in 1.1)
-
-    .. method:: __init__(\*searches)
-
-        * ``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.
-
-.. 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:: 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')
-
-        * ``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')
-
-
-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:: populate_user_profile
-
-    Like :data:`~django_auth_ldap.backend.populate_user`, but sent for the user
-    profile object. This will only be sent if the user has an existing profile.
-    As with :data:`~django_auth_ldap.backend.populate_user`, it is sent after the
-    backend has finished setting properties and before the object is saved. This
-    signal has two keyword arguments: ``profile`` is the user profile object and
-    ``ldap_user`` is the same as ``user.ldap_user``. The sender is the
-    :class:`~django_auth_ldap.backend.LDAPBackend` class.
-
-.. 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.
-
-    .. 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.
-        :ref:`AUTH_LDAP_ALWAYS_UPDATE_USER` is ignored (assumed ``True``).
-
-    .. method:: get_or_create_user(self, username, ldap_user)
-
-        Given a username and an LDAP user object, this must return the
-        associated Django User object. 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
-        ``User.objects.get_or_create()``: a (User, created) two-tuple.
-
-        The default implementation calls ``User.objects.get_or_create()``, using
-        a case-insensitive query and creating new users with lowercase
-        usernames. Subclasses are welcome to associate LDAP users to Django
-        users any way they like.
-
-    .. 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 overriden 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.
-
-
-License
-=======
-
-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.

docs/source/.spell.utf-8.add

+LDAP
+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

docs/source/.spell.utf-8.add.spl

Binary file added.

docs/source/authentication.rst

+Authentication
+==============
+
+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.
+
+.. code-block:: python
+
+    AUTH_LDAP_SERVER_URI = "ldap://ldap.example.com"
+
+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.
+
+(New in 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"
+
+
+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.
+
+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

docs/source/changes.rst

+Change Log
+==========
+
+.. include:: ../../CHANGES

docs/source/conf.py

+# -*- coding: utf-8 -*-
+#
+# django-auth-ldap documentation build configuration file, created by
+# sphinx-quickstart on Wed Sep 23 18:06:43 2009.
+#
+# 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.
+
+import sys
+import os.path
+
+# 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.
+sys.path.insert(0, os.path.abspath('../ext'))
+
+# -- General configuration -----------------------------------------------------
+
+# 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',
+]
+
+intersphinx_mapping = {
+    'python': ('http://docs.python.org/', None),
+    'django': ('https://docs.djangoproject.com/en/dev/',
+               'https://docs.djangoproject.com/en/dev/_objects/'),
+    'pythonldap': ('http://python-ldap.org/doc/html/', None),
+}
+
+# Add any paths that contain templates here, relative to this directory.
+templates_path = ['../_templates']
+
+# The suffix of source filenames.
+source_suffix = '.rst'
+
+# The encoding of source files.
+source_encoding = 'utf-8'
+
+# The master toctree document.
+master_doc = 'index'
+
+# General information about the project.
+project = u'django-auth-ldap'
+copyright = u'2009, 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.1'
+# The full version, including alpha/beta/rc tags.
+release = '1.1.2'
+
+# The language for content autogenerated by Sphinx. Refer to documentation
+# for a list of supported languages.
+#language = None
+
+# There are two options for replacing |today|: either, you set today to some
+# non-false value, then it is used:
+#today = ''
+# Else, today_fmt is used as the format for a strftime call.
+#today_fmt = '%B %d, %Y'
+
+# List of documents that shouldn't be included in the build.
+#unused_docs = []
+
+# List of directories, relative to source directory, that shouldn't be searched
+# for source files.
+exclude_trees = ['build']
+
+# The reST default role (used for this markup: `text`) to use for all documents.
+#default_role = None
+
+# If true, '()' will be appended to :func: etc. cross-reference text.
+#add_function_parentheses = True
+
+# If true, the current module name will be prepended to all description
+# unit titles (such as .. function::).
+#add_module_names = True
+
+# If true, sectionauthor and moduleauthor directives will be shown in the
+# output. They are ignored by default.
+#show_authors = False
+
+# The name of the Pygments (syntax highlighting) style to use.
+pygments_style = 'sphinx'
+
+# A list of ignored prefixes for module index sorting.
+#modindex_common_prefix = []
+
+
+# -- Options for HTML output ---------------------------------------------------
+
+# The theme to use for HTML and HTML Help pages.  Major themes that come with
+# Sphinx are currently 'default' and 'sphinxdoc'.
+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 = {
+    #"rightsidebar": True,
+}
+
+# Add any paths that contain custom themes here, relative to this directory.
+#html_theme_path = []
+
+# The name for this set of Sphinx documents.  If None, it defaults to
+# "<project> v<release> documentation".
+#html_title = None
+
+# A shorter title for the navigation bar.  Default is the same as html_title.
+#html_short_title = None
+
+# The name of an image file (relative to this directory) to place at the top
+# of the sidebar.
+#html_logo = None
+
+# The name of an image file (within the static path) to use as favicon of the
+# docs.  This file should be a Windows icon file (.ico) being 16x16 or 32x32
+# pixels large.
+#html_favicon = None
+
+# 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']
+
+# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
+# using the given strftime format.
+#html_last_updated_fmt = '%b %d, %Y'
+
+# If true, SmartyPants will be used to convert quotes and dashes to
+# typographically correct entities.
+#html_use_smartypants = True
+
+# Custom sidebar templates, maps document names to template names.
+html_sidebars = {
+    '*': ['globaltoc.html', 'relations.html', 'sourcelink.html', 'searchbox.html']
+}
+
+# Additional templates that should be rendered to pages, maps page names to
+# template names.
+#html_additional_pages = {}
+
+# If false, no module index is generated.
+#html_use_modindex = True
+
+# If false, no index is generated.
+#html_use_index = True
+
+# If true, the index is split into individual pages for each letter.
+#html_split_index = False
+
+# If true, links to the reST sources are added to the pages.
+#html_show_sourcelink = True
+
+# If true, an OpenSearch description file will be output, and all pages will
+# contain a <link> tag referring to it.  The value of this option must be the
+# base URL from which the finished HTML is served.
+#html_use_opensearch = ''
+
+# If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml").
+#html_file_suffix = ''
+
+# Output file base name for HTML help builder.
+htmlhelp_basename = 'django-auth-ldapdoc'
+
+
+# -- Options for LaTeX output --------------------------------------------------
+
+# The paper size ('letter' or 'a4').
+#latex_paper_size = 'letter'
+
+# The font size ('10pt', '11pt' or '12pt').
+#latex_font_size = '10pt'
+
+# Grouping the document tree into LaTeX files. List of tuples
+# (source start file, target name, title, author, documentclass [howto/manual]).
+latex_documents = [
+  ('index', 'django-auth-ldap.tex', u'django-auth-ldap Documentation',
+   u'Peter Sagerson', 'manual'),
+]
+
+# The name of an image file (relative to this directory) to place at the top of
+# the title page.
+#latex_logo = None
+
+# For "manual" documents, if this is true, then toplevel headings are parts,
+# not chapters.
+#latex_use_parts = False
+
+# Additional stuff for the LaTeX preamble.
+#latex_preamble = ''
+
+# Documents to append as an appendix to all manuals.
+#latex_appendices = []
+
+# If false, no module index is generated.
+#latex_use_modindex = True

docs/source/example.rst

+Example 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_PROFILE_ATTR_MAP = {
+        "employee_number": "employeeNumber"
+    }
+
+    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"
+    }
+
+    AUTH_LDAP_PROFILE_FLAGS_BY_GROUP = {
+        "is_awesome": "cn=awesome,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',
+    )

docs/source/groups.rst

+Working 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.
+Three built-in subclasses cover most grouping mechanisms:
+
+    * :class:`~django_auth_ldap.config.PosixGroupType`
+    * :class:`~django_auth_ldap.config.MemberDNGroupType`
+    * :class:`~django_auth_ldap.config.NestedMemberDNGroupType`
+
+posixGroup objects are somewhat specialized, so they get their own class. 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`
+
+
+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
+---------------
+
+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"
+
+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.

docs/source/index.rst