PylonsHQ.

Layout: Fixed-width

Another approach for authorization in pylons (decorator based, repoze.what like)

A very nice, clean way to provide authorization for controller actions is system based on decorator usage.

put this code in /lib/decorators/authorize.py

Decorator Code

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
from decorator import decorator
from someapp.lib.auth_conditions import NotValidAuth 

def authorize(valid, handler): 
    def validate(func, self, *args, **kwargs):
        try:
            valid.check()
        except NotValidAuth, e:
            return handler(e)
        return func(self, *args, **kwargs)
    return decorator(validate)

This is the main decorator that will be used to test permissions for specific actions. Basically it receives the conditions it has to check and a handler function reference to execute if the conditions are not met.

Ok, so we now have EVERYTHING needed to protect our actions (and controllers too, by decorating the _before_ method).

But now we need to know what type of permission we want to check, so we need to have some basic classes that will handle that for us.

So now we need to create the AllMet condition class and OneMet - those will handle every possible condition.

Now let's create /lib/auth_conditions/_init_.py.

All Conditions we need

 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
class NotValidAuth(Exception):
    pass

class AllMet(object):
    
    message = u'All of these conditions have to be met: %s.'
    
    def __init__(self, *args):
        self.conditions = args
    
    def check(self):
        condition_messages = []
        valid = True
        for condition in self.conditions:
            try:
                condition.check()
            except NotValidAuth, e:
                valid = False
                condition_messages.append(unicode(e))
        if valid:
            return True
        raise NotValidAuth(self.message % ', '.join(condition_messages))
    

class OneMet(object):
    
    message = u'At least one of these conditions have to be met: %s.'
    
    def __init__(self, *args):
        self.conditions = args
    
    def check(self):
        condition_messages = []
        valid = False
        for condition in self.conditions:
            try:
                condition.check()
                return True
            except NotValidAuth, e:
                condition_messages.append(unicode(e))
        raise NotValidAuth(self.message % ', '.join(condition_messages))


class Not(object):
    message = u'All of these conditions have to be not met: %s.'
    
    def __init__(self, *args):
        self.conditions = args
    
    def check(self):
        condition_messages = []
        valid = True
        for condition in self.conditions:
            try:
                condition.check()
                valid = False
            except NotValidAuth, e:
                condition_messages.append(unicode(e))
        if valid:
            return True
        raise NotValidAuth(self.message % ', '.join(condition_messages))

From now on we can implement and check any condition we want to test.

Let's create a simple IsLogged condition - that will test if user is logged on to our app.

IsLogged

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
from pylons import session
from someapp.lib.auth_conditions import NotValidAuth 

class IsLogged(object):
    
    message = u'User must be logged'
    
    def check(self):
        if 'user' in session:
            return True
        raise NotValidAuth(self.message)

Now let's put all this together to protect our controller to make it accessible only for logged users:

controller 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
import pylons
#bla bla bla, the usual stuff is here
from someapp.lib.decorators.authorize import authorize 
from someapp.lib.auth_conditions import AllMet, OneMet
from someapp.lib.auth_conditions.is_logged import IsLogged

#this handler will get executed if condition is not met
def handler(message):
    # let's do something here like throw redirect exception, 
    # or maybe do a 401

class FooController(BaseController):
        
    @authorize(IsLogged(),handler)
    def __before__(self, controller, action):
        # often we need to remember to handle parent class __before__
        super(FooController, self).__before__(controller, action)

    #another example how to protect edit_foo action
    @authorize(OneMet(IsModerator(), EditFooPerm()), handler)
    def edit_foo(self, controller, action):
        return "I'm protected"

    #another example how to protect edit_foo action
    @authorize(AllMet(HasMoney(), FooPerm()), handler)
    def buy_foo(self, controller, action):
        # action code

Now this is all that is needed to protect your controllers and actions, now all you need to implement are your condition classes - for example use sqlalchemy to check group rights etc.

Another common use of our classes includes checking permissions INSIDE actions. This can be accomplished by simple try catch blocks we can use.

Check for moderator access

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
from pylons import session
from someapp.lib.auth_conditions import NotValidAuth 
from someapp.lib.auth_conditions.new_cond import NewCond

class FooController(BaseController):

    #lets try to fetch the data based on user status
    def edit_foo(self, controller, action):
        try:
           NewCond().check()
           #do something here if user rights are sufficient (maybe admin user?)
        except (NotValidAuth, ),e:
           #do something else, maybe do ACL check or render simplified template?

Enter labels to add to this page:
Please wait 
Looking for a label? Just start typing.

Powered by Pylons - Contact Administrators