News

[17/Sep/2009:19:20] Release 0.5 includes Open Flash Chart support.

[01/Jul/2009:10:50] Repoze.who authentication tutorial added

[22/Jun/2009:11:36] AJAX calculator tutorial added.

[01/May/2009:14:15] MVC/SQL based wiki tutorial added.



Contact Email:



view source
W1100_1500.
who.
whiff



Download instructions
at whiff.sourceforge.net
project page
.
WHIFF DOCUMENTION

Authenticating users using repoze.who

This tutorial explains how to use the WHIFF interface to repoze.who to authenticate users. By authenticating users and checking the authentication a WHIFF application may determine who can and who cannot access pages or whole URL subtrees.

Contents:
The dimensions of repoze.who
Configuring who using WHIFF resources
Configuring a WHIFF application group for basic authentication using an htpasswd file
The htpasswd file
Deploying an application with authentication resources
Restricting page access using the protect middleware
Accessing the authentication protected file...
Configuring a WHIFF application group for form based authentication
Saving some typing in the include directives
Determining whether the user is logged in
The login form
The failure page
Allowing only logged in users to see a page
Allowing only certain users to see a page
Restricting access to directory contents
Deploying a directory with authentication under a web server
Summary
The repoze.who package available from http://static.repoze.org/whodocs/ provides a flexible interface which allows WSGI applications to mix and match different implementations for different aspects of web user authentication.

The WHIFF standard middleware package middleware/repoze includes support interfaces that make the repoze.who functionality easily configurable for WHIFF applications. The repoze.who modules must be installed separately from the whiff package before the interfaces will work.  

×
Please see http://static.repoze.org/whodocs/ for details about installing who. Or if you are brave and have easy_install simply type
$ easy_install repoze.who

This tutorial walks through a couple example configurations using repoze.who with WHIFF. The source files for the installations described here are available in the WHIFF distribution under INSTALL/test.

The dimensions of repoze.who

The repoze.who package provides a relatively sophisticated object oriented interface where different dimensions of user authentication are handled by different component types: Many of these component types have a selection of implementations which may be combined freely with other implementations.

For a WHIFF application to use who there must be a sensible mechanism for configuring these components within a WHIFF application.

Configuring who using WHIFF resources

To allow different installations of an application to easily configure different authentication strategies, the WHIFF interfaces for who assume the different components are deployed as WHIFF resources in a root application, usually using standard resource paths.

For example to locate the list of authentication Identifiers the middlewares normally ask for the resource located at the resource path ["who.identifiers"].

To prepare an application deployment to use who a root application application must deploy the resources such as ["who.identifiers"] by registering a resource, something like this

identifiers = [...]
application = resolver.moduleRootApplication(...)
application.registerStaticResource(prefix="who.identifiers",
                               resourceValue=identifiers)
With the who resources configured, applications contained in the module root application may use the WHIFF interfaces for who to authenticate users and make use of the authentication information. For example the following configuration template fragment authenticates the user and prints out the authenticated user name
{{include "whiff_middleware/repoze/protect"}}
        {{using allowUsers}}true{{/using}}
        {{using failureRedirect}}failure{{/using}}
        {{using app}}

        <h1>Greetings {{include "whiff_middleware/repoze/uid"/}}</h1>
	...

        {{/using}}
{{/include}}
The middlewares "whiff_middleware/repoze/protect" and "whiff_middleware/repoze/uid" use the deployed who resources to perform the authentication and store the authentication information. The protect middleware in this case only allows logged in users to see the Greeting.... The uid middleware extracts the authenticated user-id from the who data structures in the WSGI environment.  
×
Please see below on how to use imports to save some typing so you don't have to type whiff_middleware/repoze/... over and over and over.

The following sections describe the complete process for installing authentication in an application and using it to restrict access to web pages or web page trees.

Configuring a WHIFF application group for basic authentication using an htpasswd file

In this simple application deployment we will configure a root application module INSTALL/test/whobasic/ to authenticate users using basic HTTP authentication, where the user name and password information are stored in a simple htpasswd text file.

The whobasic module should not be aware of how users are authenticated so it can be used under different authentication schemes. For this reason the authentication information is deployed separately from the whobasic module itself.

