Merge branch 'auth-requests'

This commit is contained in:
Jack Robison 2018-04-02 10:12:28 -04:00
commit e673dc6cfd
No known key found for this signature in database
GPG key ID: DF25C68FE0239BB2
8 changed files with 126 additions and 178 deletions

View file

@ -15,6 +15,9 @@ at anytime.
### Fixed
* handling error from dht clients with old `ping` method
* blobs not being re-announced if no peers successfully stored, now failed announcements are re-queued
* issue where an `AuthAPIClient` (used by `lbrynet-cli`) would fail to update its session secret and keep making new auth sessions, with every other request failing
* `use_auth_http` in a config file being overridden by the default command line argument to `lbrynet-daemon`, now the command line value will only override the config file value if it is provided
* `lbrynet-cli` not automatically switching to the authenticated client if the server is detected to be using authentication. This resulted in `lbrynet-cli` failing to run when `lbrynet-daemon` was run with the `--http-auth` flag
### Deprecated
*
@ -36,6 +39,8 @@ at anytime.
* dht logging to be more verbose with errors and warnings
* added `single_announce` and `last_announced_time` columns to the `blob` table in sqlite
* pass the sd hash to reflector ClientFactory instead of looking it up
* if the `use_authentication` setting is configured, use authentication for all api methods instead of only those with the `auth_required` decorator
* regenerate api keys on startup if the using authentication
### Added
* virtual kademlia network and mock udp transport for dht integration tests
@ -45,6 +50,8 @@ at anytime.
### Removed
* `announce_all` argument from `blob_announce`
* old `blob_announce_all` command
* `AuthJSONRPCServer.auth_required` decorator
* unused `--wallet` argument to `lbrynet-daemon`, which used to be to support `PTCWallet`.
## [0.19.2] - 2018-03-28

View file

