This commit is contained in:
Jack 2016-09-21 03:49:52 -04:00
parent 130f9cfc4d
commit aecafbbebd
8 changed files with 173 additions and 152 deletions

View file

@ -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

View file

@ -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})

View file

@ -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

View file

@ -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:

View file

@ -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'))

View file

@ -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 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.__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.__url = url
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})
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:
@ -166,3 +100,64 @@ class LBRYAPIClient(object):
'code': -343, 'message': 'missing JSON-RPC result'})
else:
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)

View file

@ -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})

View file

@ -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")
with open(path, "r") as f:
data = yaml.load(f.read())
f.close()
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")
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)
f.close()
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)