The deployment configuration for whobasic is defined in the configuration script INSTALL/test/basicWho.py. Below are the imports used by the basicWho script.

import os
import sys
import logging
from StringIO import StringIO
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.htpasswd import HTPasswdPlugin
# basicWho.py continued below...
To define a who deployment we first need to specify which components we want to use as identifiers, authenticators, and so forth. The following code lines construct the who components we will deploy.
# basicWho.py continued...
# find the user/password file in the same directory at ./htpasswd
htfilename = os.path.split(os.path.abspath(__file__))[0] + "/htpasswd"
htfile = file(htfilename)

# the passwords are in clear text
def cleartext_check(password, hashed):
    return password == hashed

# use the htpasswd file to find user names and passwords
htpasswd = HTPasswdPlugin(htfile, cleartext_check)

# allow HTTP basic authentication.
basicauth = BasicAuthPlugin('repoze.who')

# the repoze.who identifiers
identifiers = [('basicauth',basicauth)]

# the repoze.who authenticators
authenticators = [('htpasswd', htpasswd)]

# the repoze.who challengers
challengers = [('basicauth',basicauth)]

# no metadata providers, please.
mdproviders = []

# use default classifiers and deciders
from repoze.who.classifiers import default_request_classifier
from repoze.who.classifiers import default_challenge_decider

log_stream = None
#... basicWho.py continued below

The htpasswd file

The HTPasswdPlugin instance constructed above reads a file named htpasswd listing the user-ids and passwords for users in clear text. Here is the content of that file:
admin:admin
chris:chris
carla:carla
sam:sam
The repoze.who package and available plug-ins provide other ways to specify user names and passwords, including plug-ins that allow ids and passwords to be found in an SQL database.

Deploying an application with authentication resources

The basicWho script uses the who components constructed above to create a root application that deploys the components in the whoTestRootApplication defined below:
# basicWho.py continued...

def whoTestRootApplication(path="/"):
    from whiff import resolver
    from whiff.middleware import displayTraceback
    import whobasic
    
    # create the root application
    testapp = resolver.moduleRootApplication(path, whobasic,
                             exception_middleware=displayTraceback.__middleware__,
                             on_not_found=None, # show traceback (could comment)
                             )
    # set up all the repoze.who resources at standard
    # resource prefixes.

    # these MUST be defined for the plugin to work:
    testapp.registerStaticResource(prefix="who.identifiers",
                               resourceValue=identifiers)
    testapp.registerStaticResource(prefix="who.authenticators",
                               resourceValue=authenticators)
    testapp.registerStaticResource(prefix="who.challengers",
                               resourceValue=challengers)

    # these are optional:
    testapp.registerStaticResource(prefix="who.mdproviders",
                               resourceValue=mdproviders)
    testapp.registerStaticResource(prefix="who.classifier",
                               resourceValue=default_request_classifier)
    testapp.registerStaticResource(prefix="who.challenge_decider",
                               resourceValue=default_challenge_decider)
    testapp.registerStaticResource(prefix="who.log_stream",
                               resourceValue=log_stream)
    testapp.registerStaticResource(prefix="who.log_level",
                               resourceValue=logging.DEBUG)
    return testapp

# basicWho.py continued below...
The whoTestRootApplication function constructs a moduleRootApplication using the whobasic module and then attaches resources for who based authentication at standard locations using the registerStaticResource method.

To allow the configured root application to run as a stand alone server in test mode we add the following script interpretation.

# basicWho.py continued...
if __name__=="__main__":
    # script interpretation: launch the app in a server at port 8888
    import wsgiref.simple_server
    testapp = whoTestRootApplication()
    print "serving whotest at 8888"
    srv = wsgiref.simple_server.make_server('localhost', 8888, testapp)
    srv.serve_forever()

Restricting page access using the protect middleware

As mentioned above the module whobasic is not aware of how authentication is implemented -- instead it assumes that some higher configuration like basicWho.py has set things up properly so that authentication is available.

The python package initialization file whobasic/__init__.py identifies the package as a __wsgi_directory__ and publishes templates and files with recognized extensions, as usual.

