1.. _configuration_points: 2 3Configuring :mod:`repoze.who` 4============================= 5 6Configuration Points 7-------------------- 8 9Classifiers 10+++++++++++ 11 12:mod:`repoze.who` "classifies" the request on middleware ingress. 13Request classification happens before identification and 14authentication. A request from a browser might be classified a 15different way than a request from an XML-RPC client. 16:mod:`repoze.who` uses request classifiers to decide which other 17components to consult during subsequent identification, 18authentication, and challenge steps. Plugins are free to advertise 19themselves as willing to participate in identification and 20authorization for a request based on this classification. The request 21classification system is pluggable. :mod:`repoze.who` provides a 22default classifier that you may use. 23 24You may extend the classification system by making :mod:`repoze.who` aware 25of a different request classifier implementation. 26 27Challenge Deciders 28++++++++++++++++++ 29 30:mod:`repoze.who` uses a "challenge decider" to decide whether the 31response returned from a downstream application requires a challenge 32plugin to fire. When using the default challenge decider, only the 33status 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 37challenging ``401`` responses which have been "pre-challenged" by the 38application. 39 40You may supply a different challenge decider as necessary. 41 42Plugins 43+++++++ 44 45:mod:`repoze.who` has core functionality designed around the concept 46of plugins. Plugins are instances that are willing to perform one or 47more identification- and/or authentication-related duties. Each 48plugin can be configured arbitrarily. 49 50:mod:`repoze.who` consults the set of configured plugins when it 51intercepts a WSGI request, and gives some subset of them a chance to 52influence 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 62Configuring :mod:`repoze.who` via Python Code 63--------------------------------------------- 64 65.. module:: repoze.who.middleware 66 67.. class:: PluggableAuthenticationMiddleware(app, identifiers, challengers, authenticators, mdproviders, classifier, challenge_decider [, log_stream=None [, log_level=logging.INFO[, remote_user_key='REMOTE_USER']]]) 68 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 87An 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 94 from repoze.who.plugins.redirector import RedirectorPlugin 95 from repoze.who.plugins.htpasswd import HTPasswdPlugin 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') 106 auth_tkt = AuthTktCookiePlugin('secret', 'auth_tkt', digest_algo="sha512") 107 redirector = RedirectorPlugin('/login.html') 108 redirector.classifications = {IChallenger:['browser'],} # only for browser 109 identifiers = [('auth_tkt', auth_tkt), 110 ('basicauth', basicauth)] 111 authenticators = [('auth_tkt', auth_tkt), 112 ('htpasswd', htpasswd)] 113 challengers = [('redirector', redirector), 114 ('basicauth', basicauth)] 115 mdproviders = [] 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 136The above example configures the repoze.who middleware with: 137 138- Two ``IIdentifier`` plugins (auth_tkt cookie, and a 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. 144 145- Two ``IAuthenticator`` plugins: the auth_tkt plugin and an htpasswd plugin. 146 The auth_tkt plugin performs both ``IIdentifier`` and ``IAuthenticator`` 147 functions. The htpasswd plugin is configured with two valid username / 148 password combinations: chris/chris, and admin/admin. When an username 149 and password is found via any identifier, it will be checked against this 150 authenticator. 151 152- Two ``IChallenger`` plugins: the redirector plugin, then the basic auth 153 plugin. The redirector auth will fire if the request is a ``browser`` 154 request, otherwise the basic auth plugin will fire. 155 156The rest of the middleware configuration is for values like logging 157and the classifier and decider implementations. These use the "stock" 158implementations. 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 166Configuring :mod:`repoze.who` via Config File 167--------------------------------------------- 168 169:mod:`repoze.who` may be configured using a ConfigParser-style .INI 170file. The configuration file has five main types of sections: plugin 171sections, a general section, an identifiers section, an authenticators 172section, and a challengers section. Each "plugin" section defines a 173configuration for a particular plugin. The identifiers, 174authenticators, and challengers sections refer to these plugins to 175form a site configuration. The general section is general middleware 176configuration. 177 178To configure :mod:`repoze.who` in Python, using an .INI file, call 179the `make_middleware_with_config` entry point, passing the right-hand 180application, the global configuration dictionary, and the path to the 181config file. The global configuration dictionary is a dictonary passed 182by PasteDeploy. The only key 'make_middleware_with_config' needs is 183'here' pointing to the config file directory. For debugging people 184might find it useful to enable logging by adding the log_file argument, 185e.g. log_file="repoze_who.log" :: 186 187 from repoze.who.config import make_middleware_with_config 188 global_conf = {"here": "."} # if this is not defined elsewhere 189 who = make_middleware_with_config(app, global_conf, 'who.ini') 190 191:mod:`repoze.who`'s configuration file can be pointed to within a PasteDeploy 192configuration 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 200Below is an example of a configuration file (what ``config_file`` 201might point at above ) that might be used to configure the 202:mod:`repoze.who` middleware. A set of plugins are defined, and they 203are referred to by following non-plugin sections. 204 205In the below configuration, five plugins are defined. The form, and 206basicauth plugins are nominated to act as challenger plugins. The 207form, cookie, and basicauth plugins are nominated to act as 208identification plugins. The htpasswd and sqlusers plugins are 209nominated to act as authenticator plugins. :: 210 211 [plugin:redirector] 212 # identificaion and challenge 213 use = repoze.who.plugins.redirector:make_plugin 214 login_url = /login.html 215 216 [plugin:auth_tkt] 217 # identification and authentication 218 use = repoze.who.plugins.auth_tkt:make_plugin 219 secret = s33kr1t 220 cookie_name = oatmeal 221 secure = False 222 include_ip = False 223 digest_algo = sha512 224 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 239 # Note the double %%: we have to escape it from the config parser in 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 243 conn_factory = repoze.who.plugins.sql:make_psycopg_conn_factory 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 249 # Note the double %%: we have to escape it from the config parser in 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 253 filter = my.package:filter_propmd 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 = 270 auth_tkt 271 htpasswd 272 sqlusers 273 274 [challengers] 275 # plugin_name;classifier_name:.. or just plugin_name (good for any) 276 plugins = 277 redirector;browser 278 basicauth 279 280 [mdproviders] 281 plugins = 282 sqlproperties 283 284The basicauth section configures a plugin that does identification and 285challenge for basic auth credentials. The redirector section configures a 286plugin that does challenges. The auth_tkt section configures a plugin that 287does identification for cookie auth credentials, as well as authenticating 288them. The htpasswd plugin obtains its user info from a file. The sqlusers 289plugin obtains its user info from a Postgres database. 290 291The identifiers section provides an ordered list of plugins that are 292willing to provide identification capability. These will be consulted 293in the defined order. The tokens on each line of the ``plugins=`` key 294are in the form "plugin_name;requestclassifier_name:..." (or just 295"plugin_name" if the plugin can be consulted regardless of the 296classification of the request). The configuration above indicates 297that the system will look for credentials using the auth_tkt cookie 298identifier (unconditionally), then the basic auth plugin 299(unconditionally). 300 301The authenticators section provides an ordered list of plugins that 302provide authenticator capability. These will be consulted in the 303defined order, so the system will look for users in the file, then in 304the sql database when attempting to validate credentials. No 305classification prefixes are given to restrict which of the two plugins 306are used, so both plugins are consulted regardless of the 307classification of the request. Each authenticator is called with each 308set of identities found by the identifier plugins. The first identity 309that can be authenticated is used to set ``REMOTE_USER``. 310 311The mdproviders section provides an ordered list of plugins that 312provide metadata provider capability. These will be consulted in the 313defined order. Each will have a chance (on ingress) to provide add 314metadata to the authenticated identity. Our example mdproviders 315section shows one plugin configured: "sqlproperties". The 316sqlproperties plugin will add information related to user properties 317(e.g. first name and last name) to the identity dictionary. 318 319The challengers section provides an ordered list of plugins that 320provide challenger capability. These will be consulted in the defined 321order, so the system will consult the cookie auth plugin first, then 322the basic auth plugin. Each will have a chance to initiate a 323challenge. The above configuration indicates that the redirector challenger 324will fire if it's a browser request, and the basic auth challenger 325will fire if it's not (fallback). 326