==========================================================
Using the repoze.who LDAP plugins in your WSGI Application
==========================================================

Implementing authentication via `repoze.who` is a simple task that requires
few lines of code. So using its LDAP plugins should not be an exception: You
just have to configure `repoze.who` in your application and then add the
`repoze.who.plugins.ldap` plugin(s) you want to use in your application.


Setting up `repoze.who` with the LDAP authenticator
===================================================

This section explains how to setup `repoze.who` in order to use the LDAP plugins
in your WSGI application. It is based on `the documentation for repoze.who
<http://static.repoze.org/whodocs/>`_.

You can configure your authentication mechanism powered by `repoze.who` with
two methods: With an INI file or with Python code.

In the examples below we are only going to use the main plugin provided by this
package: The LDAP authenticator itself (:class:`LDAPAuthenticatorPlugin`). The
other plugins don't deal with authentication, but are useful to load automatically 
data related to the authenticated user from the LDAP server.

Using the `repoze.who` terminology, :class:`LDAPAuthenticatorPlugin` is an
`authenticator plugin` and the others are `metadata provider plugins`.


Configuring `repoze.who` in a INI file
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

You can configure your `repoze.who` based authentication via a `*.ini` file,
and then load such settings in your application.

Say we have a file called `who.ini` with the following contents::

    # These contents have been adapted from:
    # http://static.repoze.org/whodocs/#middleware-configuration-via-config-file
    [plugin:form]
    use = repoze.who.plugins.form:make_plugin
    rememberer_name = auth_tkt

    [plugin:auth_tkt]
    use = repoze.who.plugins.auth_tkt:make_plugin
    secret = something

    [plugin:ldap_auth]
    use = repoze.who.plugins.ldap:LDAPAuthenticatorPlugin
    ldap_connection = ldap://ldap.yourcompany.com
    base_dn = ou=developers,dc=yourcompany,dc=com

    [general]
    request_classifier = repoze.who.classifiers:default_request_classifier
    challenge_decider = repoze.who.classifiers:default_challenge_decider

    [identifiers]
    plugins =
        form;browser
        auth_tkt

    [authenticators]
    plugins =
            ldap_auth

    [challengers]
    plugins =
        form;browser


With the settings above, authentication via `repoze.who` is configured this way:
Visitors will login with a form, providing their user name and password; then
these credentials will be checked against the LDAP server `ldap.yourcompany.com`
under `ou=developers,dc=yourcompany,dc=com`. This form will be displayed
when your WSGI application issues an HTTP *401* error.

For example, if an user enters `jsmith` as the user name and `valencia` as their
password, the LDAP authenticator will build their Distinguished Name (DN) as
`uid=jsmith,ou=developers,dc=yourcompany,dc=com` and will try to
authenticate them in the `ldap.yourcompany.com` LDAP server with this DN and
`valencia` as the password.

You may modify the way the DN is generated by subclassing
:class:`LDAPAuthenticatorPlugin` to override the `_get_dn` method.

Finally, you can load these settings by adding the `repoze.who` middleware to your
application::

    from repoze.who.config import make_middleware_with_config
    app_with_auth = make_middleware_with_config(app, '/path/to/who.ini')

In the documentation for `repoze.who` there is `a more detailed explanation
<http://static.repoze.org/whodocs/#middleware-configuration-via-config-file>`_
for the INI file method. 


Configuring `repoze.who` with Python code
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

The Python code below does the same as the INI file above::

    # This script has been adapted from
    # http://static.repoze.org/whodocs/#module-repoze.who.middleware
    
    # Importing the plugins to be used
    from repoze.who.interfaces import IIdentifier, IChallenger
    from repoze.who.plugins.auth_tkt import AuthTktCookiePlugin
    from repoze.who.plugins.form import FormPlugin
    from repoze.who.plugins.ldap import LDAPAuthenticatorPlugin

    # Configuring the plugins
    ldap_auth = LDAPAuthenticatorPlugin('ldap://ldap.yourcompany.com',
                                        'ou=developers,dc=yourcompany,dc=com')
    auth_tkt = AuthTktCookiePlugin('secret', 'auth_tkt')
    form = FormPlugin('__do_login', rememberer_name='auth_tkt')
    form.classifications = { IIdentifier: ['browser'],
                             IChallenger: ['browser'] } # only for browser
    identifiers = [('form', form),('auth_tkt',auth_tkt)]
    authenticators = [('ldap_auth', ldap_auth)]
    challengers = [('form',form)]
    mdproviders = []

    # Using the default repoze.who classifiers:
    from repoze.who.classifiers import default_request_classifier, \
                                       default_challenge_decider
    log_stream = None
    import os
    if os.environ.get('WHO_LOG'):
        log_stream = sys.stdout


Then you can get these settings applied by adding the `repoze.who` middleware
to your application::

    app_with_auth = PluggableAuthenticationMiddleware(
        app,
        identifiers,
        authenticators,
        challengers,
        mdproviders,
        default_request_classifier,
        default_challenge_decider,
        log_stream = log_stream,
        log_level = logging.DEBUG
        )

In the documentation for `repoze.who` there is `a detailed explanation
<http://static.repoze.org/whodocs/#module-repoze.who.middleware>`_ for this 
method. 


Framework-specific documentation
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

You may want to check the following framework-specific documents to learn tips
on how to implement `repoze.who` in the framework you are using:

 * **Pylons:** `Authentication and Authorization with repoze.who
   <http://wiki.pylonshq.com/display/pylonscookbook/Authentication+and+Authorization+with+%60repoze.who%60>`_.
 * TurboGears.


Using the LDAP plugins for repoze.who
=====================================

Once you've setup `repoze.who`, you'll be ready to use its LDAP plugins. Below
you will find how to use them in your application.

.. module:: repoze.who.plugins.ldap

