~azzar1/unity/add-show-desktop-key

« back to all changes in this revision

Viewing changes to ivle/webapp/base/rest.py

fixed a slight bug on line 155, which referenced a non-existant variable

Show diffs side-by-side

added added

removed removed

Lines of Context:
18
18
# Author: Matt Giuca, Will Grant
19
19
 
20
20
import cgi
 
21
import urlparse
21
22
import inspect
22
23
 
23
24
import cjson
24
25
 
25
26
from ivle.webapp.base.views import BaseView
26
 
from ivle.webapp.errors import BadRequest, MethodNotAllowed
 
27
from ivle.webapp.errors import BadRequest, MethodNotAllowed, Unauthorized
27
28
 
28
29
class RESTView(BaseView):
29
30
    """
33
34
    content_type = "application/octet-stream"
34
35
 
35
36
    def __init__(self, req, *args, **kwargs):
36
 
        pass
 
37
        for key in kwargs:
 
38
            setattr(self, key, kwargs[key])
37
39
 
38
40
    def render(self, req):
39
 
        if req.method == 'GET':
40
 
            outstr = self.GET(req)
41
 
        # XXX PATCH hack
42
 
        if req.method == 'PUT':
43
 
            outstr = self.PATCH(req, req.read())
44
 
        req.content_type = self.content_type
45
 
        req.write(outstr)
 
41
        raise NotImplementedError()
46
42
 
47
43
class JSONRESTView(RESTView):
48
44
    """
54
50
        lambda self: [m for m in ('GET', 'PUT', 'PATCH')
55
51
                      if hasattr(self, m)] + ['POST'])
56
52
 
 
53
    def authorize(self, req):
 
54
        return True # Real authz performed in render().
 
55
 
 
56
    def authorize_method(self, req, op):
 
57
        if not hasattr(op, '_rest_api_permission'):
 
58
            raise Unauthorized()
 
59
 
 
60
        if op._rest_api_permission not in self.get_permissions(req.user):
 
61
            raise Unauthorized()
 
62
    
 
63
    def convert_bool(self, value):
 
64
        if value == 'True' or value == 'true' or value == True:
 
65
            return True
 
66
        elif value == 'False' or value == 'False' or value == False:
 
67
            return False
 
68
        else:
 
69
            raise BadRequest()
 
70
 
57
71
    def render(self, req):
58
72
        if req.method not in self._allowed_methods:
59
73
            raise MethodNotAllowed(allowed=self._allowed_methods)
60
74
 
61
75
        if req.method == 'GET':
 
76
            self.authorize_method(req, self.GET)
62
77
            outjson = self.GET(req)
63
78
        # Since PATCH isn't yet an official HTTP method, we allow users to
64
79
        # turn a PUT into a PATCH by supplying a special header.
65
80
        elif req.method == 'PATCH' or (req.method == 'PUT' and
66
81
              'X-IVLE-Patch-Semantics' in req.headers_in and
67
82
              req.headers_in['X-IVLE-Patch-Semantics'].lower() == 'yes'):
68
 
            outjson = self.PATCH(req, cjson.decode(req.read()))
 
83
            self.authorize_method(req, self.PATCH)
 
84
            try:
 
85
                input = cjson.decode(req.read())
 
86
            except cjson.DecodeError:
 
87
                raise BadRequest('Invalid JSON data')
 
88
            outjson = self.PATCH(req, input)
69
89
        elif req.method == 'PUT':
70
 
            outjson = self.PUT(req, cjson.decode(req.read()))
 
90
            self.authorize_method(req, self.PUT)
 
91
            try:
 
92
                input = cjson.decode(req.read())
 
93
            except cjson.DecodeError:
 
94
                raise BadRequest('Invalid JSON data')
 
95
            outjson = self.PUT(req, input)
71
96
        # POST implies named operation.
72
97
        elif req.method == 'POST':
73
98
            # TODO: Check Content-Type and implement multipart/form-data.
74
 
            opargs = dict(cgi.parse_qsl(req.read()))
 
99
            data = req.read()
 
100
            opargs = dict(cgi.parse_qsl(data, keep_blank_values=1))
75
101
            try:
76
102
                opname = opargs['ivle.op']
77
103
                del opargs['ivle.op']
87
113
               not op._rest_api_callable:
88
114
                raise BadRequest('Invalid named operation.')
89
115
 
 
116
            self.authorize_method(req, op)
 
117
 
90
118
            # Find any missing arguments, except for the first two (self, req)
91
119
            (args, vaargs, varkw, defaults) = inspect.getargspec(op)
92
120
            args = args[2:]
97
125
            # we are OK.
98
126
            unspec = set(args) - set(opargs.keys())
99
127
            if unspec and not defaults:
100
 
                raise BadRequest('Missing arguments: ' + ','.join(unspec))
 
128
                raise BadRequest('Missing arguments: ' + ', '.join(unspec))
101
129
 
102
130
            unspec = [k for k in unspec if k not in args[-len(defaults):]]
103
131
 
104
132
            if unspec:
105
 
                raise BadRequest('Missing arguments: ' + ','.join(unspec))
 
133
                raise BadRequest('Missing arguments: ' + ', '.join(unspec))
106
134
 
107
135
            # We have extra arguments if the are no match args in the function
108
136
            # signature, AND there is no **.
111
139
                raise BadRequest('Extra arguments: ' + ', '.join(extra))
112
140
 
113
141
            outjson = op(req, **opargs)
114
 
        else:
115
 
            raise AssertionError('Unknown method somehow got through.')
116
142
 
117
143
        req.content_type = self.content_type
118
144
        if outjson is not None:
119
145
            req.write(cjson.encode(outjson))
120
146
            req.write("\n")
121
147
 
122
 
def named_operation(meth):
 
148
class named_operation(object):
123
149
    '''Declare a function to be accessible to HTTP users via the REST API.
124
150
    '''
125
 
    meth._rest_api_callable = True
126
 
    return meth
 
151
    def __init__(self, permission):
 
152
        self.permission = permission
 
153
 
 
154
    def __call__(self, func):
 
155
        func._rest_api_callable = True
 
156
        func._rest_api_permission = self.permission
 
157
        return func
 
158
 
 
159
class require_permission(object):
 
160
    '''Declare the permission required for use of a method via the REST API.
 
161
    '''
 
162
    def __init__(self, permission):
 
163
        self.permission = permission
 
164
 
 
165
    def __call__(self, func):
 
166
        func._rest_api_permission = self.permission
 
167
        return func
127
168