diff options
Diffstat (limited to 'module/web/api_app.py')
-rw-r--r-- | module/web/api_app.py | 94 |
1 files changed, 52 insertions, 42 deletions
diff --git a/module/web/api_app.py b/module/web/api_app.py index 1629c1677..52903e92b 100644 --- a/module/web/api_app.py +++ b/module/web/api_app.py @@ -5,41 +5,43 @@ from urllib import unquote from itertools import chain from traceback import format_exc, print_exc -from bottle import route, request, response, HTTPError +from bottle import route, request, response, HTTPError, parse_auth -from utils import toDict, set_session +from utils import set_session, get_user_api from webinterface import PYLOAD -from module.common.json_layer import json -from module.lib.SafeEval import const_eval as literal_eval -from module.Api import BaseObject - -# json encoder that accepts TBase objects -class TBaseEncoder(json.JSONEncoder): - - def default(self, o): - if isinstance(o, BaseObject): - return toDict(o) - return json.JSONEncoder.default(self, o) +from module.Api import ExceptionObject +from module.remote.json_converter import loads, dumps +from module.utils import remove_chars +def add_header(r): + r.headers.replace("Content-type", "application/json") + r.headers.append("Cache-Control", "no-cache, must-revalidate") + r.headers.append("Access-Control-Allow-Origin", "*") # allow xhr requests # accepting positional arguments, as well as kwargs via post and get - -@route("/api/:func:args#[a-zA-Z0-9\-_/\"'\[\]%{}]*#") -@route("/api/:func:args#[a-zA-Z0-9\-_/\"'\[\]%{}]*#", method="POST") +# only forbidden path symbol are "?", which is used to separate GET data and # +@route("/api/<func><args:re:[^#?]*>") +@route("/api/<func><args:re:[^#?]*>", method="POST") def call_api(func, args=""): - response.headers.replace("Content-type", "application/json") - response.headers.append("Cache-Control", "no-cache, must-revalidate") + add_header(response) s = request.environ.get('beaker.session') + auth = parse_auth(request.get_header('Authorization', '')) if 'session' in request.POST: - s = s.get_by_id(request.POST['session']) + # removes "' so it works on json strings + s = s.get_by_id(remove_chars(request.POST['session'], "'\"")) + elif auth: + user = PYLOAD.checkAuth(auth[0], auth[1], request.environ.get('REMOTE_ADDR', None)) + # if auth is correct create a pseudo session + if user: s = {'uid': user.uid} - if not s or not s.get("authenticated", False): - return HTTPError(403, json.dumps("Forbidden")) + api = get_user_api(s) + if not api: + return HTTPError(403, dumps("Forbidden")) - if not PYLOAD.isAuthorized(func, {"role": s["role"], "permission": s["perms"]}): - return HTTPError(401, json.dumps("Unauthorized")) + if not PYLOAD.isAuthorized(func, api.user): + return HTTPError(401, dumps("Unauthorized")) args = args.split("/")[1:] kwargs = {} @@ -49,54 +51,62 @@ def call_api(func, args=""): kwargs[x] = unquote(y) try: - return callApi(func, *args, **kwargs) + return callApi(api, func, *args, **kwargs) + except ExceptionObject, e: + return HTTPError(400, dumps(e)) except Exception, e: print_exc() - return HTTPError(500, json.dumps({"error": e.message, "traceback": format_exc()})) + return HTTPError(500, dumps({"error": e.message, "traceback": format_exc()})) +# TODO Better error codes on invalid input -def callApi(func, *args, **kwargs): +def callApi(api, func, *args, **kwargs): if not hasattr(PYLOAD.EXTERNAL, func) or func.startswith("_"): print "Invalid API call", func - return HTTPError(404, json.dumps("Not Found")) + return HTTPError(404, dumps("Not Found")) - result = getattr(PYLOAD, func)(*[literal_eval(x) for x in args], - **dict([(x, literal_eval(y)) for x, y in kwargs.iteritems()])) + # TODO: accept same payload as WS backends, combine into json_converter + # TODO: arguments as json dictionaries + # TODO: encoding + result = getattr(api, func)(*[loads(x) for x in args], + **dict([(x, loads(y)) for x, y in kwargs.iteritems()])) - # null is invalid json response + # null is invalid json response if result is None: result = True - return json.dumps(result, cls=TBaseEncoder) + return dumps(result) #post -> username, password @route("/api/login", method="POST") def login(): - response.headers.replace("Content-type", "application/json") - response.headers.append("Cache-Control", "no-cache, must-revalidate") + add_header(response) - user = request.forms.get("username") + username = request.forms.get("username") password = request.forms.get("password") - info = PYLOAD.checkAuth(user, password) + user = PYLOAD.checkAuth(username, password, request.environ.get('REMOTE_ADDR', None)) - if not info: - return json.dumps(False) + if not user: + return dumps(False) - s = set_session(request, info) + s = set_session(request, user) # get the session id by dirty way, documentations seems wrong try: sid = s._headers["cookie_out"].split("=")[1].split(";")[0] - return json.dumps(sid) + return dumps(sid) except: - return json.dumps(True) + print "Could not get session" + return dumps(True) @route("/api/logout") +@route("/api/logout", method="POST") def logout(): - response.headers.replace("Content-type", "application/json") - response.headers.append("Cache-Control", "no-cache, must-revalidate") + add_header(response) s = request.environ.get('beaker.session') s.delete() + + return dumps(True) |