1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325
|
.. _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).
|