forked from LBRYCommunity/lbry-sdk
clean up
This commit is contained in:
parent
130f9cfc4d
commit
aecafbbebd
8 changed files with 173 additions and 152 deletions
|
@ -2,7 +2,24 @@
|
|||
Some network wide and also application specific parameters
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
from appdirs import user_data_dir
|
||||
|
||||
LINUX = 1
|
||||
DARWIN = 2
|
||||
WINDOWS = 3
|
||||
|
||||
if sys.platform.startswith("linux"):
|
||||
platform = LINUX
|
||||
elif sys.platform.startswith("darwin"):
|
||||
platform = DARWIN
|
||||
elif sys.platform.startswith("win"):
|
||||
platform = WINDOWS
|
||||
|
||||
if platform is LINUX:
|
||||
DATA_DIR = os.path.join(os.path.expanduser("~"), ".lbrynet")
|
||||
else:
|
||||
DATA_DIR = user_data_dir("LBRY")
|
||||
|
||||
IS_DEVELOPMENT_VERSION = False
|
||||
|
||||
|
|
|
@ -35,10 +35,11 @@ def get_params_from_kwargs(params):
|
|||
|
||||
|
||||
def main():
|
||||
api = LBRYAPIClient()
|
||||
api = LBRYAPIClient.config()
|
||||
|
||||
try:
|
||||
s = api.is_running()
|
||||
status = api.daemon_status()
|
||||
assert status.get('code', False) == "started"
|
||||
except:
|
||||
print "lbrynet-daemon isn't running"
|
||||
sys.exit(1)
|
||||
|
@ -71,10 +72,10 @@ def main():
|
|||
if meth in api.help():
|
||||
try:
|
||||
if params:
|
||||
r = LBRYAPIClient(service=meth)(params)
|
||||
result = LBRYAPIClient.config(service=meth)(params)
|
||||
else:
|
||||
r = LBRYAPIClient(service=meth)()
|
||||
print json.dumps(r, sort_keys=True)
|
||||
result = LBRYAPIClient.config(service=meth)()
|
||||
print json.dumps(result, sort_keys=True)
|
||||
except:
|
||||
print "Something went wrong, here's the usage for %s:" % meth
|
||||
print api.help({'function': meth})
|
||||
|
|
|
@ -16,14 +16,10 @@ from jsonrpc.proxy import JSONRPCProxy
|
|||
from lbrynet.core import log_support
|
||||
from lbrynet.lbrynet_daemon.LBRYDaemonServer import LBRYDaemonServer, LBRYDaemonRequest
|
||||
from lbrynet.lbrynet_daemon.auth.auth import PasswordChecker, HttpPasswordRealm
|
||||
from lbrynet.lbrynet_daemon.auth.util import initialize_api_key_file
|
||||
from lbrynet.conf import API_CONNECTION_STRING, API_INTERFACE, API_PORT, \
|
||||
UI_ADDRESS, DEFAULT_UI_BRANCH, LOG_FILE_NAME
|
||||
|
||||
# TODO: stop it!
|
||||
if sys.platform != "darwin":
|
||||
log_dir = os.path.join(os.path.expanduser("~"), ".lbrynet")
|
||||
else:
|
||||
log_dir = user_data_dir("LBRY")
|
||||
from lbrynet.conf import DATA_DIR as log_dir
|
||||
|
||||
if not os.path.isdir(log_dir):
|
||||
os.mkdir(log_dir)
|
||||
|
@ -116,11 +112,13 @@ def start():
|
|||
if args.launchui:
|
||||
d.addCallback(lambda _: webbrowser.open(UI_ADDRESS))
|
||||
|
||||
checker = PasswordChecker()
|
||||
pw_path = os.path.join(log_dir, ".api_keys")
|
||||
initialize_api_key_file(pw_path)
|
||||
checker = PasswordChecker.load_file(pw_path)
|
||||
realm = HttpPasswordRealm(lbry.root)
|
||||
p = portal.Portal(realm, [checker, ])
|
||||
portal_to_realm = portal.Portal(realm, [checker, ])
|
||||
factory = guard.BasicCredentialFactory('Login to lbrynet api')
|
||||
protected_resource = guard.HTTPAuthSessionWrapper(p, [factory, ])
|
||||
protected_resource = guard.HTTPAuthSessionWrapper(portal_to_realm, [factory, ])
|
||||
lbrynet_server = server.Site(protected_resource)
|
||||
lbrynet_server.requestFactory = LBRYDaemonRequest
|
||||
|
||||
|
|
|
@ -50,11 +50,15 @@ class MarketFeed(object):
|
|||
log.debug("Saving price update %f for %s" % (price, self.market))
|
||||
self.rate = ExchangeRate(self.market, price, int(time.time()))
|
||||
|
||||
def _log_error(self):
|
||||
log.warning("%s failed to update exchange rate information", self.name)
|
||||
|
||||
def _update_price(self):
|
||||
d = defer.succeed(self._make_request())
|
||||
d.addCallback(self._handle_response)
|
||||
d.addCallback(self._subtract_fee)
|
||||
d.addCallback(self._save_price)
|
||||
d.addErrback(lambda _: self._log_error())
|
||||
|
||||
def start(self):
|
||||
if not self._updater.running:
|
||||
|
|
|
@ -1,26 +1,15 @@
|
|||
import logging
|
||||
import os
|
||||
from zope.interface import implements, implementer
|
||||
from zope.interface import implementer
|
||||
from twisted.cred import portal, checkers, credentials, error as cred_error
|
||||
from twisted.internet import defer
|
||||
from twisted.web import resource
|
||||
from lbrynet.lbrynet_daemon.auth.util import load_api_keys, APIKey, API_KEY_NAME, save_api_keys
|
||||
from lbrynet.lbrynet_daemon.LBRYDaemon import log_dir as DATA_DIR
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
# initialize api key if none exist
|
||||
if not os.path.isfile(os.path.join(DATA_DIR, ".api_keys")):
|
||||
keys = {}
|
||||
api_key = APIKey.new()
|
||||
api_key.rename(API_KEY_NAME)
|
||||
keys.update(api_key)
|
||||
save_api_keys(keys, os.path.join(DATA_DIR, ".api_keys"))
|
||||
|
||||
|
||||
@implementer(portal.IRealm)
|
||||
class HttpPasswordRealm:
|
||||
class HttpPasswordRealm(object):
|
||||
def __init__(self, resource):
|
||||
self.resource = resource
|
||||
|
||||
|
@ -31,19 +20,28 @@ class HttpPasswordRealm:
|
|||
raise NotImplementedError()
|
||||
|
||||
|
||||
class PasswordChecker:
|
||||
implements(checkers.ICredentialsChecker)
|
||||
@implementer(checkers.ICredentialsChecker)
|
||||
class PasswordChecker(object):
|
||||
credentialInterfaces = (credentials.IUsernamePassword,)
|
||||
|
||||
def __init__(self):
|
||||
keys = load_api_keys(os.path.join(DATA_DIR, ".api_keys"))
|
||||
self.passwords = {key: keys[key]['token'] for key in keys}
|
||||
def __init__(self, passwords):
|
||||
self.passwords = passwords
|
||||
|
||||
@classmethod
|
||||
def load_file(cls, key_path):
|
||||
keys = load_api_keys(key_path)
|
||||
return cls.load(keys)
|
||||
|
||||
@classmethod
|
||||
def load(cls, password_dict):
|
||||
passwords = {key: password_dict[key].secret for key in password_dict}
|
||||
return cls(passwords)
|
||||
|
||||
def requestAvatarId(self, creds):
|
||||
if creds.username in self.passwords:
|
||||
pw = self.passwords.get(creds.username)
|
||||
pw_match = creds.checkPassword(pw)
|
||||
if pw_match is True:
|
||||
if pw_match:
|
||||
return defer.succeed(creds.username)
|
||||
log.warning('Incorrect username or password')
|
||||
return defer.fail(cred_error.UnauthorizedLogin('Incorrect username or password'))
|
||||
|
|
|
@ -1,12 +1,4 @@
|
|||
try:
|
||||
import http.client as httplib
|
||||
except ImportError:
|
||||
import httplib
|
||||
try:
|
||||
import urllib.parse as urlparse
|
||||
except ImportError:
|
||||
import urlparse
|
||||
|
||||
import urlparse
|
||||
import logging
|
||||
import requests
|
||||
import os
|
||||
|
@ -15,10 +7,12 @@ import json
|
|||
|
||||
from lbrynet.lbrynet_daemon.auth.util import load_api_keys, APIKey, API_KEY_NAME
|
||||
from lbrynet.conf import API_INTERFACE, API_ADDRESS, API_PORT
|
||||
from lbrynet.lbrynet_daemon.LBRYDaemon import log_dir as DATA_DIR
|
||||
from lbrynet.conf import DATA_DIR
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
USER_AGENT = "AuthServiceProxy/0.1"
|
||||
TWISTED_SESSION = "TWISTED_SESSION"
|
||||
LBRY_SECRET = "LBRY_SECRET"
|
||||
HTTP_TIMEOUT = 30
|
||||
|
||||
|
||||
|
@ -29,77 +23,15 @@ class JSONRPCException(Exception):
|
|||
|
||||
|
||||
class LBRYAPIClient(object):
|
||||
__api_token = None
|
||||
|
||||
def __init__(self, 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):
|
||||
self.__api_key_name = API_KEY_NAME if not key_name else key_name
|
||||
self.__api_token = key
|
||||
self.__pw_path = os.path.join(DATA_DIR, ".api_keys") if not pw_path else pw_path
|
||||
def __init__(self, key, timeout, connection, count, service, cookies, auth, url, login_url):
|
||||
self.__service_name = service
|
||||
|
||||
if not key:
|
||||
keys = load_api_keys(self.__pw_path)
|
||||
api_key = keys.get(self.__api_key_name, False)
|
||||
self.__api_token = api_key['token']
|
||||
self.__api_key_obj = api_key
|
||||
else:
|
||||
self.__api_key_obj = APIKey({'token': key})
|
||||
|
||||
if login_url is None:
|
||||
self.__service_url = "http://%s:%s@%s:%i/%s" % (self.__api_key_name, self.__api_token,
|
||||
API_INTERFACE, API_PORT, API_ADDRESS)
|
||||
else:
|
||||
self.__service_url = login_url
|
||||
|
||||
self.__api_key = key
|
||||
self.__service_url = login_url
|
||||
self.__id_count = count
|
||||
|
||||
if auth is None and connection is None and cookies is None and url is None:
|
||||
self.__url = urlparse.urlparse(self.__service_url)
|
||||
(user, passwd) = (self.__url.username, self.__url.password)
|
||||
try:
|
||||
user = user.encode('utf8')
|
||||
except AttributeError:
|
||||
pass
|
||||
try:
|
||||
passwd = passwd.encode('utf8')
|
||||
except AttributeError:
|
||||
pass
|
||||
authpair = user + b':' + passwd
|
||||
self.__auth_header = b'Basic ' + base64.b64encode(authpair)
|
||||
|
||||
self.__conn = requests.Session()
|
||||
self.__conn.auth = (user, passwd)
|
||||
|
||||
req = requests.Request(method='POST',
|
||||
url=self.__service_url,
|
||||
auth=self.__conn.auth,
|
||||
headers={'Host': self.__url.hostname,
|
||||
'User-Agent': USER_AGENT,
|
||||
'Authorization': self.__auth_header,
|
||||
'Content-type': 'application/json'},)
|
||||
r = req.prepare()
|
||||
http_response = self.__conn.send(r)
|
||||
cookies = http_response.cookies
|
||||
self.__cookies = cookies
|
||||
# print "Logged in"
|
||||
|
||||
uid = cookies.get('TWISTED_SESSION')
|
||||
api_key = APIKey.new(seed=uid)
|
||||
# print "Created temporary api key"
|
||||
|
||||
self.__api_token = api_key.token()
|
||||
self.__api_key_obj = api_key
|
||||
else:
|
||||
self.__auth_header = auth
|
||||
self.__conn = connection
|
||||
self.__cookies = cookies
|
||||
self.__url = url
|
||||
|
||||
if cookies.get("secret", False):
|
||||
self.__api_token = cookies.get("secret")
|
||||
self.__api_key_obj = APIKey({'name': self.__api_key_name, 'token': self.__api_token})
|
||||
|
||||
self.__url = url
|
||||
self.__auth_header = auth
|
||||
self.__conn = connection
|
||||
self.__cookies = cookies
|
||||
|
||||
def __getattr__(self, name):
|
||||
if name.startswith('__') and name.endswith('__'):
|
||||
|
@ -107,11 +39,11 @@ class LBRYAPIClient(object):
|
|||
raise AttributeError
|
||||
if self.__service_name is not None:
|
||||
name = "%s.%s" % (self.__service_name, name)
|
||||
return LBRYAPIClient(key_name=self.__api_key_name,
|
||||
key=self.__api_token,
|
||||
return LBRYAPIClient(key=self.__api_key,
|
||||
timeout=HTTP_TIMEOUT,
|
||||
connection=self.__conn,
|
||||
service=name,
|
||||
count=self.__id_count,
|
||||
service=name,
|
||||
cookies=self.__cookies,
|
||||
auth=self.__auth_header,
|
||||
url=self.__url,
|
||||
|
@ -124,7 +56,7 @@ class LBRYAPIClient(object):
|
|||
'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_obj.get_hmac(to_auth.decode('hex'))
|
||||
token = self.__api_key.get_hmac(to_auth.decode('hex'))
|
||||
pre_auth_postdata.update({'hmac': token})
|
||||
postdata = json.dumps(pre_auth_postdata)
|
||||
service_url = self.__service_url
|
||||
|
@ -142,12 +74,14 @@ class LBRYAPIClient(object):
|
|||
cookies=cookies)
|
||||
r = req.prepare()
|
||||
http_response = self.__conn.send(r)
|
||||
self.__cookies = http_response.cookies
|
||||
cookies = http_response.cookies
|
||||
headers = http_response.headers
|
||||
next_secret = headers.get('Next-Secret', False)
|
||||
next_secret = headers.get(LBRY_SECRET, False)
|
||||
|
||||
if next_secret:
|
||||
cookies.update({'secret': 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:
|
||||
|
@ -165,4 +99,65 @@ class LBRYAPIClient(object):
|
|||
raise JSONRPCException({
|
||||
'code': -343, 'message': 'missing JSON-RPC result'})
|
||||
else:
|
||||
return response['result']
|
||||
return response['result']
|
||||
|
||||
@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:
|
||||
# This is a new client instance, initialize the auth header and start a session
|
||||
url = urlparse.urlparse(service_url)
|
||||
(user, passwd) = (url.username, url.password)
|
||||
try:
|
||||
user = user.encode('utf8')
|
||||
except AttributeError:
|
||||
pass
|
||||
try:
|
||||
passwd = passwd.encode('utf8')
|
||||
except AttributeError:
|
||||
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,
|
||||
headers={'Host': url.hostname,
|
||||
'User-Agent': USER_AGENT,
|
||||
'Authorization': auth_header,
|
||||
'Content-type': 'application/json'},)
|
||||
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
|
||||
conn = connection
|
||||
assert cookies.get(LBRY_SECRET, False), "Missing cookie"
|
||||
secret = cookies.get(LBRY_SECRET)
|
||||
api_key = APIKey(secret, api_key_name)
|
||||
return cls(api_key, timeout, conn, id_count, service, cookies, auth_header, url, service_url)
|
|
@ -9,6 +9,7 @@ from txjsonrpc.web.jsonrpc import Handler
|
|||
|
||||
from lbrynet.core.Error import InvalidAuthenticationToken, InvalidHeaderError
|
||||
from lbrynet.lbrynet_daemon.auth.util import APIKey
|
||||
from lbrynet.lbrynet_daemon.auth.client import LBRY_SECRET
|
||||
from lbrynet.conf import ALLOWED_DURING_STARTUP
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
@ -84,21 +85,21 @@ class LBRYJSONRPCServer(jsonrpc.JSONRPC):
|
|||
return function
|
||||
|
||||
def _verify_token(session_id, message, token):
|
||||
request.setHeader("Next-Secret", "")
|
||||
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()})
|
||||
request.setHeader("Next-Secret", self.sessions.get(session_id).token())
|
||||
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)
|
||||
token = APIKey.new(seed=session_id, name=session_id)
|
||||
log.info("Initializing new api session")
|
||||
self.sessions.update({session_id: token})
|
||||
# log.info("Generated token %s", str(self.sessions[session_id]))
|
||||
|
@ -191,3 +192,5 @@ class LBRYJSONRPCServer(jsonrpc.JSONRPC):
|
|||
|
||||
def _render_response(self, result, code):
|
||||
return defer.succeed({'result': result, 'code': code})
|
||||
|
||||
|
||||
|
|
|
@ -22,24 +22,20 @@ def generate_key(x=None):
|
|||
return sha(x)
|
||||
|
||||
|
||||
class APIKey(dict):
|
||||
def __init__(self, key, name=None):
|
||||
self.key = key if isinstance(key, str) else key['token']
|
||||
self.name = name if name else hashlib.sha256(self.key).hexdigest()
|
||||
self.expiration = None if isinstance(key, str) else key.get('expiration', None)
|
||||
self.update({self.name: {'token': self.key, 'expiration': self.expiration}})
|
||||
class APIKey(object):
|
||||
def __init__(self, secret, name, expiration=None):
|
||||
self.secret = secret
|
||||
self.name = name
|
||||
self.expiration = expiration
|
||||
|
||||
@classmethod
|
||||
def new(cls, expiration=None, seed=None, name=None):
|
||||
key_val = generate_key(seed)
|
||||
key = {'token': key_val, 'expiration': expiration}
|
||||
return APIKey(key, name)
|
||||
|
||||
def token(self):
|
||||
return self[self.name]['token']
|
||||
def new(cls, seed=None, name=None, expiration=None):
|
||||
secret = generate_key(seed)
|
||||
key_name = name if name else sha(secret)
|
||||
return APIKey(secret, key_name, expiration)
|
||||
|
||||
def _raw_key(self):
|
||||
return base58.b58decode(self.token())
|
||||
return base58.b58decode(self.secret)
|
||||
|
||||
def get_hmac(self, message):
|
||||
decoded_key = self._raw_key()
|
||||
|
@ -56,27 +52,36 @@ class APIKey(dict):
|
|||
return False
|
||||
return r
|
||||
|
||||
def rename(self, name):
|
||||
old = self.keys()[0]
|
||||
t = self.pop(old)
|
||||
self.update({name: t})
|
||||
|
||||
|
||||
def load_api_keys(path):
|
||||
if not os.path.isfile(path):
|
||||
raise Exception("Invalid api key path")
|
||||
|
||||
f = open(path, "r")
|
||||
data = yaml.load(f.read())
|
||||
f.close()
|
||||
with open(path, "r") as f:
|
||||
data = yaml.load(f.read())
|
||||
|
||||
keys = {key: APIKey(data[key], name=key)[key] for key in data}
|
||||
keys_for_return = {}
|
||||
for key_name in data:
|
||||
key = data[key_name]
|
||||
secret = key['secret']
|
||||
expiration = key['expiration']
|
||||
keys_for_return.update({key_name: APIKey(secret, key_name, expiration)})
|
||||
|
||||
return keys
|
||||
return keys_for_return
|
||||
|
||||
|
||||
def save_api_keys(keys, path):
|
||||
data = yaml.safe_dump(dict(keys))
|
||||
f = open(path, "w")
|
||||
f.write(data)
|
||||
f.close()
|
||||
with open(path, "w") as f:
|
||||
key_dict = {keys[key_name].name: {'secret': keys[key_name].secret,
|
||||
'expiration': keys[key_name].expiration}
|
||||
for key_name in keys}
|
||||
data = yaml.safe_dump(key_dict)
|
||||
f.write(data)
|
||||
|
||||
|
||||
def initialize_api_key_file(key_path):
|
||||
if not os.path.isfile(key_path):
|
||||
keys = {}
|
||||
new_api_key = APIKey.new(name=API_KEY_NAME)
|
||||
keys.update({new_api_key.name: new_api_key})
|
||||
save_api_keys(keys, key_path)
|
Loading…
Reference in a new issue