mirror of
https://github.com/LBRYFoundation/lbry-sdk.git
synced 2025-08-23 17:27:25 +00:00
fix lbrynet-cli when using authentication
-add explanation of daemon authentication to AuthJSONRPCServer docstring -remove auth_required decorator, use auth for all api methods if use_authentication is true -fix issues with the command line --http-auth flag to lbrynet-daemon and the use_http_auth setting in the config file
This commit is contained in:
parent
ea5190aa9a
commit
743ae59d54
8 changed files with 126 additions and 178 deletions
|
@ -15,6 +15,9 @@ at anytime.
|
||||||
### Fixed
|
### Fixed
|
||||||
* handling error from dht clients with old `ping` method
|
* 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
|
* 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
|
### Deprecated
|
||||||
*
|
*
|
||||||
|
@ -36,6 +39,8 @@ at anytime.
|
||||||
* dht logging to be more verbose with errors and warnings
|
* dht logging to be more verbose with errors and warnings
|
||||||
* added `single_announce` and `last_announced_time` columns to the `blob` table in sqlite
|
* 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
|
* 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
|
### Added
|
||||||
* virtual kademlia network and mock udp transport for dht integration tests
|
* virtual kademlia network and mock udp transport for dht integration tests
|
||||||
|
@ -45,6 +50,8 @@ at anytime.
|
||||||
### Removed
|
### Removed
|
||||||
* `announce_all` argument from `blob_announce`
|
* `announce_all` argument from `blob_announce`
|
||||||
* old `blob_announce_all` command
|
* 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
|
## [0.19.2] - 2018-03-28
|
||||||
|
|
|
@ -1150,7 +1150,6 @@ class Daemon(AuthJSONRPCServer):
|
||||||
"""
|
"""
|
||||||
return self._render_response(conf.settings.get_adjustable_settings_dict())
|
return self._render_response(conf.settings.get_adjustable_settings_dict())
|
||||||
|
|
||||||
@AuthJSONRPCServer.auth_required
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def jsonrpc_settings_set(self, **kwargs):
|
def jsonrpc_settings_set(self, **kwargs):
|
||||||
"""
|
"""
|
||||||
|
@ -1495,7 +1494,6 @@ class Daemon(AuthJSONRPCServer):
|
||||||
response = yield self._render_response(claim_results)
|
response = yield self._render_response(claim_results)
|
||||||
defer.returnValue(response)
|
defer.returnValue(response)
|
||||||
|
|
||||||
@AuthJSONRPCServer.auth_required
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def jsonrpc_resolve(self, force=False, uri=None, uris=[]):
|
def jsonrpc_resolve(self, force=False, uri=None, uris=[]):
|
||||||
"""
|
"""
|
||||||
|
@ -1586,7 +1584,6 @@ class Daemon(AuthJSONRPCServer):
|
||||||
response = yield self._render_response(results)
|
response = yield self._render_response(results)
|
||||||
defer.returnValue(response)
|
defer.returnValue(response)
|
||||||
|
|
||||||
@AuthJSONRPCServer.auth_required
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def jsonrpc_get(self, uri, file_name=None, timeout=None):
|
def jsonrpc_get(self, uri, file_name=None, timeout=None):
|
||||||
"""
|
"""
|
||||||
|
@ -1675,7 +1672,6 @@ class Daemon(AuthJSONRPCServer):
|
||||||
response = yield self._render_response(result)
|
response = yield self._render_response(result)
|
||||||
defer.returnValue(response)
|
defer.returnValue(response)
|
||||||
|
|
||||||
@AuthJSONRPCServer.auth_required
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def jsonrpc_file_set_status(self, status, **kwargs):
|
def jsonrpc_file_set_status(self, status, **kwargs):
|
||||||
"""
|
"""
|
||||||
|
@ -1716,7 +1712,6 @@ class Daemon(AuthJSONRPCServer):
|
||||||
response = yield self._render_response(msg)
|
response = yield self._render_response(msg)
|
||||||
defer.returnValue(response)
|
defer.returnValue(response)
|
||||||
|
|
||||||
@AuthJSONRPCServer.auth_required
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def jsonrpc_file_delete(self, delete_from_download_dir=False, delete_all=False, **kwargs):
|
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)
|
cost = yield self.get_est_cost(uri, size)
|
||||||
defer.returnValue(cost)
|
defer.returnValue(cost)
|
||||||
|
|
||||||
@AuthJSONRPCServer.auth_required
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def jsonrpc_channel_new(self, channel_name, amount):
|
def jsonrpc_channel_new(self, channel_name, amount):
|
||||||
"""
|
"""
|
||||||
|
@ -1852,7 +1846,6 @@ class Daemon(AuthJSONRPCServer):
|
||||||
response = yield self._render_response(result)
|
response = yield self._render_response(result)
|
||||||
defer.returnValue(response)
|
defer.returnValue(response)
|
||||||
|
|
||||||
@AuthJSONRPCServer.auth_required
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def jsonrpc_channel_list(self):
|
def jsonrpc_channel_list(self):
|
||||||
"""
|
"""
|
||||||
|
@ -1874,7 +1867,6 @@ class Daemon(AuthJSONRPCServer):
|
||||||
defer.returnValue(response)
|
defer.returnValue(response)
|
||||||
|
|
||||||
@AuthJSONRPCServer.deprecated("channel_list")
|
@AuthJSONRPCServer.deprecated("channel_list")
|
||||||
@AuthJSONRPCServer.auth_required
|
|
||||||
def jsonrpc_channel_list_mine(self):
|
def jsonrpc_channel_list_mine(self):
|
||||||
"""
|
"""
|
||||||
Get certificate claim infos for channels that can be published to (deprecated)
|
Get certificate claim infos for channels that can be published to (deprecated)
|
||||||
|
@ -1891,7 +1883,6 @@ class Daemon(AuthJSONRPCServer):
|
||||||
|
|
||||||
return self.jsonrpc_channel_list()
|
return self.jsonrpc_channel_list()
|
||||||
|
|
||||||
@AuthJSONRPCServer.auth_required
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def jsonrpc_channel_export(self, claim_id):
|
def jsonrpc_channel_export(self, claim_id):
|
||||||
"""
|
"""
|
||||||
|
@ -1910,7 +1901,6 @@ class Daemon(AuthJSONRPCServer):
|
||||||
result = yield self.session.wallet.export_certificate_info(claim_id)
|
result = yield self.session.wallet.export_certificate_info(claim_id)
|
||||||
defer.returnValue(result)
|
defer.returnValue(result)
|
||||||
|
|
||||||
@AuthJSONRPCServer.auth_required
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def jsonrpc_channel_import(self, serialized_certificate_info):
|
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)
|
result = yield self.session.wallet.import_certificate_info(serialized_certificate_info)
|
||||||
defer.returnValue(result)
|
defer.returnValue(result)
|
||||||
|
|
||||||
@AuthJSONRPCServer.auth_required
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def jsonrpc_publish(self, name, bid, metadata=None, file_path=None, fee=None, title=None,
|
def jsonrpc_publish(self, name, bid, metadata=None, file_path=None, fee=None, title=None,
|
||||||
description=None, author=None, language=None, license=None,
|
description=None, author=None, language=None, license=None,
|
||||||
|
@ -2139,7 +2128,6 @@ class Daemon(AuthJSONRPCServer):
|
||||||
response = yield self._render_response(result)
|
response = yield self._render_response(result)
|
||||||
defer.returnValue(response)
|
defer.returnValue(response)
|
||||||
|
|
||||||
@AuthJSONRPCServer.auth_required
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def jsonrpc_claim_abandon(self, claim_id=None, txid=None, nout=None):
|
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')
|
self.analytics_manager.send_claim_action('abandon')
|
||||||
defer.returnValue(result)
|
defer.returnValue(result)
|
||||||
|
|
||||||
@AuthJSONRPCServer.auth_required
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def jsonrpc_claim_new_support(self, name, claim_id, amount):
|
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')
|
self.analytics_manager.send_claim_action('new_support')
|
||||||
defer.returnValue(result)
|
defer.returnValue(result)
|
||||||
|
|
||||||
@AuthJSONRPCServer.auth_required
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def jsonrpc_claim_renew(self, outpoint=None, height=None):
|
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)
|
result = yield self.session.wallet.claim_renew_all_before_expiration(height)
|
||||||
defer.returnValue(result)
|
defer.returnValue(result)
|
||||||
|
|
||||||
@AuthJSONRPCServer.auth_required
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def jsonrpc_claim_send_to_address(self, claim_id, address, amount=None):
|
def jsonrpc_claim_send_to_address(self, claim_id, address, amount=None):
|
||||||
"""
|
"""
|
||||||
|
@ -2276,7 +2261,6 @@ class Daemon(AuthJSONRPCServer):
|
||||||
defer.returnValue(response)
|
defer.returnValue(response)
|
||||||
|
|
||||||
# TODO: claim_list_mine should be merged into claim_list, but idk how to authenticate it -Grin
|
# 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):
|
def jsonrpc_claim_list_mine(self):
|
||||||
"""
|
"""
|
||||||
List my name claims
|
List my name claims
|
||||||
|
@ -2351,7 +2335,6 @@ class Daemon(AuthJSONRPCServer):
|
||||||
claims = yield self.session.wallet.get_claims_for_name(name)
|
claims = yield self.session.wallet.get_claims_for_name(name)
|
||||||
defer.returnValue(claims)
|
defer.returnValue(claims)
|
||||||
|
|
||||||
@AuthJSONRPCServer.auth_required
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def jsonrpc_claim_list_by_channel(self, page=0, page_size=10, uri=None, uris=[]):
|
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)
|
response = yield self._render_response(results)
|
||||||
defer.returnValue(response)
|
defer.returnValue(response)
|
||||||
|
|
||||||
@AuthJSONRPCServer.auth_required
|
|
||||||
def jsonrpc_transaction_list(self):
|
def jsonrpc_transaction_list(self):
|
||||||
"""
|
"""
|
||||||
List transactions belonging to wallet
|
List transactions belonging to wallet
|
||||||
|
@ -2521,7 +2503,6 @@ class Daemon(AuthJSONRPCServer):
|
||||||
d.addCallback(lambda r: self._render_response(r))
|
d.addCallback(lambda r: self._render_response(r))
|
||||||
return d
|
return d
|
||||||
|
|
||||||
@AuthJSONRPCServer.auth_required
|
|
||||||
def jsonrpc_wallet_is_address_mine(self, address):
|
def jsonrpc_wallet_is_address_mine(self, address):
|
||||||
"""
|
"""
|
||||||
Checks if an address is associated with the current wallet.
|
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))
|
d.addCallback(lambda is_mine: self._render_response(is_mine))
|
||||||
return d
|
return d
|
||||||
|
|
||||||
@AuthJSONRPCServer.auth_required
|
|
||||||
def jsonrpc_wallet_public_key(self, address):
|
def jsonrpc_wallet_public_key(self, address):
|
||||||
"""
|
"""
|
||||||
Get public key from wallet address
|
Get public key from wallet address
|
||||||
|
@ -2560,7 +2540,6 @@ class Daemon(AuthJSONRPCServer):
|
||||||
d.addCallback(lambda r: self._render_response(r))
|
d.addCallback(lambda r: self._render_response(r))
|
||||||
return d
|
return d
|
||||||
|
|
||||||
@AuthJSONRPCServer.auth_required
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def jsonrpc_wallet_list(self):
|
def jsonrpc_wallet_list(self):
|
||||||
"""
|
"""
|
||||||
|
@ -2580,7 +2559,6 @@ class Daemon(AuthJSONRPCServer):
|
||||||
response = yield self._render_response(addresses)
|
response = yield self._render_response(addresses)
|
||||||
defer.returnValue(response)
|
defer.returnValue(response)
|
||||||
|
|
||||||
@AuthJSONRPCServer.auth_required
|
|
||||||
def jsonrpc_wallet_new_address(self):
|
def jsonrpc_wallet_new_address(self):
|
||||||
"""
|
"""
|
||||||
Generate a new wallet address
|
Generate a new wallet address
|
||||||
|
@ -2604,7 +2582,6 @@ class Daemon(AuthJSONRPCServer):
|
||||||
d.addCallback(lambda address: self._render_response(address))
|
d.addCallback(lambda address: self._render_response(address))
|
||||||
return d
|
return d
|
||||||
|
|
||||||
@AuthJSONRPCServer.auth_required
|
|
||||||
def jsonrpc_wallet_unused_address(self):
|
def jsonrpc_wallet_unused_address(self):
|
||||||
"""
|
"""
|
||||||
Return an address containing no balance, will create
|
Return an address containing no balance, will create
|
||||||
|
@ -2630,7 +2607,6 @@ class Daemon(AuthJSONRPCServer):
|
||||||
return d
|
return d
|
||||||
|
|
||||||
@AuthJSONRPCServer.deprecated("wallet_send")
|
@AuthJSONRPCServer.deprecated("wallet_send")
|
||||||
@AuthJSONRPCServer.auth_required
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def jsonrpc_send_amount_to_address(self, amount, address):
|
def jsonrpc_send_amount_to_address(self, amount, address):
|
||||||
"""
|
"""
|
||||||
|
@ -2659,7 +2635,6 @@ class Daemon(AuthJSONRPCServer):
|
||||||
self.analytics_manager.send_credits_sent()
|
self.analytics_manager.send_credits_sent()
|
||||||
defer.returnValue(True)
|
defer.returnValue(True)
|
||||||
|
|
||||||
@AuthJSONRPCServer.auth_required
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def jsonrpc_wallet_send(self, amount, address=None, claim_id=None):
|
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')
|
self.analytics_manager.send_claim_action('new_support')
|
||||||
defer.returnValue(result)
|
defer.returnValue(result)
|
||||||
|
|
||||||
@AuthJSONRPCServer.auth_required
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def jsonrpc_wallet_prefill_addresses(self, num_addresses, amount, no_broadcast=False):
|
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))
|
d.addCallback(lambda r: self._render_response(r))
|
||||||
return d
|
return d
|
||||||
|
|
||||||
@AuthJSONRPCServer.auth_required
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def jsonrpc_blob_get(self, blob_hash, timeout=None, encoding=None, payment_rate_manager=None):
|
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)
|
response = yield self._render_response(result)
|
||||||
defer.returnValue(response)
|
defer.returnValue(response)
|
||||||
|
|
||||||
@AuthJSONRPCServer.auth_required
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def jsonrpc_blob_delete(self, blob_hash):
|
def jsonrpc_blob_delete(self, blob_hash):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -6,7 +6,7 @@ from docopt import docopt
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
from lbrynet import conf
|
from lbrynet import conf
|
||||||
from lbrynet.core import utils
|
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.daemon.Daemon import LOADING_WALLET_CODE, Daemon
|
||||||
from lbrynet.core.system_info import get_platform
|
from lbrynet.core.system_info import get_platform
|
||||||
from jsonrpc.common import RPCError
|
from jsonrpc.common import RPCError
|
||||||
|
@ -93,12 +93,19 @@ def main():
|
||||||
status = api.status()
|
status = api.status()
|
||||||
except URLError as err:
|
except URLError as err:
|
||||||
if isinstance(err, HTTPError) and err.code == UNAUTHORIZED:
|
if isinstance(err, HTTPError) and err.code == UNAUTHORIZED:
|
||||||
print_error("Daemon requires authentication, but none was provided.",
|
api = AuthAPIClient.config()
|
||||||
suggest_help=False)
|
# 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:
|
else:
|
||||||
print_error("Could not connect to daemon. Are you sure it's running?",
|
print_error("Could not connect to daemon. Are you sure it's running?",
|
||||||
suggest_help=False)
|
suggest_help=False)
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
status_code = status['startup_status']['code']
|
status_code = status['startup_status']['code']
|
||||||
|
|
||||||
|
|
|
@ -32,12 +32,6 @@ def start():
|
||||||
type=str,
|
type=str,
|
||||||
default=None
|
default=None
|
||||||
)
|
)
|
||||||
parser.add_argument(
|
|
||||||
"--wallet",
|
|
||||||
help="lbryum or ptc for testing, default lbryum",
|
|
||||||
type=str,
|
|
||||||
default=conf.settings['wallet']
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--http-auth", dest="useauth", action="store_true", default=conf.settings['use_auth_http']
|
"--http-auth", dest="useauth", action="store_true", default=conf.settings['use_auth_http']
|
||||||
)
|
)
|
||||||
|
@ -82,23 +76,25 @@ def start():
|
||||||
|
|
||||||
if test_internet_connection():
|
if test_internet_connection():
|
||||||
analytics_manager = analytics.Manager.new_instance()
|
analytics_manager = analytics.Manager.new_instance()
|
||||||
start_server_and_listen(args.useauth, analytics_manager)
|
start_server_and_listen(analytics_manager)
|
||||||
reactor.run()
|
reactor.run()
|
||||||
else:
|
else:
|
||||||
log.info("Not connected to internet, unable to start")
|
log.info("Not connected to internet, unable to start")
|
||||||
|
|
||||||
|
|
||||||
def update_settings_from_args(args):
|
def update_settings_from_args(args):
|
||||||
conf.settings.update({
|
if args.conf:
|
||||||
'use_auth_http': args.useauth,
|
conf.conf_file = args.conf
|
||||||
'wallet': args.wallet,
|
|
||||||
}, data_types=(conf.TYPE_CLI,))
|
if args.useauth:
|
||||||
|
conf.settings.update({
|
||||||
|
'use_auth_http': args.useauth,
|
||||||
|
}, data_types=(conf.TYPE_CLI,))
|
||||||
|
|
||||||
conf.conf_file = args.conf
|
|
||||||
|
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def start_server_and_listen(use_auth, analytics_manager):
|
def start_server_and_listen(analytics_manager):
|
||||||
"""
|
"""
|
||||||
Args:
|
Args:
|
||||||
use_auth: set to true to enable http authentication
|
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()
|
analytics_manager.send_server_startup()
|
||||||
daemon_server = DaemonServer(analytics_manager)
|
daemon_server = DaemonServer(analytics_manager)
|
||||||
try:
|
try:
|
||||||
yield daemon_server.start(use_auth)
|
yield daemon_server.start(conf.settings['use_auth_http'])
|
||||||
analytics_manager.send_server_startup_success()
|
analytics_manager.send_server_startup_success()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
log.exception('Failed to start lbrynet-daemon')
|
log.exception('Failed to start lbrynet-daemon')
|
||||||
|
|
|
@ -35,6 +35,7 @@ class PasswordChecker(object):
|
||||||
@classmethod
|
@classmethod
|
||||||
def load(cls, password_dict):
|
def load(cls, password_dict):
|
||||||
passwords = {key: password_dict[key].secret for key in 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)
|
return cls(passwords)
|
||||||
|
|
||||||
def requestAvatarId(self, creds):
|
def requestAvatarId(self, creds):
|
||||||
|
@ -45,4 +46,3 @@ class PasswordChecker(object):
|
||||||
return defer.succeed(creds.username)
|
return defer.succeed(creds.username)
|
||||||
log.warning('Incorrect username or password')
|
log.warning('Incorrect username or password')
|
||||||
return defer.fail(cred_error.UnauthorizedLogin('Incorrect username or password'))
|
return defer.fail(cred_error.UnauthorizedLogin('Incorrect username or password'))
|
||||||
|
|
||||||
|
|
|
@ -1,13 +1,12 @@
|
||||||
import urlparse
|
|
||||||
import logging
|
|
||||||
import requests
|
|
||||||
import os
|
import os
|
||||||
import base64
|
|
||||||
import json
|
import json
|
||||||
|
import urlparse
|
||||||
from lbrynet.daemon.auth.util import load_api_keys, APIKey, API_KEY_NAME, get_auth_message
|
import requests
|
||||||
from lbrynet import conf
|
from requests.cookies import RequestsCookieJar
|
||||||
|
import logging
|
||||||
from jsonrpc.proxy import JSONRPCProxy
|
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__)
|
log = logging.getLogger(__name__)
|
||||||
USER_AGENT = "AuthServiceProxy/0.1"
|
USER_AGENT = "AuthServiceProxy/0.1"
|
||||||
|
@ -16,6 +15,12 @@ LBRY_SECRET = "LBRY_SECRET"
|
||||||
HTTP_TIMEOUT = 30
|
HTTP_TIMEOUT = 30
|
||||||
|
|
||||||
|
|
||||||
|
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):
|
||||||
Exception.__init__(self)
|
Exception.__init__(self)
|
||||||
|
@ -23,25 +28,25 @@ class JSONRPCException(Exception):
|
||||||
|
|
||||||
|
|
||||||
class AuthAPIClient(object):
|
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.__api_key = key
|
||||||
self.__service_url = login_url
|
self.__service_url = login_url
|
||||||
self.__id_count = count
|
self.__id_count = count
|
||||||
self.__url = url
|
self.__url = url
|
||||||
self.__auth_header = auth
|
|
||||||
self.__conn = connection
|
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('__'):
|
||||||
raise AttributeError # Python internal stuff
|
raise AttributeError(name)
|
||||||
|
|
||||||
def f(*args):
|
def f(*args):
|
||||||
return self.call(name, args[0] if args else {})
|
return self.call(name, args[0] if args else {})
|
||||||
|
|
||||||
return f
|
return f
|
||||||
|
|
||||||
def call(self, method, params={}):
|
def call(self, method, params=None):
|
||||||
|
params = params or {}
|
||||||
self.__id_count += 1
|
self.__id_count += 1
|
||||||
pre_auth_post_data = {
|
pre_auth_post_data = {
|
||||||
'version': '2',
|
'version': '2',
|
||||||
|
@ -50,41 +55,27 @@ class AuthAPIClient(object):
|
||||||
'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)
|
||||||
token = self.__api_key.get_hmac(to_auth)
|
pre_auth_post_data.update({'hmac': self.__api_key.get_hmac(to_auth)})
|
||||||
pre_auth_post_data.update({'hmac': token})
|
|
||||||
post_data = json.dumps(pre_auth_post_data)
|
post_data = json.dumps(pre_auth_post_data)
|
||||||
service_url = self.__service_url
|
cookies = copy_cookies(self.__cookies)
|
||||||
auth_header = self.__auth_header
|
req = requests.Request(
|
||||||
cookies = self.__cookies
|
method='POST', url=self.__service_url, data=post_data, cookies=cookies,
|
||||||
host = self.__url.hostname
|
headers={
|
||||||
|
'Host': self.__url.hostname,
|
||||||
req = requests.Request(method='POST',
|
'User-Agent': USER_AGENT,
|
||||||
url=service_url,
|
'Content-type': 'application/json'
|
||||||
data=post_data,
|
}
|
||||||
headers={
|
)
|
||||||
'Host': host,
|
http_response = self.__conn.send(req.prepare())
|
||||||
'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
|
|
||||||
|
|
||||||
if http_response is None:
|
if http_response is None:
|
||||||
raise JSONRPCException({
|
raise JSONRPCException({
|
||||||
'code': -342, 'message': 'missing HTTP response from server'})
|
'code': -342, 'message': 'missing HTTP response from server'})
|
||||||
|
|
||||||
http_response.raise_for_status()
|
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()
|
response = http_response.json()
|
||||||
|
|
||||||
if response.get('error') is not None:
|
if response.get('error') is not None:
|
||||||
raise JSONRPCException(response['error'])
|
raise JSONRPCException(response['error'])
|
||||||
elif 'result' not in response:
|
elif 'result' not in response:
|
||||||
|
@ -94,13 +85,10 @@ class AuthAPIClient(object):
|
||||||
return response['result']
|
return response['result']
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def config(cls, key_name=None, key=None, pw_path=None,
|
def config(cls, key_name=None, key=None, pw_path=None, timeout=HTTP_TIMEOUT, connection=None, count=0,
|
||||||
timeout=HTTP_TIMEOUT,
|
cookies=None, auth=None, url=None, login_url=None):
|
||||||
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
|
pw_path = os.path.join(conf.settings['data_dir'], ".api_keys") if not pw_path else pw_path
|
||||||
if not key:
|
if not key:
|
||||||
keys = load_api_keys(pw_path)
|
keys = load_api_keys(pw_path)
|
||||||
|
@ -118,41 +106,28 @@ class AuthAPIClient(object):
|
||||||
id_count = count
|
id_count = count
|
||||||
|
|
||||||
if auth is None and connection is None and cookies is None and url is None:
|
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)
|
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 = requests.Session()
|
||||||
conn.auth = (user, passwd)
|
|
||||||
req = requests.Request(method='POST',
|
req = requests.Request(method='POST',
|
||||||
url=service_url,
|
url=service_url,
|
||||||
auth=conn.auth,
|
|
||||||
headers={'Host': url.hostname,
|
headers={'Host': url.hostname,
|
||||||
'User-Agent': USER_AGENT,
|
'User-Agent': USER_AGENT,
|
||||||
'Authorization': auth_header,
|
|
||||||
'Content-type': 'application/json'},)
|
'Content-type': 'application/json'},)
|
||||||
r = req.prepare()
|
r = req.prepare()
|
||||||
http_response = conn.send(r)
|
http_response = conn.send(r)
|
||||||
cookies = http_response.cookies
|
cookies = RequestsCookieJar()
|
||||||
|
cookies.update(http_response.cookies)
|
||||||
uid = cookies.get(TWISTED_SESSION)
|
uid = cookies.get(TWISTED_SESSION)
|
||||||
api_key = APIKey.new(seed=uid)
|
api_key = APIKey.new(seed=uid)
|
||||||
else:
|
else:
|
||||||
# This is a client that already has a session, use it
|
# This is a client that already has a session, use it
|
||||||
auth_header = auth
|
|
||||||
conn = connection
|
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)
|
secret = cookies.get(LBRY_SECRET)
|
||||||
api_key = APIKey(secret, api_key_name)
|
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):
|
class LBRYAPIClient(object):
|
||||||
|
|
|
@ -13,7 +13,7 @@ from txjsonrpc import jsonrpclib
|
||||||
from traceback import format_exc
|
from traceback import format_exc
|
||||||
|
|
||||||
from lbrynet import conf
|
from lbrynet import conf
|
||||||
from lbrynet.core.Error import InvalidAuthenticationToken
|
from lbrynet.core.Error import InvalidAuthenticationToken, InvalidHeaderError
|
||||||
from lbrynet.core import utils
|
from lbrynet.core import utils
|
||||||
from lbrynet.daemon.auth.util import APIKey, get_auth_message
|
from lbrynet.daemon.auth.util import APIKey, get_auth_message
|
||||||
from lbrynet.daemon.auth.client import LBRY_SECRET
|
from lbrynet.daemon.auth.client import LBRY_SECRET
|
||||||
|
@ -119,15 +119,12 @@ class JSONRPCServerType(type):
|
||||||
klass = type.__new__(mcs, name, bases, newattrs)
|
klass = type.__new__(mcs, name, bases, newattrs)
|
||||||
klass.callable_methods = {}
|
klass.callable_methods = {}
|
||||||
klass.deprecated_methods = {}
|
klass.deprecated_methods = {}
|
||||||
klass.authorized_functions = []
|
|
||||||
|
|
||||||
for methodname in dir(klass):
|
for methodname in dir(klass):
|
||||||
if methodname.startswith("jsonrpc_"):
|
if methodname.startswith("jsonrpc_"):
|
||||||
method = getattr(klass, methodname)
|
method = getattr(klass, methodname)
|
||||||
if not hasattr(method, '_deprecated'):
|
if not hasattr(method, '_deprecated'):
|
||||||
klass.callable_methods.update({methodname.split("jsonrpc_")[1]: method})
|
klass.callable_methods.update({methodname.split("jsonrpc_")[1]: method})
|
||||||
if hasattr(method, '_auth_required'):
|
|
||||||
klass.authorized_functions.append(methodname.split("jsonrpc_")[1])
|
|
||||||
else:
|
else:
|
||||||
klass.deprecated_methods.update({methodname.split("jsonrpc_")[1]: method})
|
klass.deprecated_methods.update({methodname.split("jsonrpc_")[1]: method})
|
||||||
return klass
|
return klass
|
||||||
|
@ -136,11 +133,6 @@ class JSONRPCServerType(type):
|
||||||
class AuthorizedBase(object):
|
class AuthorizedBase(object):
|
||||||
__metaclass__ = JSONRPCServerType
|
__metaclass__ = JSONRPCServerType
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def auth_required(f):
|
|
||||||
f._auth_required = True
|
|
||||||
return f
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def deprecated(new_command=None):
|
def deprecated(new_command=None):
|
||||||
def _deprecated_wrapper(f):
|
def _deprecated_wrapper(f):
|
||||||
|
@ -151,26 +143,28 @@ class AuthorizedBase(object):
|
||||||
|
|
||||||
|
|
||||||
class AuthJSONRPCServer(AuthorizedBase):
|
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_"
|
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:
|
Attributes:
|
||||||
allowed_during_startup (list): list of api methods that are
|
allowed_during_startup (list): list of api methods that are callable before the server has finished startup
|
||||||
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:
|
Authentication:
|
||||||
lbrynet.lbrynet_daemon.auth.util.APIKey values
|
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
|
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".
|
||||||
callable_methods (dict): dictionary of api_callable_name: method values
|
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)
|
implements(resource.IResource)
|
||||||
|
|
||||||
|
@ -178,9 +172,7 @@ class AuthJSONRPCServer(AuthorizedBase):
|
||||||
allowed_during_startup = []
|
allowed_during_startup = []
|
||||||
|
|
||||||
def __init__(self, use_authentication=None):
|
def __init__(self, use_authentication=None):
|
||||||
self._use_authentication = (
|
self._use_authentication = use_authentication or conf.settings['use_auth_http']
|
||||||
use_authentication if use_authentication is not None else conf.settings['use_auth_http']
|
|
||||||
)
|
|
||||||
self.announced_startup = False
|
self.announced_startup = False
|
||||||
self.sessions = {}
|
self.sessions = {}
|
||||||
|
|
||||||
|
@ -239,7 +231,9 @@ class AuthJSONRPCServer(AuthorizedBase):
|
||||||
|
|
||||||
def _render(self, request):
|
def _render(self, request):
|
||||||
time_in = utils.now()
|
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 = request.getSession()
|
||||||
session_id = session.uid
|
session_id = session.uid
|
||||||
finished_deferred = request.notifyFinish()
|
finished_deferred = request.notifyFinish()
|
||||||
|
@ -270,36 +264,36 @@ class AuthJSONRPCServer(AuthorizedBase):
|
||||||
self._render_error(JSONRPCError(None, JSONRPCError.CODE_PARSE_ERROR), request, None)
|
self._render_error(JSONRPCError(None, JSONRPCError.CODE_PARSE_ERROR), request, None)
|
||||||
return server.NOT_DONE_YET
|
return server.NOT_DONE_YET
|
||||||
|
|
||||||
id_ = None
|
request_id = None
|
||||||
try:
|
try:
|
||||||
function_name = parsed.get('method')
|
function_name = parsed.get('method')
|
||||||
args = parsed.get('params', {})
|
args = parsed.get('params', {})
|
||||||
id_ = parsed.get('id', None)
|
request_id = parsed.get('id', None)
|
||||||
token = parsed.pop('hmac', None)
|
token = parsed.pop('hmac', None)
|
||||||
except AttributeError as err:
|
except AttributeError as err:
|
||||||
log.warning(err)
|
log.warning(err)
|
||||||
self._render_error(
|
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
|
return server.NOT_DONE_YET
|
||||||
|
|
||||||
reply_with_next_secret = False
|
reply_with_next_secret = False
|
||||||
if self._use_authentication:
|
if self._use_authentication:
|
||||||
if function_name in self.authorized_functions:
|
try:
|
||||||
try:
|
self._verify_token(session_id, parsed, token)
|
||||||
self._verify_token(session_id, parsed, token)
|
except InvalidAuthenticationToken as err:
|
||||||
except InvalidAuthenticationToken as err:
|
log.warning("API validation failed")
|
||||||
log.warning("API validation failed")
|
self._render_error(
|
||||||
self._render_error(
|
JSONRPCError.create_from_exception(
|
||||||
JSONRPCError.create_from_exception(
|
err, code=JSONRPCError.CODE_AUTHENTICATION_ERROR,
|
||||||
err.message, code=JSONRPCError.CODE_AUTHENTICATION_ERROR,
|
traceback=format_exc()
|
||||||
traceback=format_exc()
|
),
|
||||||
),
|
request, request_id
|
||||||
request, id_
|
)
|
||||||
)
|
return server.NOT_DONE_YET
|
||||||
return server.NOT_DONE_YET
|
request.addCookie("TWISTED_SESSION", session_id)
|
||||||
self._update_session_secret(session_id)
|
self._update_session_secret(session_id)
|
||||||
reply_with_next_secret = True
|
reply_with_next_secret = True
|
||||||
|
|
||||||
try:
|
try:
|
||||||
fn = self._get_jsonrpc_method(function_name)
|
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)
|
log.warning('Failed to get function %s: %s', function_name, err)
|
||||||
self._render_error(
|
self._render_error(
|
||||||
JSONRPCError(None, JSONRPCError.CODE_METHOD_NOT_FOUND),
|
JSONRPCError(None, JSONRPCError.CODE_METHOD_NOT_FOUND),
|
||||||
request, id_
|
request, request_id
|
||||||
)
|
)
|
||||||
return server.NOT_DONE_YET
|
return server.NOT_DONE_YET
|
||||||
except NotAllowedDuringStartupError:
|
except NotAllowedDuringStartupError:
|
||||||
|
@ -315,7 +309,7 @@ class AuthJSONRPCServer(AuthorizedBase):
|
||||||
self._render_error(
|
self._render_error(
|
||||||
JSONRPCError("This method is unavailable until the daemon is fully started",
|
JSONRPCError("This method is unavailable until the daemon is fully started",
|
||||||
code=JSONRPCError.CODE_INVALID_REQUEST),
|
code=JSONRPCError.CODE_INVALID_REQUEST),
|
||||||
request, id_
|
request, request_id
|
||||||
)
|
)
|
||||||
return server.NOT_DONE_YET
|
return server.NOT_DONE_YET
|
||||||
|
|
||||||
|
@ -341,7 +335,7 @@ class AuthJSONRPCServer(AuthorizedBase):
|
||||||
log.warning(params_error_message)
|
log.warning(params_error_message)
|
||||||
self._render_error(
|
self._render_error(
|
||||||
JSONRPCError(params_error_message, code=JSONRPCError.CODE_INVALID_PARAMS),
|
JSONRPCError(params_error_message, code=JSONRPCError.CODE_INVALID_PARAMS),
|
||||||
request, id_
|
request, request_id
|
||||||
)
|
)
|
||||||
return server.NOT_DONE_YET
|
return server.NOT_DONE_YET
|
||||||
|
|
||||||
|
@ -353,12 +347,9 @@ class AuthJSONRPCServer(AuthorizedBase):
|
||||||
# request.finish() from being called on a closed request.
|
# request.finish() from being called on a closed request.
|
||||||
finished_deferred.addErrback(self._handle_dropped_request, d, function_name)
|
finished_deferred.addErrback(self._handle_dropped_request, d, function_name)
|
||||||
|
|
||||||
d.addCallback(self._callback_render, request, id_, reply_with_next_secret)
|
d.addCallback(self._callback_render, request, request_id, reply_with_next_secret)
|
||||||
# TODO: don't trap RuntimeError, which is presently caught to
|
d.addErrback(trap, ConnectionDone, ConnectionLost, defer.CancelledError)
|
||||||
# handle deferredLists that won't peacefully cancel, namely
|
d.addErrback(self._render_error, request, request_id)
|
||||||
# get_lbry_files
|
|
||||||
d.addErrback(trap, ConnectionDone, ConnectionLost, defer.CancelledError, RuntimeError)
|
|
||||||
d.addErrback(self._render_error, request, id_)
|
|
||||||
d.addBoth(lambda _: log.debug("%s took %f",
|
d.addBoth(lambda _: log.debug("%s took %f",
|
||||||
function_name,
|
function_name,
|
||||||
(utils.now() - time_in).total_seconds()))
|
(utils.now() - time_in).total_seconds()))
|
||||||
|
@ -371,7 +362,7 @@ class AuthJSONRPCServer(AuthorizedBase):
|
||||||
@param session_id:
|
@param session_id:
|
||||||
@return: secret
|
@return: secret
|
||||||
"""
|
"""
|
||||||
log.info("Register api session")
|
log.info("Started new api session")
|
||||||
token = APIKey.new(seed=session_id)
|
token = APIKey.new(seed=session_id)
|
||||||
self.sessions.update({session_id: token})
|
self.sessions.update({session_id: token})
|
||||||
|
|
||||||
|
@ -382,7 +373,8 @@ class AuthJSONRPCServer(AuthorizedBase):
|
||||||
def _check_headers(self, request):
|
def _check_headers(self, request):
|
||||||
return (
|
return (
|
||||||
self._check_header_source(request, 'Origin') and
|
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):
|
def _check_header_source(self, request, header):
|
||||||
"""Check if the source of the request is allowed based on the header value."""
|
"""Check if the source of the request is allowed based on the header value."""
|
||||||
|
@ -461,7 +453,7 @@ class AuthJSONRPCServer(AuthorizedBase):
|
||||||
return None, None
|
return None, None
|
||||||
|
|
||||||
def _initialize_session(self, session_id):
|
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)
|
self._register_user_session(session_id)
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
|
@ -46,12 +46,13 @@ class APIKey(object):
|
||||||
def compare_hmac(self, message, token):
|
def compare_hmac(self, message, token):
|
||||||
decoded_token = base58.b58decode(token)
|
decoded_token = base58.b58decode(token)
|
||||||
target = base58.b58decode(self.get_hmac(message))
|
target = base58.b58decode(self.get_hmac(message))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
assert len(decoded_token) == len(target), "Length mismatch"
|
if len(decoded_token) != len(target):
|
||||||
r = hmac.compare_digest(decoded_token, target)
|
return False
|
||||||
|
return hmac.compare_digest(decoded_token, target)
|
||||||
except:
|
except:
|
||||||
return False
|
return False
|
||||||
return r
|
|
||||||
|
|
||||||
|
|
||||||
def load_api_keys(path):
|
def load_api_keys(path):
|
||||||
|
@ -67,7 +68,6 @@ def load_api_keys(path):
|
||||||
secret = key['secret']
|
secret = key['secret']
|
||||||
expiration = key['expiration']
|
expiration = key['expiration']
|
||||||
keys_for_return.update({key_name: APIKey(secret, key_name, expiration)})
|
keys_for_return.update({key_name: APIKey(secret, key_name, expiration)})
|
||||||
|
|
||||||
return keys_for_return
|
return keys_for_return
|
||||||
|
|
||||||
|
|
||||||
|
@ -81,11 +81,10 @@ def save_api_keys(keys, path):
|
||||||
|
|
||||||
|
|
||||||
def initialize_api_key_file(key_path):
|
def initialize_api_key_file(key_path):
|
||||||
if not os.path.isfile(key_path):
|
keys = {}
|
||||||
keys = {}
|
new_api_key = APIKey.new(name=API_KEY_NAME)
|
||||||
new_api_key = APIKey.new(name=API_KEY_NAME)
|
keys.update({new_api_key.name: new_api_key})
|
||||||
keys.update({new_api_key.name: new_api_key})
|
save_api_keys(keys, key_path)
|
||||||
save_api_keys(keys, key_path)
|
|
||||||
|
|
||||||
|
|
||||||
def get_auth_message(message_dict):
|
def get_auth_message(message_dict):
|
||||||
|
|
Loading…
Add table
Reference in a new issue