David Tulloh
2016-04-20 d7df42ae13a2a9bfb73a76ed96997dad88a794a9
commit | author | age
d95e97 1 .. _configuration_points:
TS 2
3 Configuring :mod:`repoze.who`
4 =============================
5
6 Configuration Points
7 --------------------
8
9 Classifiers
10 +++++++++++
11
12 :mod:`repoze.who` "classifies" the request on middleware ingress.
13 Request classification happens before identification and
14 authentication.  A request from a browser might be classified a
15 different way than a request from an XML-RPC client.
16 :mod:`repoze.who` uses request classifiers to decide which other
17 components to consult during subsequent identification,
18 authentication, and challenge steps.  Plugins are free to advertise
19 themselves as willing to participate in identification and
20 authorization for a request based on this classification.  The request
21 classification system is pluggable.  :mod:`repoze.who` provides a
22 default classifier that you may use.
23
24 You may extend the classification system by making :mod:`repoze.who` aware
25 of a different request classifier implementation.
26
27 Challenge Deciders
28 ++++++++++++++++++
29
30 :mod:`repoze.who` uses a "challenge decider" to decide whether the
31 response returned from a downstream application requires a challenge
32 plugin to fire.  When using the default challenge decider, only the
33 status is used (if it starts with ``401``, a challenge is required).
34
35 :mod:`repoze.who` also provides an alternate challenge decider,
36 ``repoze.who.classifiers.passthrough_challenge_decider``, which avoids
37 challenging ``401`` responses which have been "pre-challenged" by the
38 application.
39
40 You may supply a different challenge decider as necessary.
41
42 Plugins
43 +++++++
44
45 :mod:`repoze.who` has core functionality designed around the concept
46 of plugins.  Plugins are instances that are willing to perform one or
47 more identification- and/or authentication-related duties.  Each
48 plugin can be configured arbitrarily.
49
50 :mod:`repoze.who` consults the set of configured plugins when it
51 intercepts a WSGI request, and gives some subset of them a chance to
52 influence what :mod:`repoze.who` does for the current request.
53
54 .. note:: As of :mod:`repoze.who` 1.0.7, the ``repoze.who.plugins``
55    package is a namespace package, intended to make it possible for
56    people to ship eggs which are who plugins as,
57    e.g. ``repoze.who.plugins.mycoolplugin``.
58
59
60 .. _imperative_configuration:
61
62 Configuring :mod:`repoze.who` via Python Code
63 ---------------------------------------------
64
65 .. module:: repoze.who.middleware
66
e06889 67 .. class:: PluggableAuthenticationMiddleware(app, identifiers, challengers, authenticators, mdproviders, classifier, challenge_decider [, log_stream=None [, log_level=logging.INFO[, remote_user_key='REMOTE_USER']]])
d95e97 68
TS 69   The primary method of configuring the :mod:`repoze.who` middleware is
70   to use straight Python code, meant to be consumed by frameworks
71   which construct and compose middleware pipelines without using a
72   configuration file.
73
74   In the middleware constructor: *app* is the "next" application in
75   the WSGI pipeline. *identifiers* is a sequence of ``IIdentifier``
76   plugins, *challengers* is a sequence of ``IChallenger`` plugins,
77   *mdproviders* is a sequence of ``IMetadataProvider`` plugins.  Any
78   of these can be specified as the empty sequence.  *classifier* is a
79   request classifier callable, *challenge_decider* is a challenge
80   decision callable.  *log_stream* is a stream object (an object with
81   a ``write`` method) *or* a ``logging.Logger`` object, *log_level* is
82   a numeric value that maps to the ``logging`` module's notion of log
83   levels, *remote_user_key* is the key in which the ``REMOTE_USER``
84   (userid) value should be placed in the WSGI environment for
85   consumption by downstream applications.
86
87 An example configuration which uses the default plugins follows::
88
89     from repoze.who.middleware import PluggableAuthenticationMiddleware
90     from repoze.who.interfaces import IIdentifier
91     from repoze.who.interfaces import IChallenger
92     from repoze.who.plugins.basicauth import BasicAuthPlugin
93     from repoze.who.plugins.auth_tkt import AuthTktCookiePlugin
a446d6 94     from repoze.who.plugins.redirector import RedirectorPlugin
d95e97 95     from repoze.who.plugins.htpasswd import HTPasswdPlugin
TS 96
97     io = StringIO()
98     salt = 'aa'
99     for name, password in [ ('admin', 'admin'), ('chris', 'chris') ]:
100         io.write('%s:%s\n' % (name, password))
101     io.seek(0)
102     def cleartext_check(password, hashed):
103         return password == hashed
104     htpasswd = HTPasswdPlugin(io, cleartext_check)
105     basicauth = BasicAuthPlugin('repoze.who')
d7df42 106     auth_tkt = AuthTktCookiePlugin('secret', 'auth_tkt', digest_algo="sha512")
3ea820 107     redirector = RedirectorPlugin('/login.html')
a446d6 108     redirector.classifications = {IChallenger:['browser'],} # only for browser
TS 109     identifiers = [('auth_tkt', auth_tkt),
ac7dde 110                    ('basicauth', basicauth)]
TS 111     authenticators = [('auth_tkt', auth_tkt),
112                       ('htpasswd', htpasswd)]
a446d6 113     challengers = [('redirector', redirector),
ac7dde 114                    ('basicauth', basicauth)]
d95e97 115     mdproviders = []
TS 116
117     from repoze.who.classifiers import default_request_classifier
118     from repoze.who.classifiers import default_challenge_decider
119     log_stream = None
120     import os
121     if os.environ.get('WHO_LOG'):
122         log_stream = sys.stdout
123
124     middleware = PluggableAuthenticationMiddleware(
125         app,
126         identifiers,
127         authenticators,
128         challengers,
129         mdproviders,
130         default_request_classifier,
131         default_challenge_decider,
132         log_stream = log_stream,
133         log_level = logging.DEBUG
134         )
135
136 The above example configures the repoze.who middleware with:
137
a446d6 138 - Two ``IIdentifier`` plugins (auth_tkt cookie, and a
TS 139   basic auth plugin).  In this setup, when "identification" needs to
140   be performed, the auth_tkt plugin will be checked first, then
141   the basic auth plugin.  The application is responsible for handling
142   login via a form:  this view would use the API (via :method:`remember`)
143   to generate apprpriate response headers.
d95e97 144
ac7dde 145 - Two ``IAuthenticator`` plugins: the auth_tkt plugin and an htpasswd plugin.
TS 146   The auth_tkt plugin performs both ``IIdentifier`` and ``IAuthenticator``
147   functions.  The htpasswd plugin is configured with two valid username /
96f080 148   password combinations: chris/chris, and admin/admin.  When an username
ac7dde 149   and password is found via any identifier, it will be checked against this
d95e97 150   authenticator.
TS 151
a446d6 152 - Two ``IChallenger`` plugins: the redirector plugin, then the basic auth
TS 153   plugin.  The redirector auth will fire if the request is a ``browser``
d95e97 154   request, otherwise the basic auth plugin will fire.
TS 155
156 The rest of the middleware configuration is for values like logging
157 and the classifier and decider implementations.  These use the "stock"
158 implementations.
159
160 .. note:: The ``app`` referred to in the example is the "downstream"
161    WSGI application that who is wrapping.
162
163
164 .. _declarative_configuration:
165
166 Configuring :mod:`repoze.who` via Config File
167 ---------------------------------------------
168
169 :mod:`repoze.who` may be configured using a ConfigParser-style .INI
170 file.  The configuration file has five main types of sections: plugin
171 sections, a general section, an identifiers section, an authenticators
172 section, and a challengers section.  Each "plugin" section defines a
173 configuration for a particular plugin.  The identifiers,
174 authenticators, and challengers sections refer to these plugins to
175 form a site configuration.  The general section is general middleware
176 configuration.
177
178 To configure :mod:`repoze.who` in Python, using an .INI file, call
179 the `make_middleware_with_config` entry point, passing the right-hand
f80021 180 application, the global configuration dictionary, and the path to the
7b6211 181 config file. The global configuration dictionary is a dictonary passed 
RH 182 by PasteDeploy. The only key 'make_middleware_with_config' needs is 
183 'here' pointing to the config file directory. For debugging people
184 might find it useful to enable logging by adding the log_file argument,
185 e.g. log_file="repoze_who.log" ::
d95e97 186
TS 187     from repoze.who.config import make_middleware_with_config
7b6211 188     global_conf = {"here": "."}  # if this is not defined elsewhere
RH 189     who = make_middleware_with_config(app, global_conf, 'who.ini')
d95e97 190
TS 191 :mod:`repoze.who`'s configuration file can be pointed to within a PasteDeploy
192 configuration file ::
193
194     [filter:who]
195     use = egg:repoze.who#config
196     config_file = %(here)s/who.ini
197     log_file = stdout
198     log_level = debug
199
200 Below is an example of a configuration file (what ``config_file``
201 might point at above ) that might be used to configure the
202 :mod:`repoze.who` middleware.  A set of plugins are defined, and they
203 are referred to by following non-plugin sections.
204
205 In the below configuration, five plugins are defined.  The form, and
206 basicauth plugins are nominated to act as challenger plugins.  The
207 form, cookie, and basicauth plugins are nominated to act as
208 identification plugins.  The htpasswd and sqlusers plugins are
209 nominated to act as authenticator plugins. ::
210
a446d6 211     [plugin:redirector]
d95e97 212     # identificaion and challenge
a446d6 213     use = repoze.who.plugins.redirector:make_plugin
TS 214     login_url = /login.html
d95e97 215
TS 216     [plugin:auth_tkt]
ac7dde 217     # identification and authentication
d95e97 218     use = repoze.who.plugins.auth_tkt:make_plugin
TS 219     secret = s33kr1t
220     cookie_name = oatmeal
221     secure = False
222     include_ip = False
d7df42 223     digest_algo = sha512
d95e97 224
TS 225     [plugin:basicauth]
226     # identification and challenge
227     use = repoze.who.plugins.basicauth:make_plugin
228     realm = 'sample'
229
230     [plugin:htpasswd]
231     # authentication
232     use = repoze.who.plugins.htpasswd:make_plugin
233     filename = %(here)s/passwd
234     check_fn = repoze.who.plugins.htpasswd:crypt_check
235
236     [plugin:sqlusers]
237     # authentication
238     use = repoze.who.plugins.sql:make_authenticator_plugin
1d7c19 239     # Note the double %%:  we have to escape it from the config parser in
TS 240     # order to preserve it as a template for the psycopg2, whose 'paramstyle'
241     # is 'pyformat'.
242     query = SELECT userid, password FROM users where login = %%(login)s
d95e97 243     conn_factory = repoze.who.plugins.sql:make_psycopg_conn_factory
TS 244     compare_fn = repoze.who.plugins.sql:default_password_compare
245
246     [plugin:sqlproperties]
247     name = properties
248     use = repoze.who.plugins.sql:make_metadata_plugin
1d7c19 249     # Note the double %%:  we have to escape it from the config parser in
TS 250     # order to preserve it as a template for the psycopg2, whose 'paramstyle'
251     # is 'pyformat'.
252     query = SELECT firstname, lastname FROM users where userid = %%(__userid)s
d95e97 253     filter = my.package:filter_propmd
TS 254     conn_factory = repoze.who.plugins.sql:make_psycopg_conn_factory
255
256     [general]
257     request_classifier = repoze.who.classifiers:default_request_classifier
258     challenge_decider = repoze.who.classifiers:default_challenge_decider
259     remote_user_key = REMOTE_USER
260
261     [identifiers]
262     # plugin_name;classifier_name:.. or just plugin_name (good for any)
263     plugins =
264           auth_tkt
265           basicauth
266
267     [authenticators]
268     # plugin_name;classifier_name.. or just plugin_name (good for any)
269     plugins =
ac7dde 270           auth_tkt
d95e97 271           htpasswd
TS 272           sqlusers
273
274     [challengers]
275     # plugin_name;classifier_name:.. or just plugin_name (good for any)
276     plugins =
a446d6 277           redirector;browser
d95e97 278           basicauth
TS 279
280     [mdproviders]
281     plugins =
282           sqlproperties
283
284 The basicauth section configures a plugin that does identification and
a446d6 285 challenge for basic auth credentials.  The redirector section configures a
TS 286 plugin that does challenges.  The auth_tkt section configures a plugin that
287 does identification for cookie auth credentials, as well as authenticating
ac7dde 288 them.  The htpasswd plugin obtains its user info from a file.  The sqlusers
TS 289 plugin obtains its user info from a Postgres database.
d95e97 290
TS 291 The identifiers section provides an ordered list of plugins that are
292 willing to provide identification capability.  These will be consulted
293 in the defined order.  The tokens on each line of the ``plugins=`` key
17c518 294 are in the form "plugin_name;requestclassifier_name:..."  (or just
d95e97 295 "plugin_name" if the plugin can be consulted regardless of the
TS 296 classification of the request).  The configuration above indicates
a446d6 297 that the system will look for credentials using the auth_tkt cookie
d95e97 298 identifier (unconditionally), then the basic auth plugin
TS 299 (unconditionally).
300
301 The authenticators section provides an ordered list of plugins that
302 provide authenticator capability.  These will be consulted in the
303 defined order, so the system will look for users in the file, then in
304 the sql database when attempting to validate credentials.  No
305 classification prefixes are given to restrict which of the two plugins
306 are used, so both plugins are consulted regardless of the
307 classification of the request.  Each authenticator is called with each
308 set of identities found by the identifier plugins.  The first identity
309 that can be authenticated is used to set ``REMOTE_USER``.
310
311 The mdproviders section provides an ordered list of plugins that
312 provide metadata provider capability.  These will be consulted in the
313 defined order.  Each will have a chance (on ingress) to provide add
314 metadata to the authenticated identity.  Our example mdproviders
315 section shows one plugin configured: "sqlproperties".  The
316 sqlproperties plugin will add information related to user properties
317 (e.g. first name and last name) to the identity dictionary.
318
319 The challengers section provides an ordered list of plugins that
320 provide challenger capability.  These will be consulted in the defined
321 order, so the system will consult the cookie auth plugin first, then
322 the basic auth plugin.  Each will have a chance to initiate a
a446d6 323 challenge.  The above configuration indicates that the redirector challenger
d95e97 324 will fire if it's a browser request, and the basic auth challenger
TS 325 will fire if it's not (fallback).