@ -1150,7 +1150,6 @@ class Daemon(AuthJSONRPCServer):
"""
return self._render_response(conf.settings.get_adjustable_settings_dict())
@AuthJSONRPCServer.auth_required
@defer.inlineCallbacks
def jsonrpc_settings_set(self, **kwargs):
"""
@ -1495,7 +1494,6 @@ class Daemon(AuthJSONRPCServer):
response = yield self._render_response(claim_results)
defer.returnValue(response)
@AuthJSONRPCServer.auth_required
@defer.inlineCallbacks
def jsonrpc_resolve(self, force=False, uri=None, uris=[]):
"""
@ -1586,7 +1584,6 @@ class Daemon(AuthJSONRPCServer):
response = yield self._render_response(results)
defer.returnValue(response)
@AuthJSONRPCServer.auth_required
@defer.inlineCallbacks
def jsonrpc_get(self, uri, file_name=None, timeout=None):
"""
@ -1675,7 +1672,6 @@ class Daemon(AuthJSONRPCServer):
response = yield self._render_response(result)
defer.returnValue(response)
@AuthJSONRPCServer.auth_required
@defer.inlineCallbacks
def jsonrpc_file_set_status(self, status, **kwargs):
"""
@ -1716,7 +1712,6 @@ class Daemon(AuthJSONRPCServer):
response = yield self._render_response(msg)
defer.returnValue(response)
@AuthJSONRPCServer.auth_required
@defer.inlineCallbacks
def jsonrpc_file_delete(self, delete_from_download_dir=False, delete_all=False, **kwargs):
"""
@ -1797,7 +1792,6 @@ class Daemon(AuthJSONRPCServer):
cost = yield self.get_est_cost(uri, size)
defer.returnValue(cost)
@AuthJSONRPCServer.auth_required
@defer.inlineCallbacks
def jsonrpc_channel_new(self, channel_name, amount):
"""
@ -1852,7 +1846,6 @@ class Daemon(AuthJSONRPCServer):
response = yield self._render_response(result)
defer.returnValue(response)
@AuthJSONRPCServer.auth_required
@defer.inlineCallbacks
def jsonrpc_channel_list(self):
"""
@ -1874,7 +1867,6 @@ class Daemon(AuthJSONRPCServer):
defer.returnValue(response)
@AuthJSONRPCServer.deprecated("channel_list")
@AuthJSONRPCServer.auth_required
def jsonrpc_channel_list_mine(self):
"""
Get certificate claim infos for channels that can be published to (deprecated)
@ -1891,7 +1883,6 @@ class Daemon(AuthJSONRPCServer):
return self.jsonrpc_channel_list()
@AuthJSONRPCServer.auth_required
@defer.inlineCallbacks
def jsonrpc_channel_export(self, claim_id):
"""
@ -1910,7 +1901,6 @@ class Daemon(AuthJSONRPCServer):
result = yield self.session.wallet.export_certificate_info(claim_id)
defer.returnValue(result)
@AuthJSONRPCServer.auth_required
@defer.inlineCallbacks
def jsonrpc_channel_import(self, serialized_certificate_info):
"""
@ -1929,7 +1919,6 @@ class Daemon(AuthJSONRPCServer):
result = yield self.session.wallet.import_certificate_info(serialized_certificate_info)
defer.returnValue(result)
@AuthJSONRPCServer.auth_required
@defer.inlineCallbacks
def jsonrpc_publish(self, name, bid, metadata=None, file_path=None, fee=None, title=None,
description=None, author=None, language=None, license=None,
@ -2139,7 +2128,6 @@ class Daemon(AuthJSONRPCServer):
response = yield self._render_response(result)
defer.returnValue(response)
@AuthJSONRPCServer.auth_required
@defer.inlineCallbacks
def jsonrpc_claim_abandon(self, claim_id=None, txid=None, nout=None):
"""
@ -2172,7 +2160,6 @@ class Daemon(AuthJSONRPCServer):
self.analytics_manager.send_claim_action('abandon')
defer.returnValue(result)
@AuthJSONRPCServer.auth_required
@defer.inlineCallbacks
def jsonrpc_claim_new_support(self, name, claim_id, amount):
"""
@ -2200,7 +2187,6 @@ class Daemon(AuthJSONRPCServer):
self.analytics_manager.send_claim_action('new_support')
defer.returnValue(result)
@AuthJSONRPCServer.auth_required
@defer.inlineCallbacks
def jsonrpc_claim_renew(self, outpoint=None, height=None):
"""
@ -2243,7 +2229,6 @@ class Daemon(AuthJSONRPCServer):
result = yield self.session.wallet.claim_renew_all_before_expiration(height)
defer.returnValue(result)
@AuthJSONRPCServer.auth_required
@defer.inlineCallbacks
def jsonrpc_claim_send_to_address(self, claim_id, address, amount=None):
"""
@ -2276,7 +2261,6 @@ class Daemon(AuthJSONRPCServer):
defer.returnValue(response)
# TODO: claim_list_mine should be merged into claim_list, but idk how to authenticate it -Grin
@AuthJSONRPCServer.auth_required
def jsonrpc_claim_list_mine(self):
"""
List my name claims
@ -2351,7 +2335,6 @@ class Daemon(AuthJSONRPCServer):
claims = yield self.session.wallet.get_claims_for_name(name)
defer.returnValue(claims)
@AuthJSONRPCServer.auth_required
@defer.inlineCallbacks
def jsonrpc_claim_list_by_channel(self, page=0, page_size=10, uri=None, uris=[]):
"""
@ -2441,7 +2424,6 @@ class Daemon(AuthJSONRPCServer):
response = yield self._render_response(results)
defer.returnValue(response)
@AuthJSONRPCServer.auth_required
def jsonrpc_transaction_list(self):
"""
List transactions belonging to wallet
@ -2521,7 +2503,6 @@ class Daemon(AuthJSONRPCServer):
d.addCallback(lambda r: self._render_response(r))
return d
@AuthJSONRPCServer.auth_required
def jsonrpc_wallet_is_address_mine(self, address):
"""
Checks if an address is associated with the current wallet.
@ -2540,7 +2521,6 @@ class Daemon(AuthJSONRPCServer):
d.addCallback(lambda is_mine: self._render_response(is_mine))
return d
@AuthJSONRPCServer.auth_required
def jsonrpc_wallet_public_key(self, address):
"""
Get public key from wallet address
@ -2560,7 +2540,6 @@ class Daemon(AuthJSONRPCServer):
d.addCallback(lambda r: self._render_response(r))
return d
@AuthJSONRPCServer.auth_required
@defer.inlineCallbacks
def jsonrpc_wallet_list(self):
"""
@ -2580,7 +2559,6 @@ class Daemon(AuthJSONRPCServer):
response = yield self._render_response(addresses)
defer.returnValue(response)
@AuthJSONRPCServer.auth_required
def jsonrpc_wallet_new_address(self):
"""
Generate a new wallet address
@ -2604,7 +2582,6 @@ class Daemon(AuthJSONRPCServer):
d.addCallback(lambda address: self._render_response(address))
return d
@AuthJSONRPCServer.auth_required
def jsonrpc_wallet_unused_address(self):
"""
Return an address containing no balance, will create
@ -2630,7 +2607,6 @@ class Daemon(AuthJSONRPCServer):
return d
@AuthJSONRPCServer.deprecated("wallet_send")
@AuthJSONRPCServer.auth_required
@defer.inlineCallbacks
def jsonrpc_send_amount_to_address(self, amount, address):
"""
@ -2659,7 +2635,6 @@ class Daemon(AuthJSONRPCServer):
self.analytics_manager.send_credits_sent()
defer.returnValue(True)
@AuthJSONRPCServer.auth_required
@defer.inlineCallbacks
def jsonrpc_wallet_send(self, amount, address=None, claim_id=None):
"""
@ -2708,7 +2683,6 @@ class Daemon(AuthJSONRPCServer):
self.analytics_manager.send_claim_action('new_support')
defer.returnValue(result)
@AuthJSONRPCServer.auth_required
@defer.inlineCallbacks
def jsonrpc_wallet_prefill_addresses(self, num_addresses, amount, no_broadcast=False):
"""
@ -2805,7 +2779,6 @@ class Daemon(AuthJSONRPCServer):
d.addCallback(lambda r: self._render_response(r))
return d
@AuthJSONRPCServer.auth_required
@defer.inlineCallbacks
def jsonrpc_blob_get(self, blob_hash, timeout=None, encoding=None, payment_rate_manager=None):
"""
@ -2849,7 +2822,6 @@ class Daemon(AuthJSONRPCServer):
response = yield self._render_response(result)
defer.returnValue(response)
@AuthJSONRPCServer.auth_required
@defer.inlineCallbacks
def jsonrpc_blob_delete(self, blob_hash):
"""

