0

I am using cherrypy as a web server, and I want to check a user's logged-in status before returning the page. This works on methods in the main Application class (in site.py) but gives an error when I call the same decorated function on method in a class that is one layer deeper in the webpage tree (in a separate file).

validate_user() is the function used as a decorator. It either passes a user to the page or sends them to a 401 restricted page, as a cherrypy.Tool, like this:

from user import validate_user
cherrypy.tools.validate_user = cherrypy.Tool('before_handler', validate_user)

I attach different sections of the site to the main site.py file's Application class by assigning instances of the sub-classes as variables accordingly:

from user import UserAuthentication

class Root:
    user = UserAuthentication() # maps user/login, user/register, user/logout, etc
    admin = Admin()
    api = Api()

    @cherrypy.expose
    @cherrypy.tools.validate_user()
    def how_to(self, **kw):
        from other_stuff import how_to_page
        return how_to_page(kw) 

This, however, does not work when I try to use the validate_user() inside the Admin or Api or Analysis sections. These are in separate files.

import cherrypy

class Analyze:
    @cherrypy.expose
    @cherrypy.tools.validate_user() #### THIS LINE GIVES ERROR ####
    def explore(self, *args, **kw): # @addkw(fetch=['uid'])
        import explore
        kw['uid'] = cherrypy.session.get('uid',-1)
        return explore.explorer(args, kw)

The error is that cherrypy.tools doesn't have a validate_user function or method. But other things I assign in site.py do appear in cherrypy here. What's the reason why I can't use this tool in a separate file that is part of my overall site map?

If this is relevant, the validate_user() function simply looks at the cherrypy.request.cookie, finds the 'session_token' value, and compares it to our database and passes it along if the ID matches.

Sorry I don't know if the Analyze() and Api() and User() pages are subclasses, or nested classes, or extended methods, or what. So I can't give this a precise title. Do I need to pass in the parent class to them somehow?

Marc Maxmeister
  • 4,191
  • 4
  • 40
  • 54
  • I should note that this is a follow up to my previous question https://stackoverflow.com/questions/47840006/passing-function-into-a-class-and-using-it-as-a-decorator-of-class-methods and inspired by this example https://stackoverflow.com/questions/6552025/cherrypy-custom-tool-for-user-authentication – Marc Maxmeister Dec 18 '17 at 20:42
  • Where is the assignment `cherrypy.tools.validate_user = cherrypy.Tool('before_handler', validate_user)` happening? If it's not in a module that get imported when your second class runs, then the assingment will never occur and you won't be able to access the `validate_user` decorator, even though you have access to the place it would be if the assignment had been done. It might be simple to just add an extra import to your second file so that the assignment runs. It might also be a sign that you should keep the decorator somewhere else, rather than writing into `cherypy.tools`. – Blckknght Dec 22 '17 at 00:05
  • the assignment `cherrypy.tools.validate_user = cherrypy.Tool('before_handler', validate_user)` happens in site.py, the top level file, which presumably loads when the server loads (before it calls the Root class where the second class is instantiated) – Marc Maxmeister Dec 22 '17 at 17:46

1 Answers1

1

The issue here is that Python processes everything except the function/method bodies during import. So in site.py, when you import user (or from user import <anything>), that causes all of the user module to be processed before the Python interpreter has gotten to the definition of the validate_user tool, including the decorator, which is attempting to access that tool by value (rather than by a reference).

CherryPy has another mechanism for decorating functions with config that will enable tools on those handlers. Instead of @cherrypy.tools.validate_user, use:

@cherrypy.config(**{"tools.validate_user.on": True})

This decorator works because instead of needing to access validate_user from cherrypy.tools to install itself on the handler, it instead configures CherryPy to install that tool on the handler later, when the handler is invoked.

If that tool is needed for all methods on that class, you can use that config decorator on the class itself.

You could alternatively, enable that tool for given endpoints in the server config, as mentioned in the other question.

Jason R. Coombs
  • 41,115
  • 10
  • 83
  • 93