| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325 |
- .. _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).
|