2016-09-20 22:58:30 +02:00
|
|
|
import os
|
2018-07-21 20:12:29 +02:00
|
|
|
import json
|
2018-08-04 23:19:10 +02:00
|
|
|
import aiohttp
|
2018-04-01 00:42:57 +02:00
|
|
|
import logging
|
2018-08-11 12:25:36 +02:00
|
|
|
from urllib.parse import urlparse
|
|
|
|
|
2018-04-01 00:42:57 +02:00
|
|
|
from lbrynet import conf
|
|
|
|
from lbrynet.daemon.auth.util import load_api_keys, APIKey, API_KEY_NAME, get_auth_message
|
2016-09-20 22:58:30 +02:00
|
|
|
|
|
|
|
log = logging.getLogger(__name__)
|
|
|
|
USER_AGENT = "AuthServiceProxy/0.1"
|
2016-09-21 09:49:52 +02:00
|
|
|
TWISTED_SESSION = "TWISTED_SESSION"
|
|
|
|
LBRY_SECRET = "LBRY_SECRET"
|
2016-09-20 22:58:30 +02:00
|
|
|
HTTP_TIMEOUT = 30
|
2018-08-04 23:19:10 +02:00
|
|
|
SCHEME = "http"
|
2016-09-20 22:58:30 +02:00
|
|
|
|
|
|
|
|
|
|
|
class JSONRPCException(Exception):
|
|
|
|
def __init__(self, rpc_error):
|
2018-07-22 00:34:59 +02:00
|
|
|
super().__init__()
|
2016-09-20 22:58:30 +02:00
|
|
|
self.error = rpc_error
|
|
|
|
|
|
|
|
|
2018-08-04 23:19:10 +02:00
|
|
|
class UnAuthAPIClient:
|
|
|
|
def __init__(self, host, port):
|
|
|
|
self.host = host
|
|
|
|
self.port = port
|
|
|
|
self.scheme = SCHEME
|
|
|
|
|
|
|
|
def __getattr__(self, method):
|
|
|
|
async def f(*args, **kwargs):
|
|
|
|
return await self.call(method, [args, kwargs])
|
|
|
|
|
|
|
|
return f
|
|
|
|
|
|
|
|
@classmethod
|
2018-08-11 12:25:36 +02:00
|
|
|
async def from_url(cls, url):
|
2018-08-04 23:19:10 +02:00
|
|
|
url_fragment = urlparse(url)
|
|
|
|
host = url_fragment.hostname
|
|
|
|
port = url_fragment.port
|
|
|
|
return cls(host, port)
|
|
|
|
|
|
|
|
async def call(self, method, params=None):
|
|
|
|
message = {'method': method, 'params': params}
|
|
|
|
async with aiohttp.ClientSession() as session:
|
|
|
|
async with session.get('{}://{}:{}'.format(self.scheme, self.host, self.port), json=message) as resp:
|
|
|
|
return await resp.json()
|
|
|
|
|
|
|
|
|
2018-08-11 12:25:36 +02:00
|
|
|
class AsyncAuthAPIClient:
|
|
|
|
def __init__(self, key, session, cookies, url, login_url):
|
|
|
|
self.session = session
|
2016-09-21 09:49:52 +02:00
|
|
|
self.__api_key = key
|
2018-08-11 12:25:36 +02:00
|
|
|
self.__login_url = login_url
|
|
|
|
self.__id_count = 0
|
2016-09-21 09:49:52 +02:00
|
|
|
self.__url = url
|
2018-08-11 12:25:36 +02:00
|
|
|
self.__cookies = cookies
|
2016-09-20 22:58:30 +02:00
|
|
|
|
|
|
|
def __getattr__(self, name):
|
|
|
|
if name.startswith('__') and name.endswith('__'):
|
2018-04-01 00:42:57 +02:00
|
|
|
raise AttributeError(name)
|
2017-01-03 20:13:01 +01:00
|
|
|
|
2018-04-03 18:26:32 +02:00
|
|
|
def f(*args, **kwargs):
|
|
|
|
return self.call(name, [args, kwargs])
|
2017-01-03 20:13:01 +01:00
|
|
|
|
|
|
|
return f
|
|
|
|
|
2018-08-04 23:19:10 +02:00
|
|
|
async def call(self, method, params=None):
|
2018-04-01 00:42:57 +02:00
|
|
|
params = params or {}
|
2016-09-20 22:58:30 +02:00
|
|
|
self.__id_count += 1
|
2018-08-11 12:25:36 +02:00
|
|
|
|
2017-01-10 01:31:06 +01:00
|
|
|
pre_auth_post_data = {
|
2017-03-24 22:18:17 +01:00
|
|
|
'version': '2',
|
2017-01-03 20:13:01 +01:00
|
|
|
'method': method,
|
2017-01-31 00:46:06 +01:00
|
|
|
'params': params,
|
2017-01-03 20:13:01 +01:00
|
|
|
'id': self.__id_count
|
|
|
|
}
|
2017-01-10 01:31:06 +01:00
|
|
|
to_auth = get_auth_message(pre_auth_post_data)
|
2018-08-11 12:25:36 +02:00
|
|
|
auth_msg = self.__api_key.get_hmac(to_auth).decode()
|
|
|
|
pre_auth_post_data.update({'hmac': auth_msg})
|
2017-01-10 01:31:06 +01:00
|
|
|
post_data = json.dumps(pre_auth_post_data)
|
2016-09-21 09:49:52 +02:00
|
|
|
|
2018-08-11 12:25:36 +02:00
|
|
|
headers = {
|
|
|
|
'Host': self.__url.hostname,
|
|
|
|
'User-Agent': USER_AGENT,
|
|
|
|
'Content-type': 'application/json'
|
|
|
|
}
|
|
|
|
|
|
|
|
async with self.session.post(self.__login_url, data=post_data, headers=headers) as resp:
|
|
|
|
if resp is None:
|
|
|
|
raise JSONRPCException({'code': -342, 'message': 'missing HTTP response from server'})
|
|
|
|
resp.raise_for_status()
|
|
|
|
|
|
|
|
next_secret = resp.headers.get(LBRY_SECRET, False)
|
|
|
|
if next_secret:
|
|
|
|
self.__api_key.secret = next_secret
|
2016-09-22 03:36:06 +02:00
|
|
|
|
2018-08-11 12:25:36 +02:00
|
|
|
return await resp.json()
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
async def get_client(cls, key_name=None):
|
2018-04-01 00:42:57 +02:00
|
|
|
api_key_name = key_name or API_KEY_NAME
|
2018-08-11 12:25:36 +02:00
|
|
|
|
|
|
|
pw_path = os.path.join(conf.settings['data_dir'], ".api_keys")
|
|
|
|
keys = load_api_keys(pw_path)
|
|
|
|
api_key = keys.get(api_key_name, False)
|
|
|
|
|
|
|
|
login_url = "http://{}:{}@{}:{}".format(api_key_name, api_key.secret, conf.settings['api_host'],
|
|
|
|
conf.settings['api_port'])
|
|
|
|
url = urlparse(login_url)
|
|
|
|
|
|
|
|
headers = {
|
|
|
|
'Host': url.hostname,
|
|
|
|
'User-Agent': USER_AGENT,
|
|
|
|
'Content-type': 'application/json'
|
|
|
|
}
|
|
|
|
|
|
|
|
session = aiohttp.ClientSession()
|
|
|
|
|
|
|
|
async with session.post(login_url, headers=headers) as r:
|
|
|
|
cookies = r.cookies
|
|
|
|
|
|
|
|
uid = cookies.get(TWISTED_SESSION).value
|
|
|
|
api_key = APIKey.new(seed=uid.encode())
|
|
|
|
return cls(api_key, session, cookies, url, login_url)
|
2016-10-26 09:16:33 +02:00
|
|
|
|
|
|
|
|
2018-07-22 00:34:59 +02:00
|
|
|
class LBRYAPIClient:
|
2016-10-26 09:16:33 +02:00
|
|
|
@staticmethod
|
2018-08-04 23:19:10 +02:00
|
|
|
def get_client(conf_path=None):
|
|
|
|
conf.conf_file = conf_path
|
2017-06-26 03:23:48 +02:00
|
|
|
if not conf.settings:
|
|
|
|
conf.initialize_settings()
|
2018-08-11 12:25:36 +02:00
|
|
|
return AsyncAuthAPIClient.get_client() if conf.settings['use_auth_http'] else \
|
2018-08-04 23:19:10 +02:00
|
|
|
UnAuthAPIClient.from_url(conf.settings.get_api_connection_string())
|