diff options
-rw-r--r-- | module/remote/create_apitypes.py | 9 | ||||
-rw-r--r-- | module/remote/wsbackend/Server.py | 13 | ||||
-rw-r--r-- | module/web/api_app.py | 54 | ||||
-rw-r--r-- | pavement.py | 2 | ||||
-rwxr-xr-x | pyload.py | 1 | ||||
-rw-r--r-- | tests/api/test_JSONBackend.py | 36 |
6 files changed, 71 insertions, 44 deletions
diff --git a/module/remote/create_apitypes.py b/module/remote/create_apitypes.py index 7755b5c57..d596f07ac 100644 --- a/module/remote/create_apitypes.py +++ b/module/remote/create_apitypes.py @@ -3,19 +3,18 @@ import re import inspect -import sys from os.path import abspath, dirname, join path = dirname(abspath(__file__)) -module = join(path, "..", "..") - -sys.path.append(join(module, "lib")) -sys.path.append(join(module, "remote")) +root = abspath(join(path, "..", "..")) from thrift.Thrift import TType from thriftgen.pyload import ttypes from thriftgen.pyload import Pyload +# TODO: import and add version +# from pyload import CURRENT_VERSION + type_map = { TType.BOOL: 'bool', TType.DOUBLE: 'float', diff --git a/module/remote/wsbackend/Server.py b/module/remote/wsbackend/Server.py index 9b52f5464..af5e1cf19 100644 --- a/module/remote/wsbackend/Server.py +++ b/module/remote/wsbackend/Server.py @@ -459,9 +459,18 @@ class WebSocketRequestHandler(CGIHTTPServer.CGIHTTPRequestHandler): if self._options.is_executable_method is not None: self.is_executable = self._options.is_executable_method + # OWN MODIFICATION # This actually calls BaseRequestHandler.__init__. - CGIHTTPServer.CGIHTTPRequestHandler.__init__( - self, request, client_address, server) + try: + CGIHTTPServer.CGIHTTPRequestHandler.__init__( + self, request, client_address, server) + except socket.error, e: + # Broken pipe, let it pass + if e.errno != 32: + raise + self._logger.debug("WS: Broken pipe") + + def parse_request(self): """Override BaseHTTPServer.BaseHTTPRequestHandler.parse_request. diff --git a/module/web/api_app.py b/module/web/api_app.py index 690cf73aa..e8042f3fb 100644 --- a/module/web/api_app.py +++ b/module/web/api_app.py @@ -27,11 +27,11 @@ def call_api(func, args=""): add_header(response) s = request.environ.get('beaker.session') + # Accepts standard http auth auth = parse_auth(request.get_header('Authorization', '')) - # TODO: session as GET - if 'session' in request.POST: + if 'session' in request.POST or 'session' in request.GET: # removes "' so it works on json strings - s = s.get_by_id(remove_chars(request.POST['session'], "'\"")) + s = s.get_by_id(remove_chars(request.params.get('session'), "'\"")) elif auth: user = PYLOAD.checkAuth(auth[0], auth[1], request.environ.get('REMOTE_ADDR', None)) # if auth is correct create a pseudo session @@ -44,47 +44,45 @@ def call_api(func, args=""): if not PYLOAD.isAuthorized(func, api.user): return HTTPError(401, dumps("Unauthorized")) - args = args.split("/")[1:] + if not hasattr(PYLOAD.EXTERNAL, func) or func.startswith("_"): + print "Invalid API call", func + return HTTPError(404, dumps("Not Found")) + + # TODO: possible encoding + # TODO Better error codes on invalid input + + args = [loads(unquote(arg)) for arg in args.split("/")[1:]] kwargs = {} + # accepts body as json dict + if request.json: + kwargs = request.json + + # convert arguments from json to obj separately for x, y in chain(request.GET.iteritems(), request.POST.iteritems()): - if x == "session": continue - kwargs[x] = unquote(y) + if not x or not y or x == "session": continue + kwargs[x] = loads(unquote(y)) try: - return callApi(api, func, *args, **kwargs) + result = getattr(api, func)(*args, **kwargs) + # null is invalid json response + if result is None: result = True + return dumps(result) + except ExceptionObject, e: return HTTPError(400, dumps(e)) except Exception, e: print_exc() return HTTPError(500, dumps({"error": e.message, "traceback": format_exc()})) -# TODO Better error codes on invalid input - -def callApi(api, func, *args, **kwargs): - if not hasattr(PYLOAD.EXTERNAL, func) or func.startswith("_"): - print "Invalid API call", func - return HTTPError(404, dumps("Not Found")) - - # 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 - if result is None: result = True - - return dumps(result) - -#post -> username, password +@route("/api/login") @route("/api/login", method="POST") def login(): add_header(response) - username = request.forms.get("username") - password = request.forms.get("password") + username = request.params.get("username") + password = request.params.get("password") user = PYLOAD.checkAuth(username, password, request.environ.get('REMOTE_ADDR', None)) diff --git a/pavement.py b/pavement.py index 484501fab..b10ebfd0c 100644 --- a/pavement.py +++ b/pavement.py @@ -69,7 +69,7 @@ setup( }, #setup_requires=["setuptools_hg"], test_suite='nose.collector', - tests_require=['nose', 'websocket-client >= 0.8.0'], + tests_require=['nose', 'websocket-client >= 0.8.0', 'requests >= 1.2.2'], # scripts=['pyload', 'pyload-cli'], entry_points={ 'console_scripts': [ @@ -22,7 +22,6 @@ CURRENT_VERSION = '0.4.9.9-dev' import __builtin__ from getopt import getopt, GetoptError -from imp import find_module import logging import logging.handlers import os diff --git a/tests/api/test_JSONBackend.py b/tests/api/test_JSONBackend.py index b6509a52a..e99b97827 100644 --- a/tests/api/test_JSONBackend.py +++ b/tests/api/test_JSONBackend.py @@ -1,23 +1,47 @@ # -*- coding: utf-8 -*- -from nose.tools import raises +from nose.tools import raises, assert_equal + +from requests.auth import HTTPBasicAuth +import requests + +import json from module.remote.apitypes import Forbidden from module.remote.JSONClient import JSONClient + class TestJSONBackend: + login = ("User", "pwhere") def setUp(self): self.client = JSONClient() def test_login(self): - self.client.login("User", "test") + self.client.login(*self.login) self.client.getServerVersion() self.client.logout() def test_wronglogin(self): ret = self.client.login("WrongUser", "wrongpw") - assert ret == False + assert ret is False + + + def test_httpauth(self): + # cheap http auth + ret = requests.get(self.client.URL + "/getServerVersion", auth=HTTPBasicAuth(*self.login)) + assert_equal(ret.status_code, 200) + assert ret.text + + def test_jsonbody(self): + payload = {'section': 'webinterface', 'option': 'port'} + headers = {'content-type': 'application/json'} + + ret = requests.get(self.client.URL + "/getConfigValue", headers=headers, + auth=HTTPBasicAuth(*self.login), data=json.dumps(payload)) + + assert_equal(ret.status_code, 200) + assert ret.text @raises(Forbidden) def test_access(self): @@ -25,7 +49,5 @@ class TestJSONBackend: @raises(AttributeError) def test_unknown_method(self): - self.client.login("User", "test") - self.client.sdfdsg() - - # TODO: test raising of exceptions, also in WSBackend
\ No newline at end of file + self.client.login(*self.login) + self.client.sdfdsg()
\ No newline at end of file |