more cleaning up

This commit is contained in:
Jack 2016-09-21 21:36:06 -04:00
parent aecafbbebd
commit 4d79a7828c
7 changed files with 276 additions and 222 deletions

View file

@ -45,12 +45,9 @@ KNOWN_DHT_NODES = [('104.236.42.182', 4000),
POINTTRADER_SERVER = 'http://ec2-54-187-192-68.us-west-2.compute.amazonaws.com:2424'
#POINTTRADER_SERVER = 'http://127.0.0.1:2424'
if IS_DEVELOPMENT_VERSION:
SEARCH_SERVERS = ["http://107.170.207.64:50005"]
else:
SEARCH_SERVERS = ["http://lighthouse1.lbry.io:50005",
"http://lighthouse2.lbry.io:50005",
"http://lighthouse3.lbry.io:50005"]
SEARCH_SERVERS = ["http://lighthouse1.lbry.io:50005",
"http://lighthouse2.lbry.io:50005",
"http://lighthouse3.lbry.io:50005"]
REFLECTOR_SERVERS = [("reflector.lbry.io", 5566)]
@ -67,6 +64,9 @@ if os.name == "nt":
else:
ICON_PATH = "app.icns"
APP_NAME = "LBRY"
ORIGIN = "http://%s:%i" % (API_INTERFACE, API_PORT)
REFERER = "http://%s:%i/" % (API_INTERFACE, API_PORT)
API_CONNECTION_STRING = "http://%s:%i/%s" % (API_INTERFACE, API_PORT, API_ADDRESS)
UI_ADDRESS = "http://%s:%i" % (API_INTERFACE, API_PORT)
PROTOCOL_PREFIX = "lbry"
@ -88,11 +88,6 @@ CURRENCIES = {
'USD': {'type': 'fiat'},
}
ALLOWED_DURING_STARTUP = ['is_running', 'is_first_run',
'get_time_behind_blockchain', 'stop',
'daemon_status', 'get_start_notice',
'version', 'get_search_servers']
LOGGLY_TOKEN = 'LJEzATH4AzRgAwxjAP00LwZ2YGx3MwVgZTMuBQZ3MQuxLmOv'
ANALYTICS_ENDPOINT = 'https://api.segment.io/v1'

View file

@ -94,3 +94,6 @@ class InvalidHeaderError(Exception):
class InvalidAuthenticationToken(Exception):
pass
class SubhandlerError(Exception):
pass

View file

@ -40,7 +40,8 @@ from lbrynet.lbrynet_daemon.LBRYDownloader import GetStream
from lbrynet.lbrynet_daemon.LBRYPublisher import Publisher
from lbrynet.lbrynet_daemon.LBRYExchangeRateManager import ExchangeRateManager
from lbrynet.lbrynet_daemon.Lighthouse import LighthouseClient
from lbrynet.lbrynet_daemon.auth.server import LBRYJSONRPCServer, auth_required, authorizer
from lbrynet.lbrynet_daemon.auth.server import AuthJSONRPCServer
from lbrynet.lbrynet_daemon.auth.util import get_auth_message
from lbrynet.metadata.LBRYMetadata import Metadata, verify_name_characters
from lbrynet.core import log_support
from lbrynet.core import utils
@ -49,7 +50,7 @@ from lbrynet.lbrynet_console.LBRYSettings import LBRYSettings
from lbrynet.conf import MIN_BLOB_DATA_PAYMENT_RATE, DEFAULT_MAX_SEARCH_RESULTS, \
KNOWN_DHT_NODES, DEFAULT_MAX_KEY_FEE, DEFAULT_WALLET, \
DEFAULT_SEARCH_TIMEOUT, DEFAULT_CACHE_TIME, DEFAULT_UI_BRANCH, \
LOG_POST_URL, LOG_FILE_NAME, REFLECTOR_SERVERS, SEARCH_SERVERS, ALLOWED_DURING_STARTUP
LOG_POST_URL, LOG_FILE_NAME, REFLECTOR_SERVERS, SEARCH_SERVERS
from lbrynet.conf import DEFAULT_SD_DOWNLOAD_TIMEOUT
from lbrynet.conf import DEFAULT_TIMEOUT
from lbrynet.core.StreamDescriptor import StreamDescriptorIdentifier, download_sd_blob, BlobStreamDescriptorReader
@ -122,10 +123,8 @@ BAD_REQUEST = 400
NOT_FOUND = 404
OK_CODE = 200
# TODO add login credentials in a conf file
# TODO alert if your copy of a lbry file is out of date with the name record
REMOTE_SERVER = "www.google.com"
@ -134,14 +133,17 @@ class Parameters(object):
self.__dict__.update(kwargs)
@authorizer
class LBRYDaemon(LBRYJSONRPCServer):
class LBRYDaemon(AuthJSONRPCServer):
"""
LBRYnet daemon, a jsonrpc interface to lbry functions
"""
def __init__(self, root, wallet_type=None):
LBRYJSONRPCServer.__init__(self)
AuthJSONRPCServer.__init__(self)
self.allowed_during_startup = ['is_running', 'is_first_run',
'get_time_behind_blockchain', 'stop',
'daemon_status', 'get_start_notice',
'version', 'get_search_servers']
reactor.addSystemEventTrigger('before', 'shutdown', self._shutdown)
self.startup_status = STARTUP_STAGES[0]
@ -392,6 +394,17 @@ class LBRYDaemon(LBRYJSONRPCServer):
f.write("rpcpassword=" + password)
log.info("Done writing lbrycrd.conf")
@AuthJSONRPCServer.subhandler
def _exclude_lbrycrd_only_commands_from_lbryum_session(self, request):
request.content.seek(0, 0)
content = request.content.read()
parsed = jsonrpclib.loads(content)
function_path = parsed.get("method")
if self.wallet_type == "lbryum" and function_path in ['set_miner', 'get_miner_status']:
log.warning("Mining commands are not available in lbryum")
raise Exception("Command not available in lbryum")
return True
def setup(self, branch=DEFAULT_UI_BRANCH, user_specified=False, branch_specified=False, host_ui=True):
def _log_starting_vals():
log.info("Starting balance: " + str(self.session.wallet.wallet_balance))
@ -1337,7 +1350,7 @@ class LBRYDaemon(LBRYJSONRPCServer):
def _search(self, search):
return self.lighthouse_client.search(search)
@auth_required
@AuthJSONRPCServer.auth_required
def jsonrpc_is_running(self):
"""
Check if lbrynet daemon is running
@ -1354,7 +1367,6 @@ class LBRYDaemon(LBRYJSONRPCServer):
else:
return self._render_response(False, OK_CODE)
@auth_required
def jsonrpc_daemon_status(self):
"""
Get lbrynet daemon status information
@ -1386,10 +1398,8 @@ class LBRYDaemon(LBRYJSONRPCServer):
else:
r['message'] = "Catching up with the blockchain"
r['progress'] = 0
log.info("daemon status: " + str(r))
return self._render_response(r, OK_CODE)
@auth_required
def jsonrpc_is_first_run(self):
"""
Check if this is the first time lbrynet daemon has been run
@ -1410,7 +1420,6 @@ class LBRYDaemon(LBRYJSONRPCServer):
return d
@auth_required
def jsonrpc_get_start_notice(self):
"""
Get special message to be displayed at startup
@ -1430,7 +1439,6 @@ class LBRYDaemon(LBRYJSONRPCServer):
else:
self._render_response(self.startup_message, OK_CODE)
@auth_required
def jsonrpc_version(self):
"""
Get lbry version information
@ -1465,7 +1473,7 @@ class LBRYDaemon(LBRYJSONRPCServer):
log.info("Get version info: " + json.dumps(msg))
return self._render_response(msg, OK_CODE)
@auth_required
@AuthJSONRPCServer.auth_required
def jsonrpc_get_settings(self):
"""
Get lbrynet daemon settings
@ -1494,7 +1502,7 @@ class LBRYDaemon(LBRYJSONRPCServer):
log.info("Get daemon settings")
return self._render_response(self.session_settings, OK_CODE)
@auth_required
@AuthJSONRPCServer.auth_required
def jsonrpc_set_settings(self, p):
"""
Set lbrynet daemon settings
@ -1522,7 +1530,6 @@ class LBRYDaemon(LBRYJSONRPCServer):
return d
@auth_required
def jsonrpc_help(self, p=None):
"""
Function to retrieve docstring for API function
@ -1537,17 +1544,17 @@ class LBRYDaemon(LBRYJSONRPCServer):
"""
if not p:
return self._render_response(self._listFunctions(), OK_CODE)
return self._render_response(self.callable_methods.keys(), OK_CODE)
elif 'callable_during_start' in p.keys():
return self._render_response(ALLOWED_DURING_STARTUP, OK_CODE)
return self._render_response(self.allowed_during_startup, OK_CODE)
elif 'function' in p.keys():
func_path = p['function']
function = self._getFunction(func_path)
function = self.callable_methods.get(func_path)
return self._render_response(function.__doc__, OK_CODE)
else:
return self._render_response(self.jsonrpc_help.__doc__, OK_CODE)
@auth_required
@AuthJSONRPCServer.auth_required
def jsonrpc_get_balance(self):
"""
Get balance
@ -1561,7 +1568,6 @@ class LBRYDaemon(LBRYJSONRPCServer):
log.info("Get balance")
return self._render_response(float(self.session.wallet.wallet_balance), OK_CODE)
@auth_required
def jsonrpc_stop(self):
"""
Stop lbrynet-daemon
@ -1581,7 +1587,7 @@ class LBRYDaemon(LBRYJSONRPCServer):
return self._render_response("Shutting down", OK_CODE)
@auth_required
@AuthJSONRPCServer.auth_required
def jsonrpc_get_lbry_files(self):
"""
Get LBRY files
@ -1608,7 +1614,7 @@ class LBRYDaemon(LBRYJSONRPCServer):
return d
@auth_required
@AuthJSONRPCServer.auth_required
def jsonrpc_get_lbry_file(self, p):
"""
Get lbry file
@ -1638,7 +1644,6 @@ class LBRYDaemon(LBRYJSONRPCServer):
d.addCallback(lambda r: self._render_response(r, OK_CODE))
return d
@auth_required
def jsonrpc_resolve_name(self, p):
"""
Resolve stream info from a LBRY uri
@ -1660,7 +1665,6 @@ class LBRYDaemon(LBRYJSONRPCServer):
d.addCallbacks(lambda info: self._render_response(info, OK_CODE), lambda _: server.failure)
return d
@auth_required
def jsonrpc_get_claim_info(self, p):
"""
Resolve claim info from a LBRY uri
@ -1685,7 +1689,7 @@ class LBRYDaemon(LBRYJSONRPCServer):
d.addCallback(lambda r: self._render_response(r, OK_CODE))
return d
@auth_required
@AuthJSONRPCServer.auth_required
def _process_get_parameters(self, p):
"""Extract info from input parameters and fill in default values for `get` call."""
# TODO: this process can be abstracted s.t. each method
@ -1707,7 +1711,7 @@ class LBRYDaemon(LBRYJSONRPCServer):
name=name
)
@auth_required
@AuthJSONRPCServer.auth_required
def jsonrpc_get(self, p):
"""Download stream from a LBRY uri.
@ -1738,7 +1742,7 @@ class LBRYDaemon(LBRYJSONRPCServer):
d.addCallback(lambda message: self._render_response(message, OK_CODE))
return d
@auth_required
@AuthJSONRPCServer.auth_required
def jsonrpc_stop_lbry_file(self, p):
"""
Stop lbry file
@ -1764,7 +1768,7 @@ class LBRYDaemon(LBRYJSONRPCServer):
d.addCallback(lambda r: self._render_response(r, OK_CODE))
return d
@auth_required
@AuthJSONRPCServer.auth_required
def jsonrpc_start_lbry_file(self, p):
"""
Stop lbry file
@ -1789,7 +1793,6 @@ class LBRYDaemon(LBRYJSONRPCServer):
d.addCallback(lambda r: self._render_response(r, OK_CODE))
return d
@auth_required
def jsonrpc_get_est_cost(self, p):
"""
Get estimated cost for a lbry uri
@ -1811,7 +1814,7 @@ class LBRYDaemon(LBRYJSONRPCServer):
d.addCallback(lambda r: self._render_response(r, OK_CODE))
return d
@auth_required
@AuthJSONRPCServer.auth_required
def jsonrpc_search_nametrie(self, p):
"""
Search the nametrie for claims
@ -1848,7 +1851,7 @@ class LBRYDaemon(LBRYJSONRPCServer):
return d
@auth_required
@AuthJSONRPCServer.auth_required
def jsonrpc_delete_lbry_file(self, p):
"""
Delete a lbry file
@ -1878,7 +1881,7 @@ class LBRYDaemon(LBRYJSONRPCServer):
d.addCallback(lambda r: self._render_response(r, OK_CODE))
return d
@auth_required
@AuthJSONRPCServer.auth_required
def jsonrpc_publish(self, p):
"""
Make a new name claim and publish associated data to lbrynet
@ -1955,7 +1958,7 @@ class LBRYDaemon(LBRYJSONRPCServer):
return d
@auth_required
@AuthJSONRPCServer.auth_required
def jsonrpc_abandon_claim(self, p):
"""
Abandon a name and reclaim credits from the claim
@ -1982,7 +1985,7 @@ class LBRYDaemon(LBRYJSONRPCServer):
return d
@auth_required
@AuthJSONRPCServer.auth_required
def jsonrpc_abandon_name(self, p):
"""
DEPRECIATED, use abandon_claim
@ -1995,7 +1998,7 @@ class LBRYDaemon(LBRYJSONRPCServer):
return self.jsonrpc_abandon_claim(p)
@auth_required
@AuthJSONRPCServer.auth_required
def jsonrpc_support_claim(self, p):
"""
Support a name claim
@ -2015,7 +2018,7 @@ class LBRYDaemon(LBRYJSONRPCServer):
d.addCallback(lambda r: self._render_response(r, OK_CODE))
return d
@auth_required
@AuthJSONRPCServer.auth_required
def jsonrpc_get_name_claims(self):
"""
Get my name claims
@ -2039,7 +2042,6 @@ class LBRYDaemon(LBRYJSONRPCServer):
return d
@auth_required
def jsonrpc_get_claims_for_name(self, p):
"""
Get claims for a name
@ -2055,7 +2057,7 @@ class LBRYDaemon(LBRYJSONRPCServer):
d.addCallback(lambda r: self._render_response(r, OK_CODE))
return d
@auth_required
@AuthJSONRPCServer.auth_required
def jsonrpc_get_transaction_history(self):
"""
Get transaction history
@ -2070,7 +2072,6 @@ class LBRYDaemon(LBRYJSONRPCServer):
d.addCallback(lambda r: self._render_response(r, OK_CODE))
return d
@auth_required
def jsonrpc_get_transaction(self, p):
"""
Get a decoded transaction from a txid
@ -2087,7 +2088,7 @@ class LBRYDaemon(LBRYJSONRPCServer):
d.addCallback(lambda r: self._render_response(r, OK_CODE))
return d
@auth_required
@AuthJSONRPCServer.auth_required
def jsonrpc_address_is_mine(self, p):
"""
Checks if an address is associated with the current wallet.
@ -2105,7 +2106,7 @@ class LBRYDaemon(LBRYJSONRPCServer):
return d
@auth_required
@AuthJSONRPCServer.auth_required
def jsonrpc_get_public_key_from_wallet(self, p):
"""
Get public key from wallet address
@ -2120,7 +2121,6 @@ class LBRYDaemon(LBRYJSONRPCServer):
d = self.session.wallet.get_pub_keys(wallet)
d.addCallback(lambda r: self._render_response(r, OK_CODE))
@auth_required
def jsonrpc_get_time_behind_blockchain(self):
"""
Get number of blocks behind the blockchain
@ -2144,7 +2144,7 @@ class LBRYDaemon(LBRYJSONRPCServer):
return d
@auth_required
@AuthJSONRPCServer.auth_required
def jsonrpc_get_new_address(self):
"""
Generate a new wallet address
@ -2164,7 +2164,7 @@ class LBRYDaemon(LBRYJSONRPCServer):
d.addCallback(lambda address: self._render_response(address, OK_CODE))
return d
@auth_required
@AuthJSONRPCServer.auth_required
def jsonrpc_send_amount_to_address(self, p):
"""
Send credits to an address
@ -2189,7 +2189,6 @@ class LBRYDaemon(LBRYJSONRPCServer):
d.addCallback(lambda _: self._render_response(True, OK_CODE))
return d
@auth_required
def jsonrpc_get_best_blockhash(self):
"""
Get hash of most recent block
@ -2204,7 +2203,6 @@ class LBRYDaemon(LBRYJSONRPCServer):
d.addCallback(lambda r: self._render_response(r, OK_CODE))
return d
@auth_required
def jsonrpc_get_block(self, p):
"""
Get contents of a block
@ -2227,7 +2225,6 @@ class LBRYDaemon(LBRYJSONRPCServer):
d.addCallback(lambda r: self._render_response(r, OK_CODE))
return d
@auth_required
def jsonrpc_get_claims_for_tx(self, p):
"""
Get claims for tx
@ -2247,7 +2244,7 @@ class LBRYDaemon(LBRYJSONRPCServer):
d.addCallback(lambda r: self._render_response(r, OK_CODE))
return d
@auth_required
@AuthJSONRPCServer.auth_required
def jsonrpc_download_descriptor(self, p):
"""
Download and return a sd blob
@ -2264,7 +2261,6 @@ class LBRYDaemon(LBRYJSONRPCServer):
d.addCallbacks(lambda r: self._render_response(r, OK_CODE), lambda _: self._render_response(False, OK_CODE))
return d
@auth_required
def jsonrpc_get_nametrie(self):
"""
Get the nametrie
@ -2280,7 +2276,7 @@ class LBRYDaemon(LBRYJSONRPCServer):
d.addCallback(lambda r: self._render_response(r, OK_CODE))
return d
@auth_required
@AuthJSONRPCServer.auth_required
def jsonrpc_set_miner(self, p):
"""
Start of stop the miner, function only available when lbrycrd is set as the wallet
@ -2300,7 +2296,7 @@ class LBRYDaemon(LBRYJSONRPCServer):
d.addCallback(lambda r: self._render_response(r, OK_CODE))
return d
@auth_required
@AuthJSONRPCServer.auth_required
def jsonrpc_get_miner_status(self):
"""
Get status of miner
@ -2315,7 +2311,6 @@ class LBRYDaemon(LBRYJSONRPCServer):
d.addCallback(lambda r: self._render_response(r, OK_CODE))
return d
@auth_required
def jsonrpc_log(self, p):
"""
Log message
@ -2330,7 +2325,6 @@ class LBRYDaemon(LBRYJSONRPCServer):
log.info("API client log request: %s" % message)
return self._render_response(True, OK_CODE)
@auth_required
def jsonrpc_upload_log(self, p=None):
"""
Upload log
@ -2372,7 +2366,7 @@ class LBRYDaemon(LBRYJSONRPCServer):
d.addCallback(lambda _: self._render_response(True, OK_CODE))
return d
@auth_required
@AuthJSONRPCServer.auth_required
def jsonrpc_configure_ui(self, p):
"""
Configure the UI being hosted
@ -2397,7 +2391,7 @@ class LBRYDaemon(LBRYJSONRPCServer):
return d
@auth_required
@AuthJSONRPCServer.auth_required
def jsonrpc_reveal(self, p):
"""
Reveal a file or directory in file browser
@ -2417,7 +2411,7 @@ class LBRYDaemon(LBRYJSONRPCServer):
d.addCallback(lambda _: self._render_response(True, OK_CODE))
return d
@auth_required
@AuthJSONRPCServer.auth_required
def jsonrpc_get_peers_for_hash(self, p):
"""
Get peers for blob hash
@ -2435,7 +2429,7 @@ class LBRYDaemon(LBRYJSONRPCServer):
d.addCallback(lambda r: self._render_response(r, OK_CODE))
return d
@auth_required
@AuthJSONRPCServer.auth_required
def jsonrpc_announce_all_blobs_to_dht(self):
"""
Announce all blobs to the dht
@ -2450,7 +2444,7 @@ class LBRYDaemon(LBRYJSONRPCServer):
d.addCallback(lambda _: self._render_response("Announced", OK_CODE))
return d
@auth_required
@AuthJSONRPCServer.auth_required
def jsonrpc_reflect(self, p):
"""
Reflect a stream
@ -2467,7 +2461,7 @@ class LBRYDaemon(LBRYJSONRPCServer):
d.addCallbacks(lambda _: self._render_response(True, OK_CODE), lambda err: self._render_response(err.getTraceback(), OK_CODE))
return d
@auth_required
@AuthJSONRPCServer.auth_required
def jsonrpc_get_blob_hashes(self):
"""
Returns all blob hashes
@ -2482,7 +2476,7 @@ class LBRYDaemon(LBRYJSONRPCServer):
d.addCallback(lambda r: self._render_response(r, OK_CODE))
return d
@auth_required
@AuthJSONRPCServer.auth_required
def jsonrpc_reflect_all_blobs(self):
"""
Reflects all saved blobs
@ -2498,20 +2492,6 @@ class LBRYDaemon(LBRYJSONRPCServer):
d.addCallback(lambda r: self._render_response(r, OK_CODE))
return d
@auth_required
def jsonrpc_get_search_servers(self):
"""
Get list of lighthouse servers
Args:
None
Returns:
List of address:port
"""
d = self._render_response(SEARCH_SERVERS, OK_CODE)
return d
def get_lbrynet_version_from_github():
"""Return the latest released version from github."""

View file

@ -14,7 +14,7 @@ class HttpPasswordRealm(object):
self.resource = resource
def requestAvatar(self, avatarId, mind, *interfaces):
log.info("Processing request for %s", avatarId)
log.debug("Processing request for %s", avatarId)
if resource.IResource in interfaces:
return (resource.IResource, self.resource, lambda: None)
raise NotImplementedError()

View file

@ -5,7 +5,7 @@ import os
import base64
import json
from lbrynet.lbrynet_daemon.auth.util import load_api_keys, APIKey, API_KEY_NAME
from lbrynet.lbrynet_daemon.auth.util import load_api_keys, APIKey, API_KEY_NAME, get_auth_message
from lbrynet.conf import API_INTERFACE, API_ADDRESS, API_PORT
from lbrynet.conf import DATA_DIR
@ -55,8 +55,8 @@ class LBRYAPIClient(object):
'method': self.__service_name,
'params': args,
'id': self.__id_count}
to_auth = str(pre_auth_postdata['method']).encode('hex') + str(pre_auth_postdata['id']).encode('hex')
token = self.__api_key.get_hmac(to_auth.decode('hex'))
to_auth = get_auth_message(pre_auth_postdata)
token = self.__api_key.get_hmac(to_auth)
pre_auth_postdata.update({'hmac': token})
postdata = json.dumps(pre_auth_postdata)
service_url = self.__service_url
@ -79,16 +79,13 @@ class LBRYAPIClient(object):
next_secret = headers.get(LBRY_SECRET, False)
if next_secret:
# print "Next secret: %s" % next_secret
self.__api_key.secret = next_secret
self.__cookies = cookies
# print "Postdata: %s" % postdata
if http_response is None:
raise JSONRPCException({
'code': -342, 'message': 'missing HTTP response from server'})
# print "-----\n%s\n------" % http_response.text
http_response.raise_for_status()
response = http_response.json()
@ -104,20 +101,18 @@ class LBRYAPIClient(object):
@classmethod
def config(cls, key_name=None, key=None, pw_path=None, timeout=HTTP_TIMEOUT, connection=None, count=0,
service=None, cookies=None, auth=None, url=None, login_url=None):
api_key_name = API_KEY_NAME if not key_name else key_name
pw_path = os.path.join(DATA_DIR, ".api_keys") if not pw_path else pw_path
if not key:
keys = load_api_keys(pw_path)
api_key = keys.get(api_key_name, False)
else:
api_key = APIKey(name=api_key_name, secret=key)
if login_url is None:
service_url = "http://%s:%s@%s:%i/%s" % (api_key_name, api_key.secret, API_INTERFACE, API_PORT, API_ADDRESS)
else:
service_url = login_url
id_count = count
if auth is None and connection is None and cookies is None and url is None:
@ -134,10 +129,8 @@ class LBRYAPIClient(object):
pass
authpair = user + b':' + passwd
auth_header = b'Basic ' + base64.b64encode(authpair)
conn = requests.Session()
conn.auth = (user, passwd)
req = requests.Request(method='POST',
url=service_url,
auth=conn.auth,
@ -148,11 +141,9 @@ class LBRYAPIClient(object):
r = req.prepare()
http_response = conn.send(r)
cookies = http_response.cookies
# print "Logged in"
uid = cookies.get(TWISTED_SESSION)
api_key = APIKey.new(seed=uid)
# print "Created temporary api key"
else:
# This is a client that already has a session, use it
auth_header = auth

View file

@ -1,16 +1,15 @@
import logging
from decimal import Decimal
from twisted.web import server
from zope.interface import implements
from twisted.web import server, resource
from twisted.internet import defer
from txjsonrpc import jsonrpclib
from txjsonrpc.web import jsonrpc
from txjsonrpc.web.jsonrpc import Handler
from lbrynet.core.Error import InvalidAuthenticationToken, InvalidHeaderError
from lbrynet.lbrynet_daemon.auth.util import APIKey
from lbrynet.core.Error import InvalidAuthenticationToken, InvalidHeaderError, SubhandlerError
from lbrynet.conf import API_INTERFACE, REFERER, ORIGIN
from lbrynet.lbrynet_daemon.auth.util import APIKey, get_auth_message
from lbrynet.lbrynet_daemon.auth.client import LBRY_SECRET
from lbrynet.conf import ALLOWED_DURING_STARTUP
log = logging.getLogger(__name__)
@ -20,42 +19,166 @@ def default_decimal(obj):
return float(obj)
def authorizer(cls):
cls.authorized_functions = []
for methodname in dir(cls):
if methodname.startswith("jsonrpc_"):
method = getattr(cls, methodname)
if hasattr(method, '_auth_required'):
cls.authorized_functions.append(methodname.split("jsonrpc_")[1])
return cls
class AuthorizedBase(object):
def __init__(self):
self.authorized_functions = []
self.subhandlers = []
self.callable_methods = {}
for methodname in dir(self):
if methodname.startswith("jsonrpc_"):
method = getattr(self, methodname)
self.callable_methods.update({methodname.split("jsonrpc_")[1]: method})
if hasattr(method, '_auth_required'):
self.authorized_functions.append(methodname.split("jsonrpc_")[1])
elif not methodname.startswith("__"):
method = getattr(self, methodname)
if hasattr(method, '_subhandler'):
self.subhandlers.append(method)
@staticmethod
def auth_required(f):
f._auth_required = True
return f
@staticmethod
def subhandler(f):
f._subhandler = True
return f
def auth_required(f):
f._auth_required = True
return f
class AuthJSONRPCServer(AuthorizedBase):
"""
Authorized JSONRPC server used as the base class for the LBRY API
API methods are named with a leading "jsonrpc_"
@authorizer
class LBRYJSONRPCServer(jsonrpc.JSONRPC):
Decorators:
@AuthJSONRPCServer.auth_required: this requires the client include a valid hmac authentication token in their
request
@AuthJSONRPCServer.subhandler: include the tagged method in the processing of requests, to allow inheriting
classes to modify request handling. Tagged methods will be passed the request
object, and return True when finished to indicate success
Attributes:
allowed_during_startup (list): list of api methods that are callable before the server has finished
startup
sessions (dict): dictionary of active session_id: lbrynet.lbrynet_daemon.auth.util.APIKey values
authorized_functions (list): list of api methods that require authentication
subhandlers (list): list of subhandlers
callable_methods (dict): dictionary of api_callable_name: method values
"""
implements(resource.IResource)
isLeaf = True
OK = 200
UNAUTHORIZED = 401
NOT_FOUND = 8001
FAILURE = 8002
def __init__(self):
jsonrpc.JSONRPC.__init__(self)
AuthorizedBase.__init__(self)
self.allowed_during_startup = []
self.sessions = {}
def setup(self):
return NotImplementedError()
def render(self, request):
assert self._check_headers(request), InvalidHeaderError
session = request.getSession()
session_id = session.uid
# if this is a new session, send a new secret and set the expiration, otherwise, session.touch()
if self._initialize_session(session_id):
def expire_session():
self._unregister_user_session(session_id)
session.startCheckingExpiration()
session.notifyOnExpire(expire_session)
message = "OK"
request.setResponseCode(self.OK)
self._set_headers(request, message, True)
self._render_message(request, message)
return server.NOT_DONE_YET
session.touch()
request.content.seek(0, 0)
content = request.content.read()
try:
parsed = jsonrpclib.loads(content)
except ValueError:
return server.failure
function_name = parsed.get('method')
args = parsed.get('params')
id = parsed.get('id')
token = parsed.pop('hmac', None)
version = self._get_jsonrpc_version(parsed.get('jsonrpc'), id)
try:
self._run_subhandlers(request)
except SubhandlerError:
return server.failure
reply_with_next_secret = False
if function_name in self.authorized_functions:
try:
self._verify_token(session_id, parsed, token)
except InvalidAuthenticationToken:
log.warning("API validation failed")
request.setResponseCode(self.UNAUTHORIZED)
request.finish()
return server.NOT_DONE_YET
self._update_session_secret(session_id)
reply_with_next_secret = True
try:
function = self._get_jsonrpc_method(function_name)
except Exception:
log.warning("Unknown method: %s", function_name)
return server.failure
d = defer.maybeDeferred(function) if args == [{}] else defer.maybeDeferred(function, *args)
# cancel the response if the connection is broken
notify_finish = request.notifyFinish()
notify_finish.addErrback(self._response_failed, d)
d.addErrback(self._errback_render, id)
d.addCallback(self._callback_render, request, id, version, reply_with_next_secret)
d.addErrback(notify_finish.errback)
return server.NOT_DONE_YET
def _register_user_session(self, session_id):
"""
Add or update a HMAC secret for a session
@param session_id:
@return: secret
"""
token = APIKey.new()
self.sessions.update({session_id: token})
return token
def _responseFailed(self, err, call):
def _unregister_user_session(self, session_id):
log.info("Unregister API session")
del self.sessions[session_id]
def _response_failed(self, err, call):
log.debug(err.getTraceback())
def _set_headers(self, request, data):
request.setHeader("Access-Control-Allow-Origin", "localhost")
def _set_headers(self, request, data, update_secret=False):
request.setHeader("Access-Control-Allow-Origin", API_INTERFACE)
request.setHeader("Content-Type", "text/json")
request.setHeader("Content-Length", str(len(data)))
if update_secret:
session_id = request.getSession().uid
request.setHeader(LBRY_SECRET, self.sessions.get(session_id).secret)
def _render_message(self, request, message):
request.write(message)
@ -64,112 +187,81 @@ class LBRYJSONRPCServer(jsonrpc.JSONRPC):
def _check_headers(self, request):
origin = request.getHeader("Origin")
referer = request.getHeader("Referer")
if origin not in [None, 'http://localhost:5279']:
if origin not in [None, ORIGIN]:
log.warning("Attempted api call from %s", origin)
raise InvalidHeaderError
if referer is not None and not referer.startswith('http://localhost:5279/'):
return False
if referer is not None and not referer.startswith(REFERER):
log.warning("Attempted api call from %s", referer)
raise InvalidHeaderError
return False
return True
def _handle(self, request):
def _check_function_path(function_path):
if not self.announced_startup:
if function_path not in ALLOWED_DURING_STARTUP:
log.warning("Cannot call %s during startup", function_path)
raise Exception("Function not allowed")
def _check_function_path(self, function_path):
if function_path not in self.callable_methods:
log.warning("Unknown method: %s", function_path)
return False
if not self.announced_startup:
if function_path not in self.allowed_during_startup:
log.warning("Cannot call %s during startup", function_path)
return False
return True
def _get_function(function_path):
function = self._getFunction(function_path)
return function
def _get_jsonrpc_method(self, function_path):
assert self._check_function_path(function_path)
return self.callable_methods.get(function_path)
def _verify_token(session_id, message, token):
request.setHeader(LBRY_SECRET, "")
api_key = self.sessions.get(session_id, None)
assert api_key is not None, InvalidAuthenticationToken
r = api_key.compare_hmac(message, token)
assert r, InvalidAuthenticationToken
# log.info("Generating new token for next request")
self.sessions.update({session_id: APIKey.new(name=session_id)})
request.setHeader(LBRY_SECRET, self.sessions.get(session_id).secret)
session = request.getSession()
session_id = session.uid
session_store = self.sessions.get(session_id, False)
if not session_store:
token = APIKey.new(seed=session_id, name=session_id)
def _initialize_session(self, session_id):
if not self.sessions.get(session_id, False):
log.info("Initializing new api session")
self.sessions.update({session_id: token})
# log.info("Generated token %s", str(self.sessions[session_id]))
self.sessions.update({session_id: APIKey.new(seed=session_id, name=session_id)})
return True
return False
request.content.seek(0, 0)
content = request.content.read()
def _verify_token(self, session_id, message, token):
to_auth = get_auth_message(message)
api_key = self.sessions.get(session_id)
assert api_key.compare_hmac(to_auth, token), InvalidAuthenticationToken
parsed = jsonrpclib.loads(content)
functionPath = parsed.get("method")
_check_function_path(functionPath)
require_auth = functionPath in self.authorized_functions
if require_auth:
token = parsed.pop('hmac')
to_auth = functionPath.encode('hex') + str(parsed.get('id')).encode('hex')
_verify_token(session_id, to_auth.decode('hex'), token)
args = parsed.get('params')
id = parsed.get('id')
version = parsed.get('jsonrpc')
def _update_session_secret(self, session_id):
# log.info("Generating new token for next request")
self.sessions.update({session_id: APIKey.new(name=session_id)})
def _get_jsonrpc_version(self, version=None, id=None):
if version:
version = int(float(version))
version_for_return = int(float(version))
elif id and not version:
version = jsonrpclib.VERSION_1
version_for_return = jsonrpclib.VERSION_1
else:
version = jsonrpclib.VERSION_PRE1
version_for_return = jsonrpclib.VERSION_PRE1
return version_for_return
if self.wallet_type == "lbryum" and functionPath in ['set_miner', 'get_miner_status']:
log.warning("Mining commands are not available in lbryum")
raise Exception("Command not available in lbryum")
def _run_subhandlers(self, request):
for handler in self.subhandlers:
try:
assert handler(request)
except Exception as err:
log.error(err.message)
raise SubhandlerError
try:
function = _get_function(functionPath)
if args == [{}]:
d = defer.maybeDeferred(function)
else:
d = defer.maybeDeferred(function, *args)
except jsonrpclib.Fault as f:
d = self._cbRender(f, request, id, version)
finally:
# cancel the response if the connection is broken
notify_finish = request.notifyFinish()
notify_finish.addErrback(self._responseFailed, d)
d.addErrback(self._ebRender, id)
d.addCallback(self._cbRender, request, id, version)
d.addErrback(notify_finish.errback)
def _cbRender(self, result, request, id, version):
if isinstance(result, Handler):
result = result.result
if isinstance(result, dict):
result = result['result']
def _callback_render(self, result, request, id, version, auth_required=False):
result_for_return = result if not isinstance(result, dict) else result['result']
if version == jsonrpclib.VERSION_PRE1:
if not isinstance(result, jsonrpclib.Fault):
result = (result,)
result_for_return = (result_for_return,)
# Convert the result (python) to JSON-RPC
try:
s = jsonrpclib.dumps(result, version=version, default=default_decimal)
self._render_message(request, s)
encoded_message = jsonrpclib.dumps(result_for_return, version=version, default=default_decimal)
self._set_headers(request, encoded_message, auth_required)
self._render_message(request, encoded_message)
except:
f = jsonrpclib.Fault(self.FAILURE, "can't serialize output")
s = jsonrpclib.dumps(f, version=version)
self._set_headers(request, s)
self._render_message(request, s)
fault = jsonrpclib.Fault(self.FAILURE, "can't serialize output")
encoded_message = jsonrpclib.dumps(fault, version=version)
self._set_headers(request, encoded_message)
self._render_message(request, encoded_message)
def _ebRender(self, failure, id):
def _errback_render(self, failure, id):
log.error("Request failed:")
log.error(failure)
log.error(failure.value)
log.error(id)
@ -177,19 +269,6 @@ class LBRYJSONRPCServer(jsonrpc.JSONRPC):
return failure.value
return server.failure
def render(self, request):
try:
self._check_headers(request)
except InvalidHeaderError:
return server.failure
try:
self._handle(request)
except:
return server.failure
return server.NOT_DONE_YET
def _render_response(self, result, code):
return defer.succeed({'result': result, 'code': code})

View file

@ -85,3 +85,9 @@ def initialize_api_key_file(key_path):
new_api_key = APIKey.new(name=API_KEY_NAME)
keys.update({new_api_key.name: new_api_key})
save_api_keys(keys, key_path)
def get_auth_message(message_dict):
to_auth = message_dict.get('method').encode('hex')
to_auth += str(message_dict.get('id')).encode('hex')
return to_auth.decode('hex')