.. _configuration_points:
|
|
Configuring :mod:`repoze.who`
|
=============================
|
|
Configuration Points
|
--------------------
|
|
Classifiers
|
+++++++++++
|
|
:mod:`repoze.who` "classifies" the request on middleware ingress.
|
Request classification happens before identification and
|
authentication. A request from a browser might be classified a
|
different way than a request from an XML-RPC client.
|
:mod:`repoze.who` uses request classifiers to decide which other
|
components to consult during subsequent identification,
|
authentication, and challenge steps. Plugins are free to advertise
|
themselves as willing to participate in identification and
|
authorization for a request based on this classification. The request
|
classification system is pluggable. :mod:`repoze.who` provides a
|
default classifier that you may use.
|
|
You may extend the classification system by making :mod:`repoze.who` aware
|
of a different request classifier implementation.
|
|
Challenge Deciders
|
++++++++++++++++++
|
|
:mod:`repoze.who` uses a "challenge decider" to decide whether the
|
response returned from a downstream application requires a challenge
|
plugin to fire. When using the default challenge decider, only the
|
status is used (if it starts with ``401``, a challenge is required).
|
|
:mod:`repoze.who` also provides an alternate challenge decider,
|
``repoze.who.classifiers.passthrough_challenge_decider``, which avoids
|
challenging ``401`` responses which have been "pre-challenged" by the
|
application.
|
|
You may supply a different challenge decider as necessary.
|
|
Plugins
|
+++++++
|
|
:mod:`repoze.who` has core functionality designed around the concept
|
of plugins. Plugins are instances that are willing to perform one or
|
more identification- and/or authentication-related duties. Each
|
plugin can be configured arbitrarily.
|
|
:mod:`repoze.who` consults the set of configured plugins when it
|
intercepts a WSGI request, and gives some subset of them a chance to
|
influence what :mod:`repoze.who` does for the current request.
|
|
.. note:: As of :mod:`repoze.who` 1.0.7, the ``repoze.who.plugins``
|
package is a namespace package, intended to make it possible for
|
people to ship eggs which are who plugins as,
|
e.g. ``repoze.who.plugins.mycoolplugin``.
|
|
|
.. _imperative_configuration:
|
|
Configuring :mod:`repoze.who` via Python Code
|
---------------------------------------------
|
|
.. module:: repoze.who.middleware
|
|
.. class:: PluggableAuthenticationMiddleware(app, identifiers, challengers, authenticators, mdproviders, classifier, challenge_decider [, log_stream=None [, log_level=logging.INFO[, remote_user_key='REMOTE_USER']]])
|
|
The primary method of configuring the :mod:`repoze.who` middleware is
|
to use straight Python code, meant to be consumed by frameworks
|
which construct and compose middleware pipelines without using a
|
configuration file.
|
|
In the middleware constructor: *app* is the "next" application in
|
the WSGI pipeline. *identifiers* is a sequence of ``IIdentifier``
|
plugins, *challengers* is a sequence of ``IChallenger`` plugins,
|
*mdproviders* is a sequence of ``IMetadataProvider`` plugins. Any
|
of these can be specified as the empty sequence. *classifier* is a
|
request classifier callable, *challenge_decider* is a challenge
|
decision callable. *log_stream* is a stream object (an object with
|
a ``write`` method) *or* a ``logging.Logger`` object, *log_level* is
|
a numeric value that maps to the ``logging`` module's notion of log
|
levels, *remote_user_key* is the key in which the ``REMOTE_USER``
|
(userid) value should be placed in the WSGI environment for
|
consumption by downstream applications.
|
|
An example configuration which uses the default plugins follows::
|
|
from repoze.who.middleware import PluggableAuthenticationMiddleware
|
from repoze.who.interfaces import IIdentifier
|
from repoze.who.interfaces import IChallenger
|
from repoze.who.plugins.basicauth import BasicAuthPlugin
|
from repoze.who.plugins.auth_tkt import AuthTktCookiePlugin
|
from repoze.who.plugins.redirector import RedirectorPlugin
|
from repoze.who.plugins.htpasswd import HTPasswdPlugin
|
|
io = StringIO()
|
salt = 'aa'
|
for name, password in [ ('admin', 'admin'), ('chris', 'chris') ]:
|
io.write('%s:%s\n' % (name, password))
|
io.seek(0)
|
def cleartext_check(password, hashed):
|
return password == hashed
|
htpasswd = HTPasswdPlugin(io, cleartext_check)
|
basicauth = BasicAuthPlugin('repoze.who')
|
auth_tkt = AuthTktCookiePlugin('secret', 'auth_tkt', digest_algo="sha512")
|
redirector = RedirectorPlugin('/login.html')
|
redirector.classifications = {IChallenger:['browser'],} # only for browser
|
identifiers = [('auth_tkt', auth_tkt),
|
('basicauth', basicauth)]
|
authenticators = [('auth_tkt', auth_tkt),
|
('htpasswd', htpasswd)]
|
challengers = [('redirector', redirector),
|
('basicauth', basicauth)]
|
mdproviders = []
|
|
from repoze.who.classifiers import default_request_classifier
|
from repoze.who.classifiers import default_challenge_decider
|
log_stream = None
|
import os
|
if os.environ.get('WHO_LOG'):
|
log_stream = sys.stdout
|
|
middleware = PluggableAuthenticationMiddleware(
|
app,
|
identifiers,
|
authenticators,
|
challengers,
|
mdproviders,
|
default_request_classifier,
|
default_challenge_decider,
|
log_stream = log_stream,
|
log_level = logging.DEBUG
|
)
|
|
The above example configures the repoze.who middleware with:
|
|
- Two ``IIdentifier`` plugins (auth_tkt cookie, and a
|
basic auth plugin). In this setup, when "identification" needs to
|
be performed, the auth_tkt plugin will be checked first, then
|
the basic auth plugin. The application is responsible for handling
|
login via a form: this view would use the API (via :method:`remember`)
|
to generate apprpriate response headers.
|
|
- Two ``IAuthenticator`` plugins: the auth_tkt plugin and an htpasswd plugin.
|
The auth_tkt plugin performs both ``IIdentifier`` and ``IAuthenticator``
|
functions. The htpasswd plugin is configured with two valid username /
|
password combinations: chris/chris, and admin/admin. When an username
|
and password is found via any identifier, it will be checked against this
|
authenticator.
|
|
- Two ``IChallenger`` plugins: the redirector plugin, then the basic auth
|
plugin. The redirector auth will fire if the request is a ``browser``
|
request, otherwise the basic auth plugin will fire.
|
|
The rest of the middleware configuration is for values like logging
|
and the classifier and decider implementations. These use the "stock"
|
implementations.
|
|
.. note:: The ``app`` referred to in the example is the "downstream"
|
WSGI application that who is wrapping.
|
|
|
.. _declarative_configuration:
|
|
Configuring :mod:`repoze.who` via Config File
|
---------------------------------------------
|
|
:mod:`repoze.who` may be configured using a ConfigParser-style .INI
|
file. The configuration file has five main types of sections: plugin
|
sections, a general section, an identifiers section, an authenticators
|
section, and a challengers section. Each "plugin" section defines a
|
configuration for a particular plugin. The identifiers,
|
authenticators, and challengers sections refer to these plugins to
|
form a site configuration. The general section is general middleware
|
configuration.
|
|
To configure :mod:`repoze.who` in Python, using an .INI file, call
|
the `make_middleware_with_config` entry point, passing the right-hand
|
application, the global configuration dictionary, and the path to the
|
config file. The global configuration dictionary is a dictonary passed
|
by PasteDeploy. The only key 'make_middleware_with_config' needs is
|
'here' pointing to the config file directory. For debugging people
|
might find it useful to enable logging by adding the log_file argument,
|
e.g. log_file="repoze_who.log" ::
|
|
from repoze.who.config import make_middleware_with_config
|
global_conf = {"here": "."} # if this is not defined elsewhere
|
who = make_middleware_with_config(app, global_conf, 'who.ini')
|
|
:mod:`repoze.who`'s configuration file can be pointed to within a PasteDeploy
|
configuration file ::
|
|
[filter:who]
|
use = egg:repoze.who#config
|
config_file = %(here)s/who.ini
|
log_file = stdout
|
log_level = debug
|
|
Below is an example of a configuration file (what ``config_file``
|
might point at above ) that might be used to configure the
|
:mod:`repoze.who` middleware. A set of plugins are defined, and they
|
are referred to by following non-plugin sections.
|
|
In the below configuration, five plugins are defined. The form, and
|
basicauth plugins are nominated to act as challenger plugins. The
|
form, cookie, and basicauth plugins are nominated to act as
|
identification plugins. The htpasswd and sqlusers plugins are
|
nominated to act as authenticator plugins. ::
|
|
[plugin:redirector]
|
# identificaion and challenge
|
use = repoze.who.plugins.redirector:make_plugin
|
login_url = /login.html
|
|
[plugin:auth_tkt]
|
# identification and authentication
|
use = repoze.who.plugins.auth_tkt:make_plugin
|
secret = s33kr1t
|
cookie_name = oatmeal
|
secure = False
|
include_ip = False
|
digest_algo = sha512
|
|
[plugin:basicauth]
|
# identification and challenge
|
use = repoze.who.plugins.basicauth:make_plugin
|
realm = 'sample'
|
|
[plugin:htpasswd]
|
# authentication
|
use = repoze.who.plugins.htpasswd:make_plugin
|
filename = %(here)s/passwd
|
check_fn = repoze.who.plugins.htpasswd:crypt_check
|
|
[plugin:sqlusers]
|
# authentication
|
use = repoze.who.plugins.sql:make_authenticator_plugin
|
# Note the double %%: we have to escape it from the config parser in
|
# order to preserve it as a template for the psycopg2, whose 'paramstyle'
|
# is 'pyformat'.
|
query = SELECT userid, password FROM users where login = %%(login)s
|
conn_factory = repoze.who.plugins.sql:make_psycopg_conn_factory
|
compare_fn = repoze.who.plugins.sql:default_password_compare
|
|
[plugin:sqlproperties]
|
name = properties
|
use = repoze.who.plugins.sql:make_metadata_plugin
|
# Note the double %%: we have to escape it from the config parser in
|
# order to preserve it as a template for the psycopg2, whose 'paramstyle'
|
# is 'pyformat'.
|
query = SELECT firstname, lastname FROM users where userid = %%(__userid)s
|
filter = my.package:filter_propmd
|
conn_factory = repoze.who.plugins.sql:make_psycopg_conn_factory
|
|
[general]
|
request_classifier = repoze.who.classifiers:default_request_classifier
|
challenge_decider = repoze.who.classifiers:default_challenge_decider
|
remote_user_key = REMOTE_USER
|
|
[identifiers]
|
# plugin_name;classifier_name:.. or just plugin_name (good for any)
|
plugins =
|
auth_tkt
|
basicauth
|
|
[authenticators]
|
# plugin_name;classifier_name.. or just plugin_name (good for any)
|
plugins =
|
auth_tkt
|
htpasswd
|
sqlusers
|
|
[challengers]
|
# plugin_name;classifier_name:.. or just plugin_name (good for any)
|
plugins =
|
redirector;browser
|
basicauth
|
|
[mdproviders]
|
plugins =
|
sqlproperties
|
|
The basicauth section configures a plugin that does identification and
|
challenge for basic auth credentials. The redirector section configures a
|
plugin that does challenges. The auth_tkt section configures a plugin that
|
does identification for cookie auth credentials, as well as authenticating
|
them. The htpasswd plugin obtains its user info from a file. The sqlusers
|
plugin obtains its user info from a Postgres database.
|
|
The identifiers section provides an ordered list of plugins that are
|
willing to provide identification capability. These will be consulted
|
in the defined order. The tokens on each line of the ``plugins=`` key
|
are in the form "plugin_name;requestclassifier_name:..." (or just
|
"plugin_name" if the plugin can be consulted regardless of the
|
classification of the request). The configuration above indicates
|
that the system will look for credentials using the auth_tkt cookie
|
identifier (unconditionally), then the basic auth plugin
|
(unconditionally).
|
|
The authenticators section provides an ordered list of plugins that
|
provide authenticator capability. These will be consulted in the
|
defined order, so the system will look for users in the file, then in
|
the sql database when attempting to validate credentials. No
|
classification prefixes are given to restrict which of the two plugins
|
are used, so both plugins are consulted regardless of the
|
classification of the request. Each authenticator is called with each
|
set of identities found by the identifier plugins. The first identity
|
that can be authenticated is used to set ``REMOTE_USER``.
|
|
The mdproviders section provides an ordered list of plugins that
|
provide metadata provider capability. These will be consulted in the
|
defined order. Each will have a chance (on ingress) to provide add
|
metadata to the authenticated identity. Our example mdproviders
|
section shows one plugin configured: "sqlproperties". The
|
sqlproperties plugin will add information related to user properties
|
(e.g. first name and last name) to the identity dictionary.
|
|
The challengers section provides an ordered list of plugins that
|
provide challenger capability. These will be consulted in the defined
|
order, so the system will consult the cookie auth plugin first, then
|
the basic auth plugin. Each will have a chance to initiate a
|
challenge. The above configuration indicates that the redirector challenger
|
will fire if it's a browser request, and the basic auth challenger
|
will fire if it's not (fallback).
|