configuration.rst 13 KB

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