Latest Version: 0.9.6.2
  Dashboard > Pylons Cookbook > ... > Recipes > Authentication and Authorization
  Pylons Cookbook Log In | Sign Up   View a printable version of the current page.  
  Authentication and Authorization
Added by Graham Higgins, last edited by Eric Ongerth on Mar 16, 2008  (view change) show comment
Labels: 

AuthKit

Historically AuthKit has been the typical solution for authentication and authorization needs in Pylons applications. Unfortunately the version 0.3 has a few bugs like problems with Unicode strings. Version 0.4 is in the making but not yet finished. Most of the documentation that is floating around doesn't match either version properly. At the moment (August 2007) AuthKit should probably be avoided. It is definitely worth a look in the future because it include different ways of authentication (like OpenID) and works as a proper WSGI middleware layer.

Home-grown authentication

Pylons makes it easy to hook functionality into your application. So it's easy to add authentication to your application. This is just a quick recipe to show you how easy it is.

lib/base.py

Add a class variable (here: requires_auth) to your BaseController class. It is set to false by default to declare all controllers as not requiring authentication. It can be set to true for the controllers that you like to be protected. Like:

1
2
3
4
5
class MyController(BaseController):
    requires_auth = True

    def index(self):
        ...

Add a __before__ method to that class that checks whether requires_auth is set to true. It then checks whether a session variable user is set (that is supposed to contain the username of the logged-in user). If it finds that there is no such variable set then the user is redirected to a login controller. For some extra user-friendliness the current path is saved so that the user can return to the actually requested page after the successful login:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
class BaseController(WSGIController):
    requires_auth = False

    def __before__(self):
        # Authentication required?
        if self.requires_auth and 'user' not in session:
            # Remember where we came from so that the user can be sent there
            # after a successful login
            session['path_before_login'] = request.path_info
            session.save()
            return redirect_to(h.url_for(controller='login'))

controllers/login.py

Your LoginController just needs to do the authentication. In basic cases it displays a form to read the username and password and to check it against a user database table. Example:

 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
class LoginController(BaseController):
   def login(self):
       """
       Show login form. Submits to /login/submit
       """
       return render('login.mako')

   def submit(self):
       """
       Verify username and password
       """
       # Both fields filled?
       form_username = str(request.params.get('username'))
       form_password = str(request.params.get('password'))

       # Get user data from database
       db_user = model.WebUser.query(User).get_by(name=form_username)
       if db_user is None: # User does not exist
           return render('login.mako')

       # Wrong password? (MD5 hashes used here)
       if db_user.passwd != md5.md5(form_password).hexdigest():
           return render('login.mako')

       # Mark user as logged in
       session['user'] = form_username
       session.save()

       # Send user back to the page he originally wanted to get to
       if session.get('path_before_login'):
           redirect_to(session['path_before_login'])
       else: # if previous target is unknown just send the user to a welcome page
           return render('loggedin.mako')

   def logout(self):
       """
       Logout the user and display a confirmation message
       """
       if 'user' in session:
           del session['user']
           session.save()
       return render('logout.mako')

Per Action Authorization

lib/base.py

1
2
3
4
5
6
7
8
9
class BaseController(WSGIController):
   requires_auth = []

   def __before__(self, action):
       if action in self.requires_auth:
           if 'user' not in session:
               session['path_before_login'] = request.path_info
               session.save()
               return redirect_to(h.url_for(controller='login'))

controller/my.py

1
2
3
4
5
class MyController(BaseController):
 requires_auth = ['index']

 def index(self):
     pass

Home-grown authorization

The way of authorizing users (to check whether they have certain permission) can be done similarly.

Could someone clarify what "session" is?

I often see SqlAlchemy 'Session', beaker 'session', and random 'session' items. This is a bit unclear.

Posted by jonathan at Mar 06, 2008 23:04 | Permalink

jonathan, in this case 'session' refers to the Beaker session, not SQLAlchemy's (capitalized) Session. It's basically a dict of persistent data typically lasting as long as the actual browser session (but configurable to use cookies and various options for expiration, and multiple back-end storage options). More information on it is available in the Beaker docs, and a couple of references to it within pylonshq.com can be found by searching the site for 'pylons.session'.

Posted by Eric Ongerth at Mar 16, 2008 04:56 | Permalink
Site running on a free Atlassian Confluence Open Source Project License granted to Pylons. Evaluate Confluence today.
Powered by Atlassian Confluence, the Enterprise Wiki. (Version: 2.3.3 Build:#645 Feb 13, 2007) - Bug/feature request - Contact Administrators
Top