summaryrefslogtreecommitdiff
path: root/artima/python/webpy/resource_auth.py
blob: d7247a5da5a6fb4a4095eba2995f5b0ee9b826da (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
"""
An authorization library for resources. It requires an user table with 
fields username, password and role.
"""

from dispatcher import HttpResourceDispatcher, Request, Response

class AuthenticatedUser(object):
    def __init__(self, username):
        self.username = username
    def __str__(self):
        return '<%s %s>' % (self.__class__.__name__, self.username)

def _subclasses(base, acc):
    for sub in base.__subclasses__():
        acc.append(sub)
        _subclasses(sub, acc)

def subclasses(base):
    acc = [base]
    _subclasses(base, acc)
    return acc

def getclass(role):
    "The role is a name of a user class"
    for c in subclasses(AuthenticatedUser):
        if c.__name__ == role:
            return c
    raise NameError('Unknown role %s' % role)

class AuthorizationModel(object):
    """
    Provides a single method getrole extracting the role string
    from the user table. Requires in input as fieldmapping a dictionary
    with keys user, username, password, role; moreover, you can pass
    the placeholder used by the paramstyle of the database driver. 
    """
    def __init__(self, conn, fieldmapping=None, placeholder='?'):
        fieldmapping = fieldmapping or {}
        self.fieldmapping = fieldmapping
        self.placeholder = placeholder
        self.user = fieldmapping.get('user', 'user')
        self.username = fieldmapping.get('username', 'username')
        self.password = fieldmapping.get('password', 'password')
        self.role = fieldmapping.get('role', 'role')
        getrole = 'SELECT %(role)s FROM %(user)s WHERE %(username)s=?'
        self.getrole_templ = (getrole % vars(self)).replace('?', placeholder)
        self.conn = conn
        self.curs = conn.cursor()

    def getuser(self, username):
        "From username to user object"
        # username is authenticated, therefore in the database for sure
        self.curs.execute(self.getrole_templ, (username,))
        role = self.curs.fetchall()[0][0]
        return getclass(role)(username)

class HttpDispatcherAuth(HttpResourceDispatcher):
    """
    A resource manage which looks to the resource table for the given
    (path, meth) and responds unauthorized if the current user role
    is not sufficient to access the resource. Notice that it MUST
    be wrapped in an authentication middleware. getuser is callable
    taking an username and returning an user object.
    """   
    def __init__(self, getuser, Request=Request, Response=Response):
        HttpResourceDispatcher.__init__(self, Request, Response)
        self.getuser = getuser
        self.rolemap = {} # {path->userclass}

    def bind_hook(self, resource, path, kw):
        self.rolemap[path] = kw['roles'] # this is a class
        return resource

    def call_hook(self, resource, req, args, ctype, path):
        auth_classes = self.rolemap[path]
        user = self.getuser(req.remote_user)
        if isinstance(user, auth_classes):
            return HttpResourceDispatcher.call_hook(
                self, resource, req, args, ctype, path)
        else:
            self.respond('User %s has no access permission' % user,
                         '401 Unauthorized')