diff options
author | bendikro <bro.devel+deluge@gmail.com> | 2015-12-15 18:26:55 +0100 |
---|---|---|
committer | bendikro <bro.devel+deluge@gmail.com> | 2016-04-10 00:10:53 +0200 |
commit | d58960d723f8fac5e090ae08ff7f6d680b736395 (patch) | |
tree | 874cd36c333caaa900db2884291f0a08211ea9b1 /deluge/ui | |
parent | bcc1db12e5763e9cc6b2ad5379b16381221f52cb (diff) | |
download | deluge-d58960d723f8fac5e090ae08ff7f6d680b736395.tar.gz deluge-d58960d723f8fac5e090ae08ff7f6d680b736395.tar.bz2 deluge-d58960d723f8fac5e090ae08ff7f6d680b736395.zip |
[Tests] [Web] Make JSON independent of Web component
* Implement JSONTestCase in test_json_api.py
* Implement WebAPITestCase test case in test_web_api.py
Diffstat (limited to 'deluge/ui')
-rw-r--r-- | deluge/ui/web/auth.py | 41 | ||||
-rw-r--r-- | deluge/ui/web/json_api.py | 174 | ||||
-rw-r--r-- | deluge/ui/web/server.py | 21 |
3 files changed, 91 insertions, 145 deletions
diff --git a/deluge/ui/web/auth.py b/deluge/ui/web/auth.py index d90273aea..ae6b11da5 100644 --- a/deluge/ui/web/auth.py +++ b/deluge/ui/web/auth.py @@ -16,7 +16,6 @@ from email.utils import formatdate from twisted.internet.task import LoopingCall -from deluge import component from deluge.common import utf8_encoded log = logging.getLogger(__name__) @@ -79,9 +78,10 @@ class Auth(JSONComponent): The component that implements authentification into the JSON interface. """ - def __init__(self): + def __init__(self, config): super(Auth, self).__init__("Auth") self.worker = LoopingCall(self._clean_sessions) + self.config = config def start(self): self.worker.start(5) @@ -90,19 +90,18 @@ class Auth(JSONComponent): self.worker.stop() def _clean_sessions(self): - config = component.get("DelugeWeb").config - session_ids = config["sessions"].keys() + session_ids = self.config["sessions"].keys() now = time.gmtime() for session_id in session_ids: - session = config["sessions"][session_id] + session = self.config["sessions"][session_id] if "expires" not in session: - del config["sessions"][session_id] + del self.config["sessions"][session_id] continue if time.gmtime(session["expires"]) < now: - del config["sessions"][session_id] + del self.config["sessions"][session_id] continue def _create_session(self, request, login='admin'): @@ -117,21 +116,18 @@ class Auth(JSONComponent): m.update(os.urandom(32)) session_id = m.hexdigest() - config = component.get("DelugeWeb").config - - expires, expires_str = make_expires(config["session_timeout"]) + expires, expires_str = make_expires(self.config["session_timeout"]) checksum = str(make_checksum(session_id)) request.addCookie('_session_id', session_id + checksum, path=request.base + "json", expires=expires_str) log.debug("Creating session for %s", login) - config = component.get("DelugeWeb").config - if isinstance(config["sessions"], list): - config.config["sessions"] = {} + if isinstance(self.config["sessions"], list): + self.config["sessions"] = {} - config["sessions"][session_id] = { + self.config["sessions"][session_id] = { "login": login, "level": AUTH_LEVEL_ADMIN, "expires": expires @@ -139,7 +135,7 @@ class Auth(JSONComponent): return True def check_password(self, password): - config = component.get("DelugeWeb").config + config = self.config if "pwd_md5" in config.config: # We are using the 1.2-dev auth method log.debug("Received a password via the 1.2-dev auth method") @@ -206,16 +202,15 @@ class Auth(JSONComponent): :raises: Exception """ - config = component.get("DelugeWeb").config session_id = get_session_id(request.getCookie("_session_id")) - if session_id not in config["sessions"]: + if session_id not in self.config["sessions"]: auth_level = AUTH_LEVEL_NONE session_id = None else: - session = config["sessions"][session_id] + session = self.config["sessions"][session_id] auth_level = session["level"] - expires, expires_str = make_expires(config["session_timeout"]) + expires, expires_str = make_expires(self.config["session_timeout"]) session["expires"] = expires _session_id = request.getCookie("_session_id") @@ -253,9 +248,8 @@ class Auth(JSONComponent): salt = hashlib.sha1(os.urandom(32)).hexdigest() s = hashlib.sha1(salt) s.update(utf8_encoded(new_password)) - config = component.get("DelugeWeb").config - config["pwd_salt"] = salt - config["pwd_sha1"] = s.hexdigest() + self.config["pwd_salt"] = salt + self.config["pwd_sha1"] = s.hexdigest() return True @export @@ -290,8 +284,7 @@ class Auth(JSONComponent): :param session_id: the id for the session to remove :type session_id: string """ - config = component.get("DelugeWeb").config - del config["sessions"][__request__.session_id] + del self.config["sessions"][__request__.session_id] return True @export(AUTH_LEVEL_NONE) diff --git a/deluge/ui/web/json_api.py b/deluge/ui/web/json_api.py index 7e63d7e81..af65af545 100644 --- a/deluge/ui/web/json_api.py +++ b/deluge/ui/web/json_api.py @@ -16,14 +16,12 @@ import shutil import tempfile import time from types import FunctionType -from urllib import unquote_plus from twisted.internet import defer, reactor from twisted.internet.defer import Deferred, DeferredList from twisted.web import http, resource, server -from deluge import component, httpdownloader -from deluge.common import is_magnet +from deluge import common, component, httpdownloader from deluge.configmanager import ConfigManager, get_config_dir from deluge.ui import common as uicommon from deluge.ui.client import Client, client @@ -104,47 +102,6 @@ class JSON(resource.Resource, component.Component): return methods return client.daemon.get_method_list().addCallback(on_get_methods) - def connect(self, host="localhost", port=58846, username="", password=""): - """ - Connects the client to a daemon - """ - d = client.connect(host, port, username, password) - - def on_client_connected(connection_id): - """ - Handles the client successfully connecting to the daemon and - invokes retrieving the method names. - """ - d = self.get_remote_methods() - component.get("Web.PluginManager").start() - component.get("Web").start() - return d - - return d.addCallback(on_client_connected) - - def disable(self): - if not client.is_classicmode(): - client.disconnect() - client.set_disconnect_callback(None) - - def enable(self): - if not client.is_classicmode(): - client.set_disconnect_callback(self._on_client_disconnect) - client.register_event_handler("PluginEnabledEvent", self.get_remote_methods) - client.register_event_handler("PluginDisabledEvent", self.get_remote_methods) - if component.get("DelugeWeb").config["default_daemon"]: - # Sort out getting the default daemon here - default = component.get("DelugeWeb").config["default_daemon"] - host = component.get("Web").get_host(default) - if host: - self.connect(*host[1:]) - else: - self.connect() - - def _on_client_disconnect(self, *args): - component.get("Web.PluginManager").stop() - component.get("Web").stop() - def _exec_local(self, method, params, request): """ Handles executing all local methods. @@ -180,9 +137,8 @@ class JSON(resource.Resource, component.Component): """ try: request.json = json.loads(request.json) - except ValueError: + except (ValueError, TypeError): raise JSONException("JSON not decodable") - if "method" not in request.json or "id" not in request.json or \ "params" not in request.json: raise JSONException("Invalid JSON request") @@ -257,12 +213,12 @@ class JSON(resource.Resource, component.Component): request.setHeader("content-type", "application/x-json") request.write(compress(response, request)) request.finish() + return server.NOT_DONE_YET def render(self, request): """ Handles all the POST requests made to the /json controller. """ - if request.method != "POST": request.setResponseCode(http.NOT_ALLOWED) request.finish() @@ -421,7 +377,30 @@ class WebApi(JSONComponent): except KeyError: self.sessionproxy = SessionProxy() - def get_host(self, host_id): + def disable(self): + if not client.is_classicmode(): + client.disconnect() + client.set_disconnect_callback(None) + + def enable(self): + if not client.is_classicmode(): + client.set_disconnect_callback(self._on_client_disconnect) + client.register_event_handler("PluginEnabledEvent", self._json.get_remote_methods) + client.register_event_handler("PluginDisabledEvent", self._json.get_remote_methods) + if component.get("DelugeWeb").config["default_daemon"]: + # Sort out getting the default daemon here + default = component.get("DelugeWeb").config["default_daemon"] + host = component.get("Web")._get_host(default) + if host: + self._connect_daemon(*host[1:]) + else: + self._connect_daemon() + + def _on_client_disconnect(self, *args): + component.get("Web.PluginManager").stop() + self.stop() + + def _get_host(self, host_id): """ Return the information about a host @@ -442,6 +421,24 @@ class WebApi(JSONComponent): self.core_config.stop() self.sessionproxy.stop() + def _connect_daemon(self, host="localhost", port=58846, username="", password=""): + """ + Connects the client to a daemon + """ + d = client.connect(host, port, username, password) + + def on_client_connected(connection_id): + """ + Handles the client successfully connecting to the daemon and + invokes retrieving the method names. + """ + d = self._json.get_remote_methods() + component.get("Web.PluginManager").start() + self.start() + return d + + return d.addCallback(on_client_connected) + @export def connect(self, host_id): """ @@ -452,16 +449,10 @@ class WebApi(JSONComponent): :returns: the methods the daemon supports :rtype: list """ - d = Deferred() - - def on_connected(methods): - d.callback(methods) - host = self.get_host(host_id) + host = self._get_host(host_id) if host: - self._json.connect(*host[1:]).addCallback(on_connected) - else: - return defer.fail(Exception("Bad host id")) - return d + return self._connect_daemon(*host[1:]) + return defer.fail(Exception("Bad host id")) @export def connected(self): @@ -478,8 +469,12 @@ class WebApi(JSONComponent): """ Disconnect the web interface from the connected daemon. """ - client.disconnect() - return True + d = client.disconnect() + + def on_disconnect(reason): + return str(reason) + d.addCallback(on_disconnect) + return d @export def update_ui(self, keys, filter_dict): @@ -677,47 +672,7 @@ class WebApi(JSONComponent): @export def get_magnet_info(self, uri): - """ - Return information about a magnet link. - - :param uri: the magnet link - :type uri: string - - :returns: information about the magnet link: - - :: - - { - "name": the torrent name, - "info_hash": the torrents info_hash, - "files_tree": empty value for magnet links - } - - :rtype: dictionary - """ - magnet_scheme = 'magnet:?' - xt_param = 'xt=urn:btih:' - dn_param = 'dn=' - if uri.startswith(magnet_scheme): - name = None - info_hash = None - for param in uri[len(magnet_scheme):].split('&'): - if param.startswith(xt_param): - xt_hash = param[len(xt_param):] - if len(xt_hash) == 32: - info_hash = base64.b32decode(xt_hash).encode("hex") - elif len(xt_hash) == 40: - info_hash = xt_hash - else: - break - elif param.startswith(dn_param): - name = unquote_plus(param[len(dn_param):]) - - if info_hash: - if not name: - name = info_hash - return {"name": name, "info_hash": info_hash, "files_tree": ''} - return False + return common.get_magnet_info(uri) @export def add_torrents(self, torrents): @@ -737,7 +692,7 @@ class WebApi(JSONComponent): """ for torrent in torrents: - if is_magnet(torrent["path"]): + if common.is_magnet(torrent["path"]): log.info("Adding torrent from magnet uri `%s` with options `%r`", torrent["path"], torrent["options"]) client.core.add_torrent_magnet(torrent["path"], torrent["options"]) @@ -769,7 +724,7 @@ class WebApi(JSONComponent): return host_id, host, port, status, info try: - host_id, host, port, user, password = self.get_host(host_id) + host_id, host, port, user, password = self._get_host(host_id) except TypeError: host = None port = None @@ -808,8 +763,8 @@ class WebApi(JSONComponent): @export def start_daemon(self, port): """ - Starts a local daemon. - """ + Starts a local daemon. + """ client.start_daemon(port, get_config_dir()) @export @@ -821,7 +776,7 @@ class WebApi(JSONComponent): :type host_id: string """ main_deferred = Deferred() - host = self.get_host(host_id) + host = self._get_host(host_id) if not host: main_deferred.callback((False, _("Daemon doesn't exist"))) return main_deferred @@ -864,7 +819,7 @@ class WebApi(JSONComponent): # Check to see if there is already an entry for this host and return # if thats the case for entry in self.host_list["hosts"]: - if (entry[0], entry[1], entry[2]) == (host, port, username): + if (entry[1], entry[2], entry[3]) == (host, port, username): return (False, "Host already in the list") try: @@ -877,7 +832,7 @@ class WebApi(JSONComponent): self.host_list["hosts"].append([connection_id, host, port, username, password]) self.host_list.save() - return (True,) + return True, connection_id @export def remove_host(self, connection_id): @@ -887,7 +842,7 @@ class WebApi(JSONComponent): :param host_id: the hash id of the host :type host_id: string """ - host = self.get_host(connection_id) + host = self._get_host(connection_id) if host is None: return False @@ -919,6 +874,9 @@ class WebApi(JSONComponent): """ web_config = component.get("DelugeWeb").config for key in config.keys(): + if key in ["sessions", "pwd_salt", "pwd_sha1"]: + log.warn("Ignored attempt to overwrite web config key '%s'" % key) + continue if isinstance(config[key], basestring): config[key] = config[key].encode("utf8") web_config[key] = config[key] diff --git a/deluge/ui/web/server.py b/deluge/ui/web/server.py index d4d49f37e..31546401d 100644 --- a/deluge/ui/web/server.py +++ b/deluge/ui/web/server.py @@ -16,7 +16,7 @@ import tempfile from OpenSSL.crypto import FILETYPE_PEM from twisted.application import internet, service -from twisted.internet import defer, error, reactor +from twisted.internet import defer, reactor from twisted.internet.ssl import SSL, Certificate, CertificateOptions, KeyPair from twisted.web import http, resource, server, static @@ -533,7 +533,6 @@ class DelugeWeb(component.Component): def __init__(self): super(DelugeWeb, self).__init__("DelugeWeb") self.config = configmanager.ConfigManager("web.conf", CONFIG_DEFAULTS) - self.socket = None self.top_level = TopLevel() self.site = server.Site(self.top_level) @@ -544,7 +543,7 @@ class DelugeWeb(component.Component): self.cert = self.config["cert"] self.base = self.config["base"] self.web_api = WebApi() - self.auth = Auth() + self.auth = Auth(self.config) # Initalize the plugins self.plugins = PluginManager() @@ -568,17 +567,15 @@ class DelugeWeb(component.Component): return 1 SetConsoleCtrlHandler(win_handler) - def start(self, start_reactor=True): + def start(self): log.info("%s %s.", _("Starting server in PID"), os.getpid()) if self.https: self.start_ssl() else: self.start_normal() - component.get("JSON").enable() - - if start_reactor: - reactor.run() + component.get("Web").enable() + reactor.run() def start_normal(self): self.socket = reactor.listenTCP(self.port, self.site, interface=self.interface) @@ -600,7 +597,7 @@ class DelugeWeb(component.Component): def stop(self): log.info("Shutting down webserver") - component.get("JSON").disable() + component.get("Web").disable() self.plugins.disable_plugins() log.debug("Saving configuration file") @@ -616,10 +613,8 @@ class DelugeWeb(component.Component): def shutdown(self, *args): self.stop() - try: - reactor.stop() - except error.ReactorNotRunning: - log.debug("Reactor not running") + reactor.stop() + if __name__ == "__builtin__": deluge_web = DelugeWeb() |