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

« back to all changes in this revision

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

Port www/apps/logout to new framework (in ivle.webapp.security).

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