from whiff import resolver

__wsgi_directory__ = True

resolver.publish_templates(__path__[0], globals(), mime_extensions=True,
                           directory_middleware=True)
Applications contained in whobasic may now make use of the authentication mechanism configured by basicWho.py. For example the configuration template whobasic/ChrisssPage.whiff specifies that the user must be authenticated as either "chris" or "admin" before the page content can sent to the user
{{env whiff.content_type: "text/html"/}}
{{include "whiff_middleware/repoze/protect"}}
	{{using allowUsers}} ["admin", "chris"] {{/using}}
	{{using app}}

	<h1>Greetings 'admin' or 'chris'</h1>

	Did you know that if chickens didn't lay eggs we'd
	have to color something else at Easter?
	That's why chickens lay eggs.
	<p>
	<a href="index">return to index.</a>

	{{/using}}
{{/include}}
Here the "whiff_middleware/repoze/protect" middleware is configured to allow only the users ["admin", "chris"] to see the app content <h1>Greetings ... -- all others will be asked to log in or will be refused.

Accessing the authentication protected file...

To test the configuration we run the script interpretation of basicWho.py which launches a test server.
INSTALL/test/ $ python basicWho.py
serving whotest at 8888
...
Now if we direct a browser to http://localhost:8888 we see the standard WHIFF directory listing, which is not protected by authentication.
However, if we click on the "ChrissPage" link the browser demands our user name and password:
If we identify ourselves as "admin/admin" the server allows us to see the page content
Of course it is far more useful to restrict access to whole collections of pages at once. The following example shows how to restrict access to the contents of whole directories.

Configuring a WHIFF application group for form based authentication

The basic authentication challenge shown above which is built in to the browser has some disadvantages: you cannot change it's "look and feel" and it does not make it easy to manage "logout" or "change user" logic, among other things. The standard who component RedirectingFormPlugin addresses these issues.  
×
The RedirectingFormPlugin implementation is sophisticated and not very easy to understand. I hope the following discussion will get you started with a working configuration which you can modify to your own purposes, even if some of the mechanisms shown are not easily understood.

The following configuration module INSTALL/test/runWho.py is similar to the basicWho.py configuration script shown above, except that it configures the WHIFF directory root whotest and it deploys different who components.

The first part of the INSTALL/test/runWho.py file defines the who components to use for authentication, introducing a cookie based AuthTktCookiePlugin and a web form based RedirectingFormPlugin.

import os
import sys
import logging
from StringIO import StringIO
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.form import RedirectingFormPlugin
from repoze.who.plugins.htpasswd import HTPasswdPlugin

# find the user/password file in the same directory at ./htpasswd
htfilename = os.path.split(os.path.abspath(__file__))[0] + "/htpasswd"
htfile = file(htfilename)

# the passwords are in clear text
def cleartext_check(password, hashed):
    return password == hashed

# use the htpasswd file to find user names and passwords
htpasswd = HTPasswdPlugin(htfile, cleartext_check)

# allow HTTP basic authentication.
basicauth = BasicAuthPlugin('repoze.who')

# also allow auth_tkt based authentication
auth_tkt = AuthTktCookiePlugin('secret', 'auth_tkt')

# use the repoze.who redirecting form plugin for challenges and identification
form = RedirectingFormPlugin('login_url', '/login_handler_path',
                             '/logout_handler_path', rememberer_name='auth_tkt')

# set up the form classifications
form.classifications = { IIdentifier:['browser'],
                         IChallenger:['browser'] }

# the repoze.who identifiers
identifiers = [('form', form),('auth_tkt',auth_tkt),('basicauth',basicauth)]

# the repoze.who authenticators
authenticators = [('htpasswd', htpasswd)]

# the repoze.who challengers
challengers = [('form',form), ('basicauth',basicauth)]

# no metadata providers, please.
mdproviders = []

# use default classifiers and deciders
from repoze.who.classifiers import default_request_classifier
from repoze.who.classifiers import default_challenge_decider

# log to standard output if WHO_LOG is set in the os.environ
log_stream = None
Here the assignment
# use the repoze.who redirecting form plugin for challenges and identification
form = RedirectingFormPlugin('/login_url', '/login_handler_path',
                             '/logout_handler_path', rememberer_name='auth_tkt')
