Source

wdmmg / wdmmg / controllers / rest.py

import logging
from datetime import datetime
# http://micheles.googlecode.com/hg/decorator/documentation.html
import decorator
# Docs as per
# http://docs.python.org/release/2.6.5/library/json.html#module-json
import simplejson

from pylons import request, response, session, tmpl_context as c, url
from pylons.controllers.util import abort, redirect

from wdmmg.lib.base import BaseController, render
from wdmmg import model

log = logging.getLogger(__name__)

# List of actions that can be performed on a domain object.
READ = u'READ'


# :TODO: Consider moving to lib/ ?
def jsonpify(func, *args, **kwargs):
    """A decorator that reformats the output as JSON; or, if the
    *callback* parameter is specified (in the HTTP request), as JSONP.
    
    Very much modelled after pylons.decorators.jsonify .
    """

    from pylons.decorators.util import get_pylons

    pylons = get_pylons(args)
    data = func(*args, **kwargs)
    if isinstance(data, (list, tuple)):
        msg = "JSON responses with Array envelopes are susceptible to " \
              "cross-site data leak attacks, see " \
              "http://pylonshq.com/warnings/JSONArray"
        warnings.warn(msg, Warning, 2)
        log.warning(msg)
    result = simplejson.dumps(data)
    if 'callback' in pylons.request.params:
        pylons.response.headers['Content-Type'] = 'text/javascript'
        log.debug("Returning JSONP wrapped action output")
        # The parameter is a unicode object, which we don't want (as it
        # causes Pylons to complain when we return a unicode object from
        # this function).  All reasonable values of this parameter will
        # "str" with no problem (ASCII clean).  So we do that then.
        cbname = str(pylons.request.params['callback'])
        result = '%s(%s);' % (cbname, result)
    else:
        pylons.response.headers['Content-Type'] = 'application/json'
        log.debug("Returning JSON wrapped action output")
    return result
jsonpify = decorator.decorator(jsonpify)


class RestController(BaseController):

    def index(self):
        dataset_ = model.Dataset.find_one()
        c.urls = [
            url(controller='rest', action='dataset', id=dataset_.name),
            url(controller='rest', action='dataset', id=dataset_.id),
            #url(controller='rest', action='entry', id=model.Entry.find_one().name),
            url(controller='rest', action='entry', id=model.Entry.find_one().id),
        ]
        return render('home/rest.html')
    
    @jsonpify
    def dataset(self, id=None):
        return self._domain_object(model.Dataset.by_id(id))
        
    @jsonpify
    def entry(self, id=None):
        return self._domain_object(model.Entry.by_id(id))
        
    def _domain_object(self, domain_object):
        self._check_access(domain_object, READ)
        return domain_object.to_safe_dict()

    def _check_access(self, domain_object, action):
        '''
        Checks whether the supplied `apikey` permits `action` to be performed
        on `domain_object`. If allowed, returns `True`. If forbidden, throws
        an appropriate HTTPException.
        '''
        if action == READ:
            return True
        abort(403)