summaryrefslogtreecommitdiff
path: root/modules/installed/lib/auth.py
blob: 6e67fb8d5122e82ac5d60eeb89e7fd617e9699b2 (plain)
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
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
# Form based authentication for CherryPy. Requires the
# Session tool to be loaded.
#
# Thanks for this code is owed to Arnar Birgisson -at - gmail.com.  It
# is based on code he wrote that was retrieved from
# http://tools.cherrypy.org/wiki/AuthenticationAndAccessRestrictions
# on 1 February 2011.

# TODO: DeprecationWarning: the md5 module is deprecated; use hashlib instead import md5

import cherrypy
import urllib, hashlib
import cfg
import random

cfg.session_key = '_cp_username'

def check_credentials(username, passphrase):
    """Verifies credentials for username and passphrase.
    Returns None on success or a string describing the error on failure"""

    start = time.clock()

    if not username or not passphrase:
        error = "No username or password."
        cfg.log(error)
        return error

    u = cfg.users[username]

    if u is None:
        # hash the password whether the user exists, to foil timing
        # side-channel attacks
        hashlib.md5(passphrase).hexdigest()
        error = "Bad user-name or password."
    elif u['passphrase'] != hashlib.md5(passphrase).hexdigest():
        error = "Bad user-name or password."
    else:
        error = None

    if error:
        cfg.log(error)
    return error

def check_auth(*args, **kwargs):
    """A tool that looks in config for 'auth.require'. If found and it
    is not None, a login is required and the entry is evaluated as a
    list of conditions that the user must fulfill"""
    conditions = cherrypy.request.config.get('auth.require', None)
    # format GET params
    get_params = urllib.quote(cherrypy.request.request_line.split()[1])
    if conditions is not None:
        username = cherrypy.session.get(cfg.session_key)
        if username:
            cherrypy.request.login = username
            for condition in conditions:
                # A condition is just a callable that returns true or false
                if not condition():
                    # Send old page as from_page parameter
                    raise cherrypy.HTTPRedirect("/auth/login?from_page=%s" % get_params)
        else:
            # Send old page as from_page parameter
            raise cherrypy.HTTPRedirect("/auth/login?from_page=%s" % get_params)

cherrypy.tools.auth = cherrypy.Tool('before_handler', check_auth)

def require(*conditions):
    """A decorator that appends conditions to the auth.require config
    variable."""
    def decorate(f):
        if not hasattr(f, '_cp_config'):
            f._cp_config = dict()
        if 'auth.require' not in f._cp_config:
            f._cp_config['auth.require'] = []
        f._cp_config['auth.require'].extend(conditions)
        return f
    return decorate


# Conditions are callables that return True
# if the user fulfills the conditions they define, False otherwise
#
# They can access the current username as cherrypy.request.login
#
# Define those at will however suits the application.

def member_of(groupname):
    def check():
        # replace with actual check if <username> is in <groupname>
        return cherrypy.request.login == 'joe' and groupname == 'admin'
    return check

def name_is(reqd_username):
    return lambda: reqd_username == cherrypy.request.login

# These might be handy

def any_of(*conditions):
    """Returns True if any of the conditions match"""
    def check():
        for c in conditions:
            if c():
                return True
        return False
    return check

# By default all conditions are required, but this might still be
# needed if you want to use it inside of an any_of(...) condition
def all_of(*conditions):
    """Returns True if all of the conditions match"""
    def check():
        for c in conditions:
            if not c():
                return False
        return True
    return check