sets up a form handler which will use the web form at URL /login_url to ask the user for the user name and password. The form handler will also look for requests with url paths that end with /logout_handler_path and "log out" any authenticated user using the response to the request. The form handler will also look for requests for URLs that end in /login_handler_path and attempt to identify submitted user information in the request.

The remainder of runWho.py is nearly identical to basicWho.py except that it configures the whotest root directory in place of the whobasic directory.

def whoTestRootApplication(path="/"):
    from whiff import resolver
    from whiff.middleware import displayTraceback
    import whotest
    
    # create the root application
    testapp = resolver.moduleRootApplication(path, whotest,
                             exception_middleware=displayTraceback.__middleware__,
                             on_not_found=None, # show traceback (could comment)
                             )
    # set up all the repoze.who resources at standard
    # resource prefixes.

    # these MUST be defined for the plugin to work:
    testapp.registerStaticResource(prefix="who.identifiers",
                               resourceValue=identifiers)
    testapp.registerStaticResource(prefix="who.authenticators",
                               resourceValue=authenticators)
    testapp.registerStaticResource(prefix="who.challengers",
                               resourceValue=challengers)

    # these are optional:
    testapp.registerStaticResource(prefix="who.mdproviders",
                               resourceValue=mdproviders)
    testapp.registerStaticResource(prefix="who.classifier",
                               resourceValue=default_request_classifier)
    testapp.registerStaticResource(prefix="who.challenge_decider",
                               resourceValue=default_challenge_decider)
    testapp.registerStaticResource(prefix="who.log_stream",
                               resourceValue=log_stream)
    testapp.registerStaticResource(prefix="who.log_level",
                               resourceValue=logging.DEBUG)
    return testapp

if __name__=="__main__":
    # script interpretation: launch the app in a server at port 8888
    import wsgiref.simple_server
    testapp = whoTestRootApplication()
    print "serving whotest at 8888"
    srv = wsgiref.simple_server.make_server('localhost', 8888, testapp)
    srv.serve_forever()

Saving some typing in the include directives

Like all WHIFF root directories INSTALL/test/whotest must include a standard initialization file INSTALL/test/whotest/__init__.py.
from whiff import resolver

__wsgi_directory__ = True

resolver.publish_templates(__path__[0], globals(), mime_extensions=True)

# import conveniences...
from whiff.middleware.repoze import uid
from whiff.middleware.repoze import protect
from whiff.middleware.repoze import who
from whiff.middleware.repoze import allow
from whiff.middleware.repoze import onStatus
from whiff.middleware import absPath

