CherryPy Project Download

It's a lot less complex than Carlos's AuthenticationFramework?, and is a lot like CSAuthenticate while providing more flexibility.

from cherrypy.lib import aspect, httptools
from cherrypy import cpg

class NewAuth(aspect.Aspect):
    """
    NewAuth is a more powerful authentication system for CherryPy which borrows from CSAuthenticate.
    You'll probably want to override loginScreen, logoutScreen, loggedInScreen, and checkCredentials.

    NewAuth is based on a system of roles. When a user logs in, they are assigned a list of roles they
    belong to based on the credentials they give. Every object and method may have a roles attribute
    which is a list of roles which are allowed to access a given resource.

    The default implementation uses ugly forms which have customizable messages. It checks username
    and password credentials via the loginMap dictionary, and if they match, assigns the user two roles:
    "loggedIn" and their username.
    """
    # all of these variables are for basic functionality which you should really override.
    permissionMessage = "You do not have permission to access this page."
    badCredentialsMessage = "Your credentials were invalid."
    loggedInMessage = "You are now logged in."
    loggedOutMessage = "You are now logged out."
    loginMap = {"login":"password"}
    def _before(self, methodName, method):
        if not getattr(method, 'exposed', None):
            return aspect.CONTINUE, None
        if methodName in ["doLogin", "logout", "login"]:
            return aspect.CONTINUE, None
        userRoles = self.getUserRoles()
        if hasattr(self, "roles"):
            # first, check to see if he's got the controller credentials
            requiredRoles = getattr(self, "roles")
            ok = False
            for role in userRoles:
                if role in requiredRoles:
                    ok = True
                    break
            if not ok:
                return aspect.STOP, self.loginScreen(cpg.request.browserUrl, self.permissionMessage)
        if hasattr(method, "roles"):
            # now, check method credentials
            requiredRoles = getattr(method, "roles")
            ok = False
            for role in userRoles:
                if role in requiredRoles:
                    ok = True
                    break
            if not ok:
                return aspect.STOP, self.loginScreen(cpg.request.browserUrl, self.permissionMessage)
        return aspect.CONTINUE, None
    def getUserRoles(self):
        return cpg.request.sessionMap.get("roles",[])
    def loginScreen(self, fromPage="loggedIn", message=""):
        """
        Override me. You *must* pass the fromPage variable on to doLogin, but
        other than that you're free to choose what your form variable names are
        (if you've overloaded checkCredentials). Don't change the arguments.
        """
        return """
        <html><body>
            <form method="post" action="doLogin">
                %s<br />
                Login: <input type="text" name="login" size="10" /><br />
                Password: <input type="password" name="password" size="10" /><br />
                <input type="hidden" name="fromPage" value="%s" /><br />
                <input type="submit" value="Login" />
            </form>
        </body></html>
        """ % (message, fromPage)
    def login(self):
        return self.loginScreen()
    login.exposed = True
    def loggedInScreen(self):
        """
        This is the default page you see if you've logged in. Feel free
        to override it.
        """
        return """
        <html><body>%s</body></html>""" % self.loggedInMessage
    def loggedIn(self):
        return self.loggedInScreen()
    loggedIn.exposed = True
    def doLogin(self, fromPage, **kwargs):
        roles = self.checkCredentials(**kwargs)
        if len(roles) == 0:
            return self.loginScreen(fromPage, self.badCredentialsMessage)
        else:
            cpg.request.sessionMap["roles"] = roles
            httptools.redirect(fromPage)
            return ""
    doLogin.exposed = True
    def logout(self):
        cpg.request.sessionMap["roles"] = []
        return self.logoutScreen()
    logout.exposed = True
    def logoutScreen(self):
        """
        Override me to provide the screen which displays the logged out message.
        """
        return """
        <html><body>%s</body></html>""" % self.loggedOutMessage
    def checkCredentials(self, login, password):
        """
        Override me. Return a list of roles the user is now in. Return a list of length 0
        if the user's credentials were invalid. This is passed the keyword args from doLogin, so you
        can actually validate on any credentials you desire.
        """
        desiredPassword = self.loginMap.get(login, False)
        if desiredPassword and desiredPassword == password:
            return ["loggedIn", login]
        else:
            return []

if __name__ == "__main__":
    class Test(NewAuth):
        roles = ["loggedIn"] # every exposed method in this object requires login
        loginMap = {"login":"password","admin":"admin"}
        def index(self):
            return "index is protected by loggedIn"
        index.exposed = True
        def admin(self):
            return "this can only be accessed by admin"
        admin.exposed = True
        admin.roles = ["admin"] # requires loggedIn _and_ admin
    cpg.root = Test()
    cpg.server.start(configDict={"port":8080,"sessionStorageType":"ram"})

Hosted by WebFaction

Log in as guest/cherrypy to create/edit wiki pages