View file

@ -6,7 +6,7 @@ from docopt import docopt
from collections import OrderedDict
from lbrynet import conf
from lbrynet.core import utils
from lbrynet.daemon.auth.client import JSONRPCException, LBRYAPIClient
from lbrynet.daemon.auth.client import JSONRPCException, LBRYAPIClient, AuthAPIClient
from lbrynet.daemon.Daemon import LOADING_WALLET_CODE, Daemon
from lbrynet.core.system_info import get_platform
from jsonrpc.common import RPCError
@ -93,12 +93,19 @@ def main():
status = api.status()
except URLError as err:
if isinstance(err, HTTPError) and err.code == UNAUTHORIZED:
print_error("Daemon requires authentication, but none was provided.",
suggest_help=False)
api = AuthAPIClient.config()
# this can happen if the daemon is using auth with the --http-auth flag
# when the config setting is to not use it
try:
status = api.status()
except:
print_error("Daemon requires authentication, but none was provided.",
suggest_help=False)
return 1
else:
print_error("Could not connect to daemon. Are you sure it's running?",
suggest_help=False)
return 1
return 1
status_code = status['startup_status']['code']

View file

@ -32,12 +32,6 @@ def start():
type=str,
default=None
)
parser.add_argument(
"--wallet",
help="lbryum or ptc for testing, default lbryum",
type=str,
default=conf.settings['wallet']
)
parser.add_argument(
"--http-auth", dest="useauth", action="store_true", default=conf.settings['use_auth_http']
)
@ -82,23 +76,25 @@ def start():
if test_internet_connection():
analytics_manager = analytics.Manager.new_instance()
start_server_and_listen(args.useauth, analytics_manager)
start_server_and_listen(analytics_manager)
reactor.run()
else:
log.info("Not connected to internet, unable to start")
def update_settings_from_args(args):
conf.settings.update({
'use_auth_http': args.useauth,
'wallet': args.wallet,
}, data_types=(conf.TYPE_CLI,))
if args.conf:
conf.conf_file = args.conf
if args.useauth:
conf.settings.update({
'use_auth_http': args.useauth,
}, data_types=(conf.TYPE_CLI,))
conf.conf_file = args.conf
@defer.inlineCallbacks
def start_server_and_listen(use_auth, analytics_manager):
def start_server_and_listen(analytics_manager):
"""
Args:
use_auth: set to true to enable http authentication
@ -107,7 +103,7 @@ def start_server_and_listen(use_auth, analytics_manager):
analytics_manager.send_server_startup()
daemon_server = DaemonServer(analytics_manager)
try:
yield daemon_server.start(use_auth)
yield daemon_server.start(conf.settings['use_auth_http'])
analytics_manager.send_server_startup_success()
except Exception as e:
log.exception('Failed to start lbrynet-daemon')

View file

@ -35,6 +35,7 @@ class PasswordChecker(object):
@classmethod
def load(cls, password_dict):
passwords = {key: password_dict[key].secret for key in password_dict}
log.info("Loaded %i api key(s)", len(passwords))
return cls(passwords)
def requestAvatarId(self, creds):
@ -45,4 +46,3 @@ class PasswordChecker(object):
return defer.succeed(creds.username)
log.warning('Incorrect username or password')
return defer.fail(cred_error.UnauthorizedLogin('Incorrect username or password'))

View file

@ -1,13 +1,12 @@
import urlparse
import logging
import requests
import os
import base64
import json
from lbrynet.daemon.auth.util import load_api_keys, APIKey, API_KEY_NAME, get_auth_message
from lbrynet import conf
import urlparse
import requests
from requests.cookies import RequestsCookieJar
import logging
from jsonrpc.proxy import JSONRPCProxy
from lbrynet import conf
from lbrynet.daemon.auth.util import load_api_keys, APIKey, API_KEY_NAME, get_auth_message
log = logging.getLogger(__name__)
USER_AGENT = "AuthServiceProxy/0.1"
@ -16,6 +15,12 @@ LBRY_SECRET = "LBRY_SECRET"
HTTP_TIMEOUT = 30
def copy_cookies(cookies):
result = RequestsCookieJar()
result.update(cookies)
return result
class JSONRPCException(Exception):
def __init__(self, rpc_error):
Exception.__init__(self)
@ -23,25 +28,25 @@ class JSONRPCException(Exception):
class AuthAPIClient(object):
def __init__(self, key, timeout, connection, count, cookies, auth, url, login_url):
def __init__(self, key, timeout, connection, count, cookies, url, login_url):
self.__api_key = key
self.__service_url = login_url
self.__id_count = count
self.__url = url
self.__auth_header = auth
self.__conn = connection
self.__cookies = cookies
self.__cookies = copy_cookies(cookies)
def __getattr__(self, name):
if name.startswith('__') and name.endswith('__'):
raise AttributeError # Python internal stuff
raise AttributeError(name)
def f(*args):
return self.call(name, args[0] if args else {})
return f
def call(self, method, params={}):
def call(self, method, params=None):
params = params or {}
self.__id_count += 1
pre_auth_post_data = {
'version': '2',
@ -50,41 +55,27 @@ class AuthAPIClient(object):
'id': self.__id_count
}
to_auth = get_auth_message(pre_auth_post_data)
token = self.__api_key.get_hmac(to_auth)
pre_auth_post_data.update({'hmac': token})
pre_auth_post_data.update({'hmac': self.__api_key.get_hmac(to_auth)})
post_data = json.dumps(pre_auth_post_data)
service_url = self.__service_url
auth_header = self.__auth_header
cookies = self.__cookies
host = self.__url.hostname
req = requests.Request(method='POST',
url=service_url,
data=post_data,
headers={
'Host': host,
'User-Agent': USER_AGENT,
'Authorization': auth_header,
'Content-type': 'application/json'
},
cookies=cookies)
r = req.prepare()
http_response = self.__conn.send(r)
cookies = http_response.cookies
headers = http_response.headers
next_secret = headers.get(LBRY_SECRET, False)
if next_secret:
self.__api_key.secret = next_secret
self.__cookies = cookies
cookies = copy_cookies(self.__cookies)
req = requests.Request(
method='POST', url=self.__service_url, data=post_data, cookies=cookies,
headers={
'Host': self.__url.hostname,
'User-Agent': USER_AGENT,
'Content-type': 'application/json'
}
)
http_response = self.__conn.send(req.prepare())
if http_response is None:
raise JSONRPCException({
'code': -342, 'message': 'missing HTTP response from server'})
http_response.raise_for_status()
next_secret = http_response.headers.get(LBRY_SECRET, False)
if next_secret:
self.__api_key.secret = next_secret
self.__cookies = copy_cookies(http_response.cookies)
response = http_response.json()
if response.get('error') is not None:
raise JSONRPCException(response['error'])
elif 'result' not in response:
@ -94,13 +85,10 @@ class AuthAPIClient(object):
return response['result']
@classmethod
def config(cls, key_name=None, key=None, pw_path=None,
timeout=HTTP_TIMEOUT,
connection=None, count=0,
cookies=None, auth=None,
url=None, login_url=None):
def config(cls, key_name=None, key=None, pw_path=None, timeout=HTTP_TIMEOUT, connection=None, count=0,
cookies=None, auth=None, url=None, login_url=None):
api_key_name = API_KEY_NAME if not key_name else 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)
@ -118,41 +106,28 @@ class AuthAPIClient(object):
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
# This is a new client instance, start an authenticated 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
cookies = RequestsCookieJar()
cookies.update(http_response.cookies)
uid = cookies.get(TWISTED_SESSION)
api_key = APIKey.new(seed=uid)
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"
if not cookies.get(LBRY_SECRET):
raise Exception("Missing cookie")
secret = cookies.get(LBRY_SECRET)
api_key = APIKey(secret, api_key_name)
return cls(api_key, timeout, conn, id_count, cookies, auth_header, url, service_url)
return cls(api_key, timeout, conn, id_count, cookies, url, service_url)
class LBRYAPIClient(object):

View file

@ -13,7 +13,7 @@ from txjsonrpc import jsonrpclib
from traceback import format_exc
from lbrynet import conf
from lbrynet.core.Error import InvalidAuthenticationToken
from lbrynet.core.Error import InvalidAuthenticationToken, InvalidHeaderError
from lbrynet.core import utils
from lbrynet.daemon.auth.util import APIKey, get_auth_message
from lbrynet.daemon.auth.client import LBRY_SECRET
@ -119,15 +119,12 @@ class JSONRPCServerType(type):
klass = type.__new__(mcs, name, bases, newattrs)
klass.callable_methods = {}
klass.deprecated_methods = {}
klass.authorized_functions = []
for methodname in dir(klass):
if methodname.startswith("jsonrpc_"):
method = getattr(klass, methodname)
if not hasattr(method, '_deprecated'):
klass.callable_methods.update({methodname.split("jsonrpc_")[1]: method})
if hasattr(method, '_auth_required'):
klass.authorized_functions.append(methodname.split("jsonrpc_")[1])
else:
klass.deprecated_methods.update({methodname.split("jsonrpc_")[1]: method})
return klass
@ -136,11 +133,6 @@ class JSONRPCServerType(type):
class AuthorizedBase(object):
__metaclass__ = JSONRPCServerType
@staticmethod
def auth_required(f):
f._auth_required = True
return f
@staticmethod
def deprecated(new_command=None):
def _deprecated_wrapper(f):
@ -151,26 +143,28 @@ class AuthorizedBase(object):
class AuthJSONRPCServer(AuthorizedBase):
"""Authorized JSONRPC server used as the base class for the LBRY API
"""
Authorized JSONRPC server used as the base class for the LBRY API
API methods are named with a leading "jsonrpc_"
Decorators:
@AuthJSONRPCServer.auth_required: this requires that the client
include a valid hmac authentication token in their request
Attributes:
allowed_during_startup (list): list of api methods that are
callable before the server has finished startup
allowed_during_startup (list): list of api methods that are callable before the server has finished startup
sessions (dict): (dict): {<session id>: <lbrynet.daemon.auth.util.APIKey>}
callable_methods (dict): {<api method name>: <api method>}
sessions (dict): dictionary of active session_id:
lbrynet.lbrynet_daemon.auth.util.APIKey values
Authentication:
If use_authentication is true, basic HTTP and HMAC authentication will be used for all requests and the
service url will require a username and password.
authorized_functions (list): list of api methods that require authentication
callable_methods (dict): dictionary of api_callable_name: method values
To start an authenticated session a client sends an HTTP POST to <user>:<password>@<api host>:<api port>.
If accepted, the server replies with a TWISTED_SESSION cookie containing a session id and the message "OK".
The client initializes their shared secret for hmac to be the b64 encoded sha256 of their session id.
To send an authenticated request a client sends an HTTP POST to the auth api url with the TWISTED_SESSION
cookie and includes a hmac token in the message using the previously set shared secret. If the token is valid
the server will randomize the shared secret and return the new value under the LBRY_SECRET header, which the
client uses to generate the token for their next request.
"""
implements(resource.IResource)
@ -178,9 +172,7 @@ class AuthJSONRPCServer(AuthorizedBase):
allowed_during_startup = []
def __init__(self, use_authentication=None):
self._use_authentication = (
use_authentication if use_authentication is not None else conf.settings['use_auth_http']
)
self._use_authentication = use_authentication or conf.settings['use_auth_http']
self.announced_startup = False
self.sessions = {}
@ -239,7 +231,9 @@ class AuthJSONRPCServer(AuthorizedBase):
def _render(self, request):
time_in = utils.now()
# assert self._check_headers(request), InvalidHeaderError
if not self._check_headers(request):
self._render_error(Failure(InvalidHeaderError()), request, None)
return server.NOT_DONE_YET
session = request.getSession()
session_id = session.uid
finished_deferred = request.notifyFinish()
@ -270,36 +264,36 @@ class AuthJSONRPCServer(AuthorizedBase):
self._render_error(JSONRPCError(None, JSONRPCError.CODE_PARSE_ERROR), request, None)
return server.NOT_DONE_YET
id_ = None
request_id = None
try:
function_name = parsed.get('method')
args = parsed.get('params', {})
id_ = parsed.get('id', None)
request_id = parsed.get('id', None)
token = parsed.pop('hmac', None)
except AttributeError as err:
log.warning(err)
self._render_error(
JSONRPCError(None, code=JSONRPCError.CODE_INVALID_REQUEST), request, id_
JSONRPCError(None, code=JSONRPCError.CODE_INVALID_REQUEST), request, request_id
)
return server.NOT_DONE_YET
reply_with_next_secret = False
if self._use_authentication:
if function_name in self.authorized_functions:
try:
self._verify_token(session_id, parsed, token)
except InvalidAuthenticationToken as err:
log.warning("API validation failed")
self._render_error(
JSONRPCError.create_from_exception(
err.message, code=JSONRPCError.CODE_AUTHENTICATION_ERROR,
traceback=format_exc()
),
request, id_
)
return server.NOT_DONE_YET
self._update_session_secret(session_id)
reply_with_next_secret = True
try:
self._verify_token(session_id, parsed, token)
except InvalidAuthenticationToken as err:
log.warning("API validation failed")
self._render_error(
JSONRPCError.create_from_exception(
err, code=JSONRPCError.CODE_AUTHENTICATION_ERROR,
traceback=format_exc()
),
request, request_id
)
return server.NOT_DONE_YET
request.addCookie("TWISTED_SESSION", session_id)
self._update_session_secret(session_id)
reply_with_next_secret = True
try:
fn = self._get_jsonrpc_method(function_name)
@ -307,7 +301,7 @@ class AuthJSONRPCServer(AuthorizedBase):
log.warning('Failed to get function %s: %s', function_name, err)
self._render_error(
JSONRPCError(None, JSONRPCError.CODE_METHOD_NOT_FOUND),
request, id_
request, request_id
)
return server.NOT_DONE_YET
except NotAllowedDuringStartupError:
@ -315,7 +309,7 @@ class AuthJSONRPCServer(AuthorizedBase):
self._render_error(
JSONRPCError("This method is unavailable until the daemon is fully started",
code=JSONRPCError.CODE_INVALID_REQUEST),
request, id_
request, request_id
)
return server.NOT_DONE_YET
@ -341,7 +335,7 @@ class AuthJSONRPCServer(AuthorizedBase):
log.warning(params_error_message)
self._render_error(
JSONRPCError(params_error_message, code=JSONRPCError.CODE_INVALID_PARAMS),
request, id_
request, request_id
)
return server.NOT_DONE_YET
@ -353,12 +347,9 @@ class AuthJSONRPCServer(AuthorizedBase):
# request.finish() from being called on a closed request.
finished_deferred.addErrback(self._handle_dropped_request, d, function_name)
d.addCallback(self._callback_render, request, id_, reply_with_next_secret)
# TODO: don't trap RuntimeError, which is presently caught to
# handle deferredLists that won't peacefully cancel, namely
# get_lbry_files
d.addErrback(trap, ConnectionDone, ConnectionLost, defer.CancelledError, RuntimeError)
d.addErrback(self._render_error, request, id_)
d.addCallback(self._callback_render, request, request_id, reply_with_next_secret)
d.addErrback(trap, ConnectionDone, ConnectionLost, defer.CancelledError)
d.addErrback(self._render_error, request, request_id)
d.addBoth(lambda _: log.debug("%s took %f",
function_name,
(utils.now() - time_in).total_seconds()))
@ -371,7 +362,7 @@ class AuthJSONRPCServer(AuthorizedBase):
@param session_id:
@return: secret
"""
log.info("Register api session")
log.info("Started new api session")
token = APIKey.new(seed=session_id)
self.sessions.update({session_id: token})
@ -382,7 +373,8 @@ class AuthJSONRPCServer(AuthorizedBase):
def _check_headers(self, request):
return (
self._check_header_source(request, 'Origin') and
self._check_header_source(request, 'Referer'))
self._check_header_source(request, 'Referer')
)
def _check_header_source(self, request, header):
"""Check if the source of the request is allowed based on the header value."""
@ -461,7 +453,7 @@ class AuthJSONRPCServer(AuthorizedBase):
return None, None
def _initialize_session(self, session_id):
if not self.sessions.get(session_id, False):
if not self.sessions.get(session_id):
self._register_user_session(session_id)
return True
return False

View file

@ -46,12 +46,13 @@ class APIKey(object):
def compare_hmac(self, message, token):
decoded_token = base58.b58decode(token)
target = base58.b58decode(self.get_hmac(message))
try:
assert len(decoded_token) == len(target), "Length mismatch"
r = hmac.compare_digest(decoded_token, target)
if len(decoded_token) != len(target):
return False
return hmac.compare_digest(decoded_token, target)
except:
return False
return r
def load_api_keys(path):
@ -67,7 +68,6 @@ def load_api_keys(path):
secret = key['secret']
expiration = key['expiration']
keys_for_return.update({key_name: APIKey(secret, key_name, expiration)})
return keys_for_return
@ -81,11 +81,10 @@ def save_api_keys(keys, path):
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)
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)
def get_auth_message(message_dict):