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