.. class:: LDAPAuthenticatorPlugin(ldap_connection, base_dn)

    This is the main plugin. It's in charge of the LDAP authentication itself
    using the LDAP connection object provided in the constructor
    (**ldap_connection**) — which can be an LDAP URL or an
    `ldap.ldapobject.SimpleLDAPObject` instance.
    
    It connects to the specified LDAP server and tries to `bind` with the
    `Distinguished Name` (DN) made by joining the `login` in the `identity`
    dictionary as the user id (`uid`) and the **base_dn** specified in the
    constructor, and then it binds with the `password` found in the `identity`
    dictionary.
    
    For example, if the `login` provided by the identifier is `carla` and the
    **base_dn** provided in the constructor is `ou=employees,dc=example,dc=org`,
    the resulting DN will be `uid=carla,ou=employees,dc=example,dc=org`.

    Therefore this plugin is compatible with any `identifier plugin` that 
    defines the `login` and `password` items in the `identity` dictionary (the
    `identifier plugins` provided by the built-in `repoze.who.plugins.form`
    plugin are some of them).

    It is a highly customizable plugin which can be adapted to your needs with
    no hassle. You could also include in the login form a `select` field for
    people to select the department they belong to, being the key of such
    departments the `Organizational Unit` in the LDAP server; then, in the
    **_get_dn** method you would get such value from the WSGI environment
    object (**environ**).
    
    You may change the way the DN is created by subclassing
    :class:`LDAPAuthenticatorPlugin` to override the *_get_dn* method. For
    example, say in your company (with `dc=yourcompany,dc.com` as its DN)
    everybody belongs to the `Organizational Unit` (OU) **employees**
    (`ou=employees`), except the shareholders who belong to the OU
    **shareholders** (`ou=shareholders`)::
    
        class YourCompanyLDAPAuthenticatorPlugin(LDAPAuthenticatorPlugin):
            """Sample LDAP authenticator adapted to your company."""
            
            shareholders = ('lgarcia, 'mferreira', 'cnarea')
            """Set of shareholders of the company"""
            
            def _get_dn(self, environ, identity):
                try:
                    if identity['login'] in self.shareholders:
                        ou = 'shareholders'
                    else:
                        ou = 'employees'
                    return u'uid=%s,ou=%s,%s' % (identity['login'], ou,
                                                 self.base_dn)
                except (KeyError, TypeError):
                    raise ValueError, ('Could not find the DN from the identity '
                                       'and environment')
    
    It is possibly an useless example on how to customize the way the DN is
    found, but it's enough to show how to override it.
        
    To configure this plugin from an INI file, you'd have to include a section 
    like this::
    
        [plugin:ldap_auth]
        use = repoze.who.plugins.ldap:LDAPAuthenticatorPlugin
        ldap_connection = ldap://yourcompany.com
        base_dn = ou=employees,dc=yourcompany,dc=com
    
    If you're using a custom LDAP authenticator, as in the example above, you
    would have to change the `use` directive accordingly — for example::
    
        use = yourpackage.lib.auth:YourCompanyLDAPAuthenticatorPlugin
    
    Finally, add the plugin to the set of authenticators::
    
        [authenticators]
        plugins =
                ldap_auth
    
    But if you're configuring `repoze.who` via Python code, you can use the code 
    below::
    
        ldap_auth = LDAPAuthenticatorPlugin('ldap://ldap.yourcompany.com',
                                            'ou=developers,dc=yourcompany,dc=com')
    
    But if you're using a custom LDAP authenticator, you would have to use the
    code below instead::
    
        ldap_auth = YourCompanyLDAPAuthenticatorPlugin('ldap://ldap.yourcompany.com',
                                                       'ou=employees,dc=yourcompany,dc=com')
    
    Finally, add this authenticator to the set of authenticators::
    
        authenticators = [('ldap_auth', ldap_auth)]
    
    As in the example above.


.. class:: LDAPAttributesPlugin(ldap_connection[, attributes=None[, filterstr='(objectClass=*)']])

    This plugin enables you to load data for the authenticated user 
    automatically and have it available from the WSGI environment — in the
    `identity` dictionary, specifically.
    
    **ldap_connection** represents the connection to the LDAP server, which,
    as in :class:`LDAPAuthenticatorPlugin`, can be either an LDAP URL or an
    instance of `ldap.ldapobject.SimpleLDAPObject`. **attributes** represents
    the list of user's attributes that you would like to fetch from the LDAP
    server; it can be an iterable, an string where the attributes are separated
    by commas, or *None* to fetch all the available attributes.
    
    By default it loads the attributes available for *any* entry whose *DN* is
    the same as the one found by :class:`LDAPAuthenticatorPlugin`, which is
    desired in most situations. However, if you would like to exclude some
    entries, you may setup a filter by means of the **filterstr** parameter,
    which is an string whose format is defined by `RFC 4515 - Lightweight 
    Directory Access Protocol (LDAP): String Representation of Search Filters
    <http://www.faqs.org/rfcs/rfc4515.html>`_.

    There is no advanced usage for this plugin, and hopefully you would never 
    need to subclass it to suit your needs.
    
    To configure this plugin from an INI file, you'd have to include a section 
    like this::
    
        [plugin:ldap_attributes]
        use = repoze.who.plugins.ldap:LDAPAttributesPlugin
        ldap_connection = ldap://ldap.yourcompany.com
        attributes = cn,sn,mail
    
    If instead of loading the *Common Name*, *surname* and *email*, as with the
    settings above, you'd prefer to load all the available attributes for the
    authenticated user, you'd just have to remove the *attributes* directive.
    
    Finally, add the plugin to the set of metadata providers::
    
        [mdproviders]
        plugins =
                ldap_attributes
    
    But if you want to configure it via Python code, you can use the code 
    below::
    
        ldap_attributes = LDAPAttributesPlugin('ldap://ldap.yourcompany.com',
                                               ['cn', 'sn', 'email'])
    
    Again, if you would prefer to load all the available attributes for the 
    user, you just have to remove the second parameter.
    
    Finally, add this authenticator to the set of metadata providers in your 
    Python code::
    
        mdproviders = [('ldap_attributes', ldap_attributes)]

