views:

131

answers:

2

I have a cherrypy application and on some of the views I want to start only allowing certain users to view them, and sending anyone else to an authorization required page.

Is there a way I can do this with a custom decorator? I think that would be the most elegant option.

Here's a basic example of what I want to do:

class MyApp:
    @authorization_required
    def view_page1(self,appID):
        ... do some stuff ...
        return html

def authorization_required(func):
    #what do I put here?

Also can the authorization_required function when called as a decorator accept parameters like allow_group1, allow_group2? Or do I need a separate decorator for each group?

A: 

Ok, in that case your decorator would look something like this:

# without any parameters
def authentication_required(f):
    @functools.wraps
    def _authentication_required(*args, **kwargs):
        # Do you login stuff here
        return f(*args, **kwargs)
    return _authentication_required

# With parameters
def authentication_required(*allowed_groups):
    def _authentication_required(f):
        @functools.wraps
        def __authentication_required(*args, **kwargs):
            # Do you login stuff here
            return f(*args, **kwargs)
        return __authentication_required
    return _authentication_required
WoLpH
What does @functools.wraps do? Is that built into cherrypy?
Greg
Also, I guess I only return f(*args, **kwargs) when the login stuff has succeeded? If on the other hand the user is not authorized, would I call a cherry.redirect instead of returning?
Greg
Yes, correct.And the `functools.wraps` is a method that handles automatic copying of the function name, docs and other data when writing decorators. That way if you do `help(method)` on a decorated method, you still get the originals docs.
WoLpH
I'm on Python 2.4 so I don't have functools. Is there a workaround?
Greg
You can simply copy the `__name__`, `__module__` and `__doc__` properties to your function. It seems that fumanchu has a more fitting solution though.
WoLpH
+3  A: 

You really don't want to be writing custom decorators for CherryPy. Instead, you want to write a new Tool:

def myauth(allowed_groups=None, debug=False):
    # Do your auth here...
    authlib.auth(...)
cherrypy.tools.myauth = cherrypy.Tool("on_start_resource", myauth)

See http://docs.cherrypy.org/dev/progguide/extending/customtools.html for more discussion. This has several benefits over writing a custom decorator:

  1. You get the decorator for free from the Tool: @cherrypy.tools.myauth(allowed_groups=['me']), and it already knows how to not clobber cherrypy.exposed on the same function.
  2. You can apply Tools either per-handler (with the decorator), per-controller-tree (via _cp_config) or per-URI-tree (in config files or dicts). You can even mix them and provide a base feature via decorators and then override their behavior in config files.
  3. If a config file turns your feature off, you don't pay the performance penalty of calling the decorator function just to see if it's off.
  4. You'll remember to add a 'debug' arg like all the builtin Tools have. ;)
  5. Your feature can run earlier (or later, if that's what you need) than a custom decorator can, by selecting a different "point".
  6. Your feature can run at multiple hook points, if needed.
fumanchu
I'm not sure if this would work for me because this code is using the RoutesDispatcher (instead of?) the expose decorator. I'm not sure why it was done that way.
Greg
Which dispatcher you use is not really an issue: any dispatcher worth its salt (including the Routes one) will work with Tools.The expose decorator does nothing more than set `method.exposed = True`. Again, this is required for all dispatchers and has no bearing on whether or not Tools work. Take it from the horse's mouth: Tools are the way to do this in CherryPy.
fumanchu