# INSTALL/test/whotest/__init__.py TO BE CONTINUED BELOW....
This __init__.py file identifies whotest as a WHIFF directory and publishes templates, as usual. The __init__.py file also imports the whiff.middleware.repoze middlewares as a convenience so that files in whotest can refer to {{include "uid"... instead of the equivalent {{include "whiff_middleware/repoze/uid"....

Determining whether the user is logged in

The following fragment of the INSTALL/test/whotest/index.whiff determines whether the user is logged in and offers a link to the login page if the user is not logged in. The "who" middleware authenticates the user (or fails to authenticate the user). The "onStatus" middleware presents the loggedIn page to users who automatically identify and authenticate successfully, and presents the unknown page to users who do not automatically authenticate successfully.
{{env whiff.content_type: "text/html"/}}

<h1>Index page for authentication test directory</h1>
{{include "who"}}
    {{include "onStatus"}}
         {{using loggedIn}}
		You are logged in as {{include "uid"/}}:
		<a href="login_url/logout_handler_path">
			logout or log in as a different user. </a>
	 {{/using}}
         {{using unknown}}
		You are not logged in:
		<a href="login_url/form?came_from={{get-env whiff.app_path/}}">
			login. </a>
         {{/using}}
    {{/include}}
{{/include}}
<br><br>

<h1>Pages</h1>
....
Note that the href links shown above are tightly bound to the RedirectingFormPlugin implementation logic. The login_url link
"login_url/form?came_from={{get-env whiff.app_path/}}"
sends the browser to the login form (described below), requesting that after the user has logged in the browser should return to the current page ({{get-env whiff.app_path/}}). The ../logout_handler_path link
"login_url/logout_handler_path"
sends the browser back to the login page, but appends to the URL the path /logout_handler_path which the RedirectingFormPlugin handler will recognize as a logout request  
×
Got that? No? Sorry. :(. The whiff.who infrastructure is powerful, but a bit complex.

The login form

The RedirectingFormPlugin instance configured above requires a login page at login_url. Here is the WHIFF configuration template INSTALL/test/whotest/login_url.whiff which implements the login_url page.
{{env whiff.content_type: "text/html"/}}
{{include "who" whiff.parse_cgi: true}}
  {{cgi-default came_from}}index{{/cgi-default}}

  {{include "onStatus"}}

    {{using loggedIn}}

	You are logged in as {{include "uid"}}UNKNOWN{{/include}}.<br>
	You must log out before logging in again.<br>
	<a href="/index">Go to the index page.</a>

    {{/using}}
    {{using unknown}}

	You are not logged in.

	<h1> Please log in... </h1>

	  <form action="../login_url/login_handler_path">
	    <input type="hidden" name="came_from" value="{{get-cgi came_from/}}">
	    <table border="0">
	    <tr>
	      <td>User Name</td>
	      <td><input type="text" name="login"></input></td>
	    </tr>
	    <tr>
	      <td>Password</td>
	      <td><input type="password" name="password"></input></td>
	    </tr>
	    <tr>
	      <td></td>
	      <td><input type="submit" name="submit" value="Log In"/></td>
	    </tr>
	    </table>
	  </form>

	<a href="/index">Go to the index page without logging in.</a>

    {{/using}}
  {{/include}}
{{/include}}
The logic of this page is tightly bound to the RedirectingFormPlugin implementation logic. We can use the index and login_url pages to test the log/in/out mechanism.

For example if we start a test server using the script interpretation of the INSTALL/test/runWho.py configuration

INSTALL/test/ $ python runWho.py
serving whotest at 8888
...
and direct a web browser browser to the index page http://localhost:8888/index we see that we are not logged in:
Following the login link we may log in using user name admin and password admin
When we click the log in button the browser returns to the index page, and the index page recognizes us as authenticated user admin.
Continuing in this manner we may log in and out using different user ids and passwords until we are blue in the face, but just logging in and out by itself is not that interesting -- we ultimately need to use authentication to allow authorized users to see protected data, and prevent other users from seeing the protected data.

The failure page

When users tries to access a page they are not authorized to view the response should do something reasonable instead of sending the protected information. In this case we will redirect the browser to a failure page -- for example the protect middleware invocation
{{include "whiff_middleware/repoze/protect"}}
        {{using allowUsers}}true{{/using}}
        {{using failureRedirect}}failure{{/using}}
        {{using app}}

        <h1>Greetings {{include "whiff_middleware/repoze/uid"/}}</h1>
	...

        {{/using}}
{{/include}}
specifies that failed attempts to view the protected app should be redirected to the failureRedirect URL failure. We implement the failure page using the colorful WHIFF configuration template INSTALL/test/whotest/failure.whiff
{{env whiff.content_type: "text/html"/}}

<html>
<head> <title> ACCESS DENIED </title> </head>
<body style="color:green; background-color:red">
<h1>Protected page</h1>

You tried to view a page that was protected.
Only specific users may view the page.
<p>
<a href="index">return to index.</a>
</body></html>

Allowing only logged in users to see a page

The configuration template INSTALL/test/whotest/loggedIn.whiff uses the protect middleware to present the app content only to users who have logged in.
{{env whiff.content_type: "text/html"/}}
{{include "protect"}}
	{{using allowUsers}}true{{/using}}
	{{using failureRedirect}}failure{{/using}}
	{{using app}}

	<h1>Greetings {{include "uid"/}}</h1>

	As a logged in user you are allowed to learn that the capitol of England is London!
	<p>
	<a href="index">return to index.</a>

	{{/using}}
{{/include}}
The parameter {{using allowUsers}}true{{/using}} indicates that any authenticated user is allowed to view the page. The parameter {{using failureRedirect}}failure{{/using}} insists that users who are not logged in should be redirected to the failure page.

And indeed when we run the test server and attempt to view http://localhost:8888/loggedIn we receive the failure page instead:

However if we log in as carla and return to http://localhost:8888/loggedIn we see the protected content:

Allowing only certain users to see a page

The whiff configuration template INSTALL/test/whotest/ChrissPage.whiff uses the protect middleware to allow users chris or admin to view the app content, with all others redirected to the failure page.
{{env whiff.content_type: "text/html"/}}
{{include "protect"}}
	{{using allowUsers}} ["admin", "chris"] {{/using}}
	{{using failureRedirect}}failure{{/using}}
	{{using app}}

	<h1>Greetings 'admin' or 'chris'</h1>

	Did you know that if chickens didn't lay eggs we'd
	have to color something else at Easter?
	That's why chickens lay eggs.
	<p>
	<a href="index">return to index.</a>

	{{/using}}
{{/include}}
Here the parameter
{{using allowUsers}} ["admin", "chris"] {{/using}}
specifies that only admin or chris are allowed to see the content. This page is very similar to one presented earlier.

Restricting access to directory contents

The function whiff.middleware.repoze.protect.protectDirectory is a directory root application factory which is analogous to the protect middleware, except that it applies access restrictions to a directory and all the directory contents.

The code fragment below provides the remainder of the source for INSTALL/test/whotest/__init__.py omitted in the listing above. The assignments in the code fragment apply access restrictions to INSTALL/test/whotest subdirectories.

# ... INSTALL/test/whotest/__init__.py continued

from whiff.middleware.repoze.protect import protectDirectory

import loggedInDir
# only logged in users can view loggedInDir content
loggedInDir = protectDirectory(loggedInDir, "loggedInDir", True, "failure")

import loggedOutDir
# only logged out users can view loggedOutDir content
loggedOutDir = protectDirectory(loggedOutDir, "loggedOutDir", None, "failure")

import adminOnlyDir
# only 'admin' can view adminOnlyDir content
adminOnlyDir = protectDirectory(adminOnlyDir, "adminOnlyDir", ["admin"], "failure")

import ChrissDir
# only 'chris' or 'admin' can view ChrissDir content
ChrissDir = protectDirectory(ChrissDir, "ChrissDir", ["chris", "admin"], "failure")
For example in the test server if carla attempts to view http://localhost:8888/ChrissDir/readme.txt she will see the failure page, but if chris attempts to view http://localhost:8888/ChrissDir/readme.txt he will see the plain text contents of the file ADMIN/test/whotest/ChrissDir/readme.txt.

Deploying a directory with authentication under a web server

A WHIFF application using who authentication may be deployed under any web server configuration which supports the WSGI interface standard. For example the following CGI script /usr/local/apache2/cgi-bin/who.cgi deploys the whotest root application under an Apache server using the CGI interface.
#!/Library/Frameworks/Python.framework/Versions/Current/bin/python

import sys
import wsgiref.handlers
import whiff.resolver

# import the root directory containing whotest
sys.path.append("/Users/Aaron/work/whiff/test/")
import runWho

# create a WSGI application to serve the whotest directory
application = runWho.whoTestRootApplication(path="/cgi-bin/who.cgi")

# serve a CGI request using the directory
wsgiref.handlers.CGIHandler().run(application)
This script uses the standard method for using a WSGI application to implement a CGI script, using the runWho.whoTestRootApplication(path="/cgi-bin/who.cgi") function call to construct the application. Note that the CGI script must be told the root path it replies to /cgi-bin/who.cgi.

Please view this application installed at http://aaron.oirt.rutgers.edu/cgi-bin/who.cgi/. To install the CGI script on your computer you will need to change some of the absolute paths in the script to reflect correct locations on you system, and you will also need to mark the CGI script executable (as usual for CGI scripts).

Summary

This discussion demonstrated how to configure WHIFF directories using repoze.who to I hope you found it helpful.
0 comments.
Care to comment?
name: (required)
- email (not published):
comment: (required)

<< security number? >>