forked from LBRYCommunity/lbry-sdk
aiohttp based Async Authenticated API Client
This commit is contained in:
parent
ab3b0134b5
commit
083bf4f61d
2 changed files with 55 additions and 78 deletions
|
@ -16,7 +16,7 @@ from lbrynet.core.system_info import get_platform
|
||||||
async def execute_command(method, params, conf_path=None):
|
async def execute_command(method, params, conf_path=None):
|
||||||
# this check if the daemon is running or not
|
# this check if the daemon is running or not
|
||||||
try:
|
try:
|
||||||
api = LBRYAPIClient.get_client(conf_path)
|
api = await LBRYAPIClient.get_client(conf_path)
|
||||||
await api.status()
|
await api.status()
|
||||||
except (ClientConnectorError, ConnectionError):
|
except (ClientConnectorError, ConnectionError):
|
||||||
print("Could not connect to daemon. Are you sure it's running?")
|
print("Could not connect to daemon. Are you sure it's running?")
|
||||||
|
@ -25,6 +25,8 @@ async def execute_command(method, params, conf_path=None):
|
||||||
# this actually executes the method
|
# this actually executes the method
|
||||||
try:
|
try:
|
||||||
resp = await api.call(method, params)
|
resp = await api.call(method, params)
|
||||||
|
if not api.session.closed:
|
||||||
|
await api.session.close()
|
||||||
print(json.dumps(resp["result"], indent=2))
|
print(json.dumps(resp["result"], indent=2))
|
||||||
except KeyError:
|
except KeyError:
|
||||||
if resp["error"]["code"] == -32500:
|
if resp["error"]["code"] == -32500:
|
||||||
|
|
|
@ -1,11 +1,9 @@
|
||||||
# pylint: skip-file
|
|
||||||
import os
|
import os
|
||||||
import json
|
import json
|
||||||
import aiohttp
|
import aiohttp
|
||||||
from urllib.parse import urlparse
|
|
||||||
import requests
|
|
||||||
from requests.cookies import RequestsCookieJar
|
|
||||||
import logging
|
import logging
|
||||||
|
from urllib.parse import urlparse
|
||||||
|
|
||||||
from lbrynet import conf
|
from lbrynet import conf
|
||||||
from lbrynet.daemon.auth.util import load_api_keys, APIKey, API_KEY_NAME, get_auth_message
|
from lbrynet.daemon.auth.util import load_api_keys, APIKey, API_KEY_NAME, get_auth_message
|
||||||
|
|
||||||
|
@ -17,12 +15,6 @@ HTTP_TIMEOUT = 30
|
||||||
SCHEME = "http"
|
SCHEME = "http"
|
||||||
|
|
||||||
|
|
||||||
def copy_cookies(cookies):
|
|
||||||
result = RequestsCookieJar()
|
|
||||||
result.update(cookies)
|
|
||||||
return result
|
|
||||||
|
|
||||||
|
|
||||||
class JSONRPCException(Exception):
|
class JSONRPCException(Exception):
|
||||||
def __init__(self, rpc_error):
|
def __init__(self, rpc_error):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
@ -42,7 +34,7 @@ class UnAuthAPIClient:
|
||||||
return f
|
return f
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_url(cls, url):
|
async def from_url(cls, url):
|
||||||
url_fragment = urlparse(url)
|
url_fragment = urlparse(url)
|
||||||
host = url_fragment.hostname
|
host = url_fragment.hostname
|
||||||
port = url_fragment.port
|
port = url_fragment.port
|
||||||
|
@ -55,14 +47,14 @@ class UnAuthAPIClient:
|
||||||
return await resp.json()
|
return await resp.json()
|
||||||
|
|
||||||
|
|
||||||
class AuthAPIClient:
|
class AsyncAuthAPIClient:
|
||||||
def __init__(self, key, timeout, connection, count, cookies, url, login_url):
|
def __init__(self, key, session, cookies, url, login_url):
|
||||||
|
self.session = session
|
||||||
self.__api_key = key
|
self.__api_key = key
|
||||||
self.__service_url = login_url
|
self.__login_url = login_url
|
||||||
self.__id_count = count
|
self.__id_count = 0
|
||||||
self.__url = url
|
self.__url = url
|
||||||
self.__conn = connection
|
self.__cookies = cookies
|
||||||
self.__cookies = copy_cookies(cookies)
|
|
||||||
|
|
||||||
def __getattr__(self, name):
|
def __getattr__(self, name):
|
||||||
if name.startswith('__') and name.endswith('__'):
|
if name.startswith('__') and name.endswith('__'):
|
||||||
|
@ -76,6 +68,7 @@ class AuthAPIClient:
|
||||||
async def call(self, method, params=None):
|
async def call(self, method, params=None):
|
||||||
params = params or {}
|
params = params or {}
|
||||||
self.__id_count += 1
|
self.__id_count += 1
|
||||||
|
|
||||||
pre_auth_post_data = {
|
pre_auth_post_data = {
|
||||||
'version': '2',
|
'version': '2',
|
||||||
'method': method,
|
'method': method,
|
||||||
|
@ -83,71 +76,53 @@ class AuthAPIClient:
|
||||||
'id': self.__id_count
|
'id': self.__id_count
|
||||||
}
|
}
|
||||||
to_auth = get_auth_message(pre_auth_post_data)
|
to_auth = get_auth_message(pre_auth_post_data)
|
||||||
pre_auth_post_data.update({'hmac': self.__api_key.get_hmac(to_auth).decode()})
|
auth_msg = self.__api_key.get_hmac(to_auth).decode()
|
||||||
|
pre_auth_post_data.update({'hmac': auth_msg})
|
||||||
post_data = json.dumps(pre_auth_post_data)
|
post_data = json.dumps(pre_auth_post_data)
|
||||||
cookies = copy_cookies(self.__cookies)
|
|
||||||
|
|
||||||
req = requests.Request(
|
headers = {
|
||||||
method='POST', url=self.__service_url, data=post_data, cookies=cookies,
|
'Host': self.__url.hostname,
|
||||||
headers={
|
'User-Agent': USER_AGENT,
|
||||||
'Host': self.__url.hostname,
|
'Content-type': 'application/json'
|
||||||
'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:
|
||||||
http_response = self.__conn.send(req.prepare())
|
raise JSONRPCException({'code': -342, 'message': 'missing HTTP response from server'})
|
||||||
if http_response is None:
|
resp.raise_for_status()
|
||||||
raise JSONRPCException({'code': -342, 'message': 'missing HTTP response from server'})
|
|
||||||
http_response.raise_for_status()
|
next_secret = resp.headers.get(LBRY_SECRET, False)
|
||||||
next_secret = http_response.headers.get(LBRY_SECRET, False)
|
if next_secret:
|
||||||
if next_secret:
|
self.__api_key.secret = next_secret
|
||||||
self.__api_key.secret = next_secret
|
|
||||||
self.__cookies = copy_cookies(http_response.cookies)
|
return await resp.json()
|
||||||
return http_response.json()
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def config(cls, key_name=None, key=None, pw_path=None, timeout=HTTP_TIMEOUT, connection=None, count=0,
|
async def get_client(cls, key_name=None):
|
||||||
cookies=None, auth=None, url=None, login_url=None):
|
|
||||||
|
|
||||||
api_key_name = key_name or API_KEY_NAME
|
api_key_name = key_name or API_KEY_NAME
|
||||||
pw_path = os.path.join(conf.settings['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://{}:{}@{}:{}".format(
|
|
||||||
api_key_name, api_key.secret, conf.settings['api_host'], conf.settings['api_port']
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
service_url = login_url
|
|
||||||
id_count = count
|
|
||||||
|
|
||||||
if auth is None and connection is None and cookies is None and url is None:
|
pw_path = os.path.join(conf.settings['data_dir'], ".api_keys")
|
||||||
# This is a new client instance, start an authenticated session
|
keys = load_api_keys(pw_path)
|
||||||
url = urlparse(service_url)
|
api_key = keys.get(api_key_name, False)
|
||||||
conn = requests.Session()
|
|
||||||
req = requests.Request(
|
login_url = "http://{}:{}@{}:{}".format(api_key_name, api_key.secret, conf.settings['api_host'],
|
||||||
method='POST', url=service_url, headers={
|
conf.settings['api_port'])
|
||||||
'Host': url.hostname,
|
url = urlparse(login_url)
|
||||||
'User-Agent': USER_AGENT,
|
|
||||||
'Content-type': 'application/json'
|
headers = {
|
||||||
})
|
'Host': url.hostname,
|
||||||
r = req.prepare()
|
'User-Agent': USER_AGENT,
|
||||||
http_response = conn.send(r)
|
'Content-type': 'application/json'
|
||||||
cookies = RequestsCookieJar()
|
}
|
||||||
cookies.update(http_response.cookies)
|
|
||||||
uid = cookies.get(TWISTED_SESSION)
|
session = aiohttp.ClientSession()
|
||||||
api_key = APIKey.new(seed=uid.encode())
|
|
||||||
else:
|
async with session.post(login_url, headers=headers) as r:
|
||||||
# This is a client that already has a session, use it
|
cookies = r.cookies
|
||||||
conn = connection
|
|
||||||
if not cookies.get(LBRY_SECRET):
|
uid = cookies.get(TWISTED_SESSION).value
|
||||||
raise Exception("Missing cookie")
|
api_key = APIKey.new(seed=uid.encode())
|
||||||
secret = cookies.get(LBRY_SECRET)
|
return cls(api_key, session, cookies, url, login_url)
|
||||||
api_key = APIKey(secret, api_key_name)
|
|
||||||
return cls(api_key, timeout, conn, id_count, cookies, url, service_url)
|
|
||||||
|
|
||||||
|
|
||||||
class LBRYAPIClient:
|
class LBRYAPIClient:
|
||||||
|
@ -156,5 +131,5 @@ class LBRYAPIClient:
|
||||||
conf.conf_file = conf_path
|
conf.conf_file = conf_path
|
||||||
if not conf.settings:
|
if not conf.settings:
|
||||||
conf.initialize_settings()
|
conf.initialize_settings()
|
||||||
return AuthAPIClient.config() if conf.settings['use_auth_http'] else \
|
return AsyncAuthAPIClient.get_client() if conf.settings['use_auth_http'] else \
|
||||||
UnAuthAPIClient.from_url(conf.settings.get_api_connection_string())
|
UnAuthAPIClient.from_url(conf.settings.get_api_connection_string())
|
||||||
|
|
Loading…
Reference in a new issue