2
2
The main entry point for RPC4Django. Usually, the user simply puts
3
:meth:`serve_rpc_request <rpc4django.views.serve_rpc_request>` into ``urls.py``
3
:meth:`rpc_url <rpc4django.utils.rpc_url>` into ``urls.py``
7
7
urlpatterns = patterns('',
8
8
# rpc4django will need to be in your Python path
9
(r'^RPC2$', 'rpc4django.views.serve_rpc_request'),
9
rpc_url(r'^RPC2$', name="my_url_name"),
15
16
from xml.dom.minidom import parseString
16
17
from xml.parsers.expat import ExpatError
17
18
from django.http import HttpResponse, Http404, HttpResponseForbidden
18
19
from django.shortcuts import render_to_response
19
20
from django.conf import settings
20
from django.core.urlresolvers import reverse, NoReverseMatch
21
21
from rpcdispatcher import RPCDispatcher
22
22
from __init__ import version
45
45
# these will be scanned for @rpcmethod decorators
46
46
APPS = getattr(settings, 'INSTALLED_APPS', [])
48
def check_request_permission(request, request_format='xml'):
48
logger = logging.getLogger("rpc4django.views")
50
class NonExistingDispatcher(Exception):
51
"""Raised when the dispatcher for a particular name is not found."""
52
def __init__(self, path, url_name):
53
super(NonExistingDispatcher, self).__init__(
54
"URL name '%s' is not used in any rpcmethod,\
55
however, the URL '%s' uses it." % (url_name, path))
57
self.url_name = url_name
59
def get_dispatcher(path, url_name):
61
dispatcher = dispatchers[url_name]
63
raise NonExistingDispatcher(path, url_name)
66
def check_request_permission(request, request_format='xml', url_name="root"):
50
68
Checks whether this user has permission to call a particular method
51
69
This method does not check method call validity. That is done later
55
73
- ``request`` - a django HttpRequest object
56
74
- ``request_format`` - the request type: 'json' or 'xml'
75
- ``url_name`` - the name of the url at which this request was received
58
77
Returns ``False`` if permission is denied and ``True`` otherwise
61
80
user = getattr(request, 'user', None)
81
dispatcher = get_dispatcher(request.path, url_name)
62
82
methods = dispatcher.list_methods()
63
83
method_name = dispatcher.get_method_name(request.raw_post_data, \
69
89
# this is the method the user is calling
70
90
# time to check the permissions
71
91
if method.permission is not None:
72
logging.debug('Method "%s" is protected by permission "%s"' \
92
logger.debug('Method "%s" is protected by permission "%s"' \
73
93
%(method.name, method.permission))
75
95
# user is only none if not using AuthenticationMiddleware
76
logging.warn('AuthenticationMiddleware is not enabled')
96
logger.warn('AuthenticationMiddleware is not enabled')
78
98
elif not user.has_perm(method.permission):
79
99
# check the permission against the permission database
80
logging.info('User "%s" is NOT authorized' %(str(user)))
100
logger.info('User "%s" is NOT authorized' %(str(user)))
83
logging.debug('User "%s" is authorized' %(str(user)))
103
logger.debug('User "%s" is authorized' %(str(user)))
85
logging.debug('Method "%s" is unprotected' %(method.name))
105
logger.debug('Method "%s" is unprotected' %(method.name))
113
133
if LOG_REQUESTS_RESPONSES:
114
logging.info('Unrecognized content-type "%s"' %conttype)
115
logging.info('Analyzing rpc request data to get content type')
134
logger.info('Unrecognized content-type "%s"' %conttype)
135
logger.info('Analyzing rpc request data to get content type')
117
137
# analyze post data to see whether it is xml or json
118
138
# this is slower than if the content-type was set properly
136
156
the Django HttpRequest object
158
the name of the url at which the request arrived
162
dispatcher = get_dispatcher(request.path, url_name)
140
164
if request.method == "POST" and len(request.POST) > 0:
141
165
# Handle POST request with RPC payload
143
167
if LOG_REQUESTS_RESPONSES:
144
logging.debug('Incoming request: %s' %str(request.raw_post_data))
168
logger.debug('Incoming request: %s' % str(request.raw_post_data))
146
170
if is_xmlrpc_request(request):
150
if not check_request_permission(request, 'xml'):
174
if not check_request_permission(request, 'xml', url_name=url_name):
151
175
return HttpResponseForbidden()
153
resp = dispatcher.xmldispatch(request.raw_post_data, \
177
resp = dispatcher.xmldispatch(request.raw_post_data,
178
request=request, **kwargs)
155
179
response_type = 'text/xml'
157
181
if RESTRICT_JSON:
160
if not check_request_permission(request, 'json'):
184
if not check_request_permission(request, 'json', url_name=url_name):
161
185
return HttpResponseForbidden()
163
resp = dispatcher.jsondispatch(request.raw_post_data, \
187
resp = dispatcher.jsondispatch(request.raw_post_data,
188
request=request, **kwargs)
165
189
response_type = 'application/json'
167
191
if LOG_REQUESTS_RESPONSES:
168
logging.debug('Outgoing %s response: %s' %(response_type, resp))
192
logger.debug('Outgoing %s response: %s' %(response_type, resp))
170
194
return HttpResponse(resp, response_type)
171
195
elif request.method == 'OPTIONS':
225
249
if csrf_exempt is not None:
226
250
serve_rpc_request = csrf_exempt(serve_rpc_request)
228
# reverse the method for use with system.describe and ajax
230
URL = reverse(serve_rpc_request)
231
except NoReverseMatch:
252
def _register_rpcmethods(apps, restrict_introspection=False, restrict_ootb_auth=True, dispatchers={}):
254
Scans the installed apps for methods with the rpcmethod decorator
255
Adds these methods to the list of methods callable via RPC
234
# instantiate the rpcdispatcher -- this examines the INSTALLED_APPS
235
# for any @rpcmethod decorators and adds them to the callable methods
236
dispatcher = RPCDispatcher(URL, APPS, RESTRICT_INTROSPECTION, RESTRICT_OOTB_AUTH)
259
# check each app for any rpcmethods
260
app = __import__(appname, globals(), locals(), ['*'])
262
method = getattr(app, obj)
263
if callable(method) and \
264
getattr(method, 'is_rpcmethod', False) == True:
265
# if this method is callable and it has the rpcmethod
266
# decorator, add it to the dispatcher
267
if method.url_name not in dispatchers:
268
logger.debug("Registered URL name '%s'" % method.url_name)
269
dispatchers[method.url_name] = RPCDispatcher(
270
method.url_name, restrict_introspection,
273
"Registered method '%s' to URL name '%s'"
274
% (method.external_name, method.url_name))
275
dispatchers[method.url_name].register_method(
276
method, method.external_name)
277
elif isinstance(method, types.ModuleType):
278
# if this is not a method and instead a sub-module,
279
# scan the module for methods with @rpcmethod
281
_register_rpcmethods(
282
["%s.%s" % (appname, obj)],
283
restrict_introspection, restrict_ootb_auth, dispatchers)
288
dispatchers = _register_rpcmethods(APPS, RESTRICT_INTROSPECTION, RESTRICT_OOTB_AUTH)