diff --git a/lbrynet/daemon/Daemon.py b/lbrynet/daemon/Daemon.py index 9f32b289c..786f33151 100644 --- a/lbrynet/daemon/Daemon.py +++ b/lbrynet/daemon/Daemon.py @@ -1018,6 +1018,7 @@ class Daemon(AuthJSONRPCServer): """ return self._render_response(sorted([command for command in self.callable_methods.keys()])) + @AuthJSONRPCServer.requires("wallet") def jsonrpc_wallet_balance(self, address=None, include_unconfirmed=False): """ Return the balance of the wallet @@ -1039,6 +1040,7 @@ class Daemon(AuthJSONRPCServer): return self._render_response(float( self.wallet.get_address_balance(address, include_unconfirmed))) + @AuthJSONRPCServer.requires("wallet") @defer.inlineCallbacks def jsonrpc_wallet_unlock(self, password): """ @@ -1065,6 +1067,7 @@ class Daemon(AuthJSONRPCServer): response = yield self._render_response(result) defer.returnValue(response) + @AuthJSONRPCServer.requires("wallet", wallet=lambda wallet: wallet.check_locked()) @defer.inlineCallbacks def jsonrpc_wallet_decrypt(self): """ @@ -1084,6 +1087,7 @@ class Daemon(AuthJSONRPCServer): response = yield self._render_response(result) defer.returnValue(response) + @AuthJSONRPCServer.requires("wallet", wallet=lambda wallet: wallet.check_locked()) @defer.inlineCallbacks def jsonrpc_wallet_encrypt(self, new_password): """ @@ -1124,6 +1128,7 @@ class Daemon(AuthJSONRPCServer): reactor.callLater(0.1, reactor.fireSystemEvent, "shutdown") defer.returnValue(response) + @AuthJSONRPCServer.requires("file_manager") @defer.inlineCallbacks def jsonrpc_file_list(self, sort=None, **kwargs): """ @@ -1195,6 +1200,7 @@ class Daemon(AuthJSONRPCServer): response = yield self._render_response(result) defer.returnValue(response) + @AuthJSONRPCServer.requires("wallet") @defer.inlineCallbacks def jsonrpc_resolve_name(self, name, force=False): """ @@ -1220,6 +1226,7 @@ class Daemon(AuthJSONRPCServer): else: defer.returnValue(metadata) + @AuthJSONRPCServer.requires("wallet") @defer.inlineCallbacks def jsonrpc_claim_show(self, txid=None, nout=None, claim_id=None): """ @@ -1265,6 +1272,7 @@ class Daemon(AuthJSONRPCServer): response = yield self._render_response(claim_results) defer.returnValue(response) + @AuthJSONRPCServer.requires("wallet") @defer.inlineCallbacks def jsonrpc_resolve(self, force=False, uri=None, uris=[]): """ @@ -1355,6 +1363,7 @@ class Daemon(AuthJSONRPCServer): response = yield self._render_response(results) defer.returnValue(response) + @AuthJSONRPCServer.requires("wallet", "file_manager", "session", wallet=lambda wallet: wallet.check_locked()) @defer.inlineCallbacks def jsonrpc_get(self, uri, file_name=None, timeout=None): """ @@ -1443,6 +1452,7 @@ class Daemon(AuthJSONRPCServer): response = yield self._render_response(result) defer.returnValue(response) + @AuthJSONRPCServer.requires("file_manager") @defer.inlineCallbacks def jsonrpc_file_set_status(self, status, **kwargs): """ @@ -1483,6 +1493,7 @@ class Daemon(AuthJSONRPCServer): response = yield self._render_response(msg) defer.returnValue(response) + @AuthJSONRPCServer.requires("file_manager") @defer.inlineCallbacks def jsonrpc_file_delete(self, delete_from_download_dir=False, delete_all=False, **kwargs): """ @@ -1543,6 +1554,7 @@ class Daemon(AuthJSONRPCServer): response = yield self._render_response(result) defer.returnValue(response) + @AuthJSONRPCServer.requires("wallet", "session", wallet=lambda wallet: wallet.check_locked()) @defer.inlineCallbacks def jsonrpc_stream_cost_estimate(self, uri, size=None): """ @@ -1563,6 +1575,7 @@ class Daemon(AuthJSONRPCServer): cost = yield self.get_est_cost(uri, size) defer.returnValue(cost) + @AuthJSONRPCServer.requires("wallet", wallet=lambda wallet: wallet.check_locked()) @defer.inlineCallbacks def jsonrpc_channel_new(self, channel_name, amount): """ @@ -1619,6 +1632,7 @@ class Daemon(AuthJSONRPCServer): response = yield self._render_response(result) defer.returnValue(response) + @AuthJSONRPCServer.requires("wallet") @defer.inlineCallbacks def jsonrpc_channel_list(self): """ @@ -1639,6 +1653,7 @@ class Daemon(AuthJSONRPCServer): response = yield self._render_response(result) defer.returnValue(response) + @AuthJSONRPCServer.requires("wallet") @AuthJSONRPCServer.deprecated("channel_list") def jsonrpc_channel_list_mine(self): """ @@ -1656,6 +1671,7 @@ class Daemon(AuthJSONRPCServer): return self.jsonrpc_channel_list() + @AuthJSONRPCServer.requires("wallet") @defer.inlineCallbacks def jsonrpc_channel_export(self, claim_id): """ @@ -1674,6 +1690,7 @@ class Daemon(AuthJSONRPCServer): result = yield self.wallet.export_certificate_info(claim_id) defer.returnValue(result) + @AuthJSONRPCServer.requires("wallet") @defer.inlineCallbacks def jsonrpc_channel_import(self, serialized_certificate_info): """ @@ -1692,6 +1709,7 @@ class Daemon(AuthJSONRPCServer): result = yield self.wallet.import_certificate_info(serialized_certificate_info) defer.returnValue(result) + @AuthJSONRPCServer.requires("wallet", "file_manager", "session", wallet=lambda wallet: wallet.check_locked()) @defer.inlineCallbacks def jsonrpc_publish(self, name, bid, metadata=None, file_path=None, fee=None, title=None, description=None, author=None, language=None, license=None, @@ -1903,6 +1921,7 @@ class Daemon(AuthJSONRPCServer): response = yield self._render_response(result) defer.returnValue(response) + @AuthJSONRPCServer.requires("wallet", wallet=lambda wallet: wallet.check_locked()) @defer.inlineCallbacks def jsonrpc_claim_abandon(self, claim_id=None, txid=None, nout=None): """ @@ -1935,6 +1954,7 @@ class Daemon(AuthJSONRPCServer): self.analytics_manager.send_claim_action('abandon') defer.returnValue(result) + @AuthJSONRPCServer.requires("wallet", wallet=lambda wallet: wallet.check_locked()) @defer.inlineCallbacks def jsonrpc_claim_new_support(self, name, claim_id, amount): """ @@ -1962,6 +1982,7 @@ class Daemon(AuthJSONRPCServer): self.analytics_manager.send_claim_action('new_support') defer.returnValue(result) + @AuthJSONRPCServer.requires("wallet", wallet=lambda wallet: wallet.check_locked()) @defer.inlineCallbacks def jsonrpc_claim_renew(self, outpoint=None, height=None): """ @@ -2004,6 +2025,7 @@ class Daemon(AuthJSONRPCServer): result = yield self.wallet.claim_renew_all_before_expiration(height) defer.returnValue(result) + @AuthJSONRPCServer.requires("wallet", wallet=lambda wallet: wallet.check_locked()) @defer.inlineCallbacks def jsonrpc_claim_send_to_address(self, claim_id, address, amount=None): """ @@ -2036,6 +2058,7 @@ class Daemon(AuthJSONRPCServer): defer.returnValue(response) # TODO: claim_list_mine should be merged into claim_list, but idk how to authenticate it -Grin + @AuthJSONRPCServer.requires("wallet") def jsonrpc_claim_list_mine(self): """ List my name claims @@ -2073,6 +2096,7 @@ class Daemon(AuthJSONRPCServer): d.addCallback(lambda claims: self._render_response(claims)) return d + @AuthJSONRPCServer.requires("wallet") @defer.inlineCallbacks def jsonrpc_claim_list(self, name): """ @@ -2111,6 +2135,7 @@ class Daemon(AuthJSONRPCServer): sort_claim_results(claims['claims']) defer.returnValue(claims) + @AuthJSONRPCServer.requires("wallet") @defer.inlineCallbacks def jsonrpc_claim_list_by_channel(self, page=0, page_size=10, uri=None, uris=[]): """ @@ -2200,6 +2225,7 @@ class Daemon(AuthJSONRPCServer): response = yield self._render_response(results) defer.returnValue(response) + @AuthJSONRPCServer.requires("wallet") def jsonrpc_transaction_list(self): """ List transactions belonging to wallet @@ -2261,6 +2287,7 @@ class Daemon(AuthJSONRPCServer): d.addCallback(lambda r: self._render_response(r)) return d + @AuthJSONRPCServer.requires("wallet") def jsonrpc_transaction_show(self, txid): """ Get a decoded transaction from a txid @@ -2279,6 +2306,7 @@ class Daemon(AuthJSONRPCServer): d.addCallback(lambda r: self._render_response(r)) return d + @AuthJSONRPCServer.requires("wallet") def jsonrpc_wallet_is_address_mine(self, address): """ Checks if an address is associated with the current wallet. @@ -2297,6 +2325,7 @@ class Daemon(AuthJSONRPCServer): d.addCallback(lambda is_mine: self._render_response(is_mine)) return d + @AuthJSONRPCServer.requires("wallet") def jsonrpc_wallet_public_key(self, address): """ Get public key from wallet address @@ -2316,6 +2345,7 @@ class Daemon(AuthJSONRPCServer): d.addCallback(lambda r: self._render_response(r)) return d + @AuthJSONRPCServer.requires("wallet") @defer.inlineCallbacks def jsonrpc_wallet_list(self): """ @@ -2335,6 +2365,7 @@ class Daemon(AuthJSONRPCServer): response = yield self._render_response(addresses) defer.returnValue(response) + @AuthJSONRPCServer.requires("wallet") def jsonrpc_wallet_new_address(self): """ Generate a new wallet address @@ -2358,6 +2389,7 @@ class Daemon(AuthJSONRPCServer): d.addCallback(lambda address: self._render_response(address)) return d + @AuthJSONRPCServer.requires("wallet") def jsonrpc_wallet_unused_address(self): """ Return an address containing no balance, will create @@ -2382,6 +2414,7 @@ class Daemon(AuthJSONRPCServer): d.addCallback(lambda address: self._render_response(address)) return d + @AuthJSONRPCServer.requires("wallet", wallet=lambda wallet: wallet.check_locked()) @AuthJSONRPCServer.deprecated("wallet_send") @defer.inlineCallbacks def jsonrpc_send_amount_to_address(self, amount, address): @@ -2411,6 +2444,7 @@ class Daemon(AuthJSONRPCServer): self.analytics_manager.send_credits_sent() defer.returnValue(True) + @AuthJSONRPCServer.requires("wallet", wallet=lambda wallet: wallet.check_locked()) @defer.inlineCallbacks def jsonrpc_wallet_send(self, amount, address=None, claim_id=None): """ @@ -2459,6 +2493,7 @@ class Daemon(AuthJSONRPCServer): self.analytics_manager.send_claim_action('new_support') defer.returnValue(result) + @AuthJSONRPCServer.requires("wallet", wallet=lambda wallet: wallet.check_locked()) @defer.inlineCallbacks def jsonrpc_wallet_prefill_addresses(self, num_addresses, amount, no_broadcast=False): """ @@ -2489,6 +2524,7 @@ class Daemon(AuthJSONRPCServer): tx['broadcast'] = broadcast defer.returnValue(tx) + @AuthJSONRPCServer.requires("wallet") @defer.inlineCallbacks def jsonrpc_utxo_list(self): """ @@ -2528,6 +2564,7 @@ class Daemon(AuthJSONRPCServer): defer.returnValue(unspent) + @AuthJSONRPCServer.requires("wallet") def jsonrpc_block_show(self, blockhash=None, height=None): """ Get contents of a block @@ -2555,6 +2592,7 @@ class Daemon(AuthJSONRPCServer): d.addCallback(lambda r: self._render_response(r)) return d + @AuthJSONRPCServer.requires("wallet", "session", wallet=lambda wallet: wallet.check_locked()) @defer.inlineCallbacks def jsonrpc_blob_get(self, blob_hash, timeout=None, encoding=None, payment_rate_manager=None): """ @@ -2598,6 +2636,7 @@ class Daemon(AuthJSONRPCServer): response = yield self._render_response(result) defer.returnValue(response) + @AuthJSONRPCServer.requires("session") @defer.inlineCallbacks def jsonrpc_blob_delete(self, blob_hash): """ @@ -2625,6 +2664,7 @@ class Daemon(AuthJSONRPCServer): response = yield self._render_response("Deleted %s" % blob_hash) defer.returnValue(response) + @AuthJSONRPCServer.requires("dht") @defer.inlineCallbacks def jsonrpc_peer_list(self, blob_hash, timeout=None): """ @@ -2663,6 +2703,7 @@ class Daemon(AuthJSONRPCServer): ] defer.returnValue(results) + @AuthJSONRPCServer.requires("database") @defer.inlineCallbacks def jsonrpc_blob_announce(self, blob_hash=None, stream_hash=None, sd_hash=None): """ @@ -2699,6 +2740,7 @@ class Daemon(AuthJSONRPCServer): response = yield self._render_response(True) defer.returnValue(response) + @AuthJSONRPCServer.requires("file_manager") @defer.inlineCallbacks def jsonrpc_file_reflect(self, **kwargs): """ @@ -2734,6 +2776,7 @@ class Daemon(AuthJSONRPCServer): results = yield reupload.reflect_file(lbry_file, reflector_server=reflector_server) defer.returnValue(results) + @AuthJSONRPCServer.requires("database", "session", "wallet") @defer.inlineCallbacks def jsonrpc_blob_list(self, uri=None, stream_hash=None, sd_hash=None, needed=None, finished=None, page_size=None, page=None): @@ -2797,6 +2840,7 @@ class Daemon(AuthJSONRPCServer): response = yield self._render_response(blob_hashes_for_return) defer.returnValue(response) + @AuthJSONRPCServer.requires("session") def jsonrpc_blob_reflect(self, blob_hashes, reflector_server=None): """ Reflects specified blobs @@ -2815,6 +2859,7 @@ class Daemon(AuthJSONRPCServer): d.addCallback(lambda r: self._render_response(r)) return d + @AuthJSONRPCServer.requires("session") def jsonrpc_blob_reflect_all(self): """ Reflects all saved blobs @@ -2834,6 +2879,7 @@ class Daemon(AuthJSONRPCServer): d.addCallback(lambda r: self._render_response(r)) return d + @AuthJSONRPCServer.requires("dht") @defer.inlineCallbacks def jsonrpc_peer_ping(self, node_id): """ @@ -2863,6 +2909,7 @@ class Daemon(AuthJSONRPCServer): result = {'error': 'ping timeout'} defer.returnValue(result) + @AuthJSONRPCServer.requires("dht") def jsonrpc_routing_table_get(self): """ Get DHT routing information @@ -2938,6 +2985,8 @@ class Daemon(AuthJSONRPCServer): result['node_id'] = self.dht_node.node_id.encode('hex') return self._render_response(result) + # the single peer downloader needs wallet access + @AuthJSONRPCServer.requires("dht", "wallet", wallet=lambda wallet: wallet.check_locked()) def jsonrpc_blob_availability(self, blob_hash, search_timeout=None, blob_timeout=None): """ Get blob availability @@ -2962,6 +3011,7 @@ class Daemon(AuthJSONRPCServer): return self._blob_availability(blob_hash, search_timeout, blob_timeout) + @AuthJSONRPCServer.requires("session", "wallet", "dht", wallet=lambda wallet: wallet.check_locked()) @AuthJSONRPCServer.deprecated("stream_availability") def jsonrpc_get_availability(self, uri, sd_timeout=None, peer_timeout=None): """ @@ -2982,6 +3032,7 @@ class Daemon(AuthJSONRPCServer): return self.jsonrpc_stream_availability(uri, peer_timeout, sd_timeout) + @AuthJSONRPCServer.requires("session", "wallet", "dht", wallet=lambda wallet: wallet.check_locked()) @defer.inlineCallbacks def jsonrpc_stream_availability(self, uri, search_timeout=None, blob_timeout=None): """ diff --git a/lbrynet/daemon/auth/server.py b/lbrynet/daemon/auth/server.py index a0d365a35..72d7e7b6b 100644 --- a/lbrynet/daemon/auth/server.py +++ b/lbrynet/daemon/auth/server.py @@ -4,6 +4,7 @@ import json import inspect from decimal import Decimal +from functools import wraps from zope.interface import implements from twisted.web import server, resource from twisted.internet import defer @@ -15,6 +16,7 @@ from traceback import format_exc from lbrynet import conf from lbrynet.core.Error import InvalidAuthenticationToken from lbrynet.core import utils +from lbrynet.core.Error import ComponentsNotStarted, ComponentStartConditionNotMet from lbrynet.daemon.auth.util import APIKey, get_auth_message from lbrynet.daemon.auth.client import LBRY_SECRET from lbrynet.undecorated import undecorated @@ -141,6 +143,31 @@ class AuthorizedBase(object): return f return _deprecated_wrapper + @staticmethod + def requires(*components, **component_conditionals): + def _wrap(fn): + @defer.inlineCallbacks + @wraps(fn) + def _inner(*args, **kwargs): + if component_conditionals: + for component_name, condition in component_conditionals.iteritems(): + if not callable(condition): + raise SyntaxError("The specified condition is invalid/not callable") + if args[0].component_manager.all_components_running(component_name): + if not (yield condition(args[0].component_manager.get_component(component_name))): + raise ComponentStartConditionNotMet( + "Not all conditions required to do this operation are met") + else: + raise ComponentsNotStarted("%s component is not setup.\nConditional cannot be checked" + % component_name) + if args[0].component_manager.all_components_running(*components): + result = yield fn(*args, **kwargs) + defer.returnValue(result) + else: + raise ComponentsNotStarted("Not all required components are set up:", components) + return _inner + return _wrap + class AuthJSONRPCServer(AuthorizedBase): """ @@ -149,7 +176,6 @@ class AuthJSONRPCServer(AuthorizedBase): API methods are named with a leading "jsonrpc_" Attributes: - allowed_during_startup (list): list of api methods that are callable before the server has finished startup sessions (dict): (dict): {: } callable_methods (dict): {: } @@ -416,9 +442,6 @@ class AuthJSONRPCServer(AuthorizedBase): def _verify_method_is_callable(self, function_path): if function_path not in self.callable_methods: raise UnknownAPIMethodError(function_path) - if not self.announced_startup: - if function_path not in self.allowed_during_startup: - raise NotAllowedDuringStartupError(function_path) def _get_jsonrpc_method(self, function_path): if function_path in self.deprecated_methods: