From fb658390327e1b6694e0ddc913709740d90b8151 Mon Sep 17 00:00:00 2001 From: Victor Grau Serrat Date: Tue, 6 Feb 2018 23:33:34 -0700 Subject: [PATCH] BLD: marketplace: getkeysecret is authenticated --- catalyst/marketplace/marketplace.py | 9 +-- catalyst/marketplace/marketplace_errors.py | 15 ++++- catalyst/marketplace/utils/auth_utils.py | 77 ++++++++++++++++++---- 3 files changed, 80 insertions(+), 21 deletions(-) diff --git a/catalyst/marketplace/marketplace.py b/catalyst/marketplace/marketplace.py index 71620b66..2eced9ee 100644 --- a/catalyst/marketplace/marketplace.py +++ b/catalyst/marketplace/marketplace.py @@ -422,8 +422,7 @@ class Marketplace: key = self.addresses[address_i]['key'] secret = self.addresses[address_i]['secret'] else: - # TODO: Verify signature to obtain key/secret pair - key, secret = get_key_secret(address, ds_name) + key, secret = get_key_secret(address) headers = get_signed_headers(ds_name, key, secret) log.debug('Starting download of dataset for ingestion...') @@ -596,8 +595,7 @@ class Marketplace: key = self.addresses[address_i]['key'] secret = self.addresses[address_i]['secret'] else: - # TODO: Verify signature to obtain key/secret pair - key, secret = get_key_secret(address, dataset) + key, secret = get_key_secret(address) tx = self.mkt_contract.functions.register( bytes32(dataset), @@ -655,8 +653,7 @@ class Marketplace: key = match['key'] secret = match['secret'] else: - # TODO: Verify signature to obtain key/secret pair - key, secret = get_key_secret(provider_info[0], dataset) + key, secret = get_key_secret(provider_info[0]) headers = get_signed_headers(dataset, key, secret) filenames = glob.glob(os.path.join(datadir, '*.csv')) diff --git a/catalyst/marketplace/marketplace_errors.py b/catalyst/marketplace/marketplace_errors.py index ed3d0fa4..953941fc 100644 --- a/catalyst/marketplace/marketplace_errors.py +++ b/catalyst/marketplace/marketplace_errors.py @@ -8,7 +8,8 @@ def silent_except_hook(exctype, excvalue, exctraceback): if exctype in [MarketplacePubAddressEmpty, MarketplaceDatasetNotFound, MarketplaceNoAddressMatch, MarketplaceHTTPRequest, MarketplaceNoCSVFiles, MarketplaceContractDataNoMatch, - MarketplaceSubscriptionExpired]: + MarketplaceSubscriptionExpired, + MarketplaceWalletNotSupported, MarketplaceEmptySignature]: fn = traceback.extract_tb(exctraceback)[-1][0] ln = traceback.extract_tb(exctraceback)[-1][1] print("Error traceback: {1} (line {2})\n" @@ -66,3 +67,15 @@ class MarketplaceSubscriptionExpired(ZiplineError): 'following command:\n' 'catalyst marketplace subscribe --dataset={dataset}' ) + + +class MarketplaceWalletNotSupported(ZiplineError): + msg = ( + 'Wallet {wallet} is not supported.' + ) + + +class MarketplaceEmptySignature(ZiplineError): + msg = ( + 'Signature cannot be empty.' + ) diff --git a/catalyst/marketplace/utils/auth_utils.py b/catalyst/marketplace/utils/auth_utils.py index d761cf8d..4979b6f3 100644 --- a/catalyst/marketplace/utils/auth_utils.py +++ b/catalyst/marketplace/utils/auth_utils.py @@ -5,13 +5,14 @@ import requests import time from catalyst.marketplace.marketplace_errors import ( - MarketplaceHTTPRequest) + MarketplaceHTTPRequest, MarketplaceWalletNotSupported, + MarketplaceEmptySignature) from catalyst.marketplace.utils.path_utils import ( get_user_pubaddr, save_user_pubaddr) from catalyst.constants import AUTH_SERVER -def get_key_secret(pubAddr, dataset): +def get_key_secret(pubAddr, wallet='mew'): """ Obtain a new key/secret pair from authentication server @@ -29,25 +30,73 @@ def get_key_secret(pubAddr, dataset): session = requests.Session() response = session.get('{}/marketplace/getkeysecret'.format(AUTH_SERVER), headers={ - 'pubAddr': pubAddr, - 'dataset': dataset}) + 'Authorization': 'Digest username="{0}"'.format( + pubAddr)}) - if 'error' in response.json(): + if response.status_code != 401: raise MarketplaceHTTPRequest(request=str('obtain key/secret'), - error=str(response.json()['error'])) + error='Unexpected response code: ' + '{}'.format(response.status_code)) - addresses = get_user_pubaddr() + header = response.headers.get('WWW-Authenticate') + auth_type, auth_info = header.split(None, 1) + d = requests.utils.parse_dict_header(auth_info) - match = next((l for l in addresses if - l['pubAddr'] == pubAddr), None) - match['key'] = response.json()['key'] - match['secret'] = response.json()['secret'] + nonce = '0x{}'.format(d['nonce']) - addresses[addresses.index(match)] = match + if wallet == 'mew': + print('\nObtaining a key/secret pair to streamline all future ' + 'requests with the authentication server.\n' + 'Visit https://www.myetherwallet.com/signmsg.html and sign the' + 'following message:\n{}'.format(nonce)) + signature = input('Copy and Paste the "sig" field from ' + 'the signature here (without the double quotes, ' + 'only the HEX value:\n') + else: + raise MarketplaceWalletNotSupported(wallet=wallet) - save_user_pubaddr(addresses) + if signature is None: + raise MarketplaceEmptySignature() - return match['key'], match['secret'] + signature = signature[2:] + r = int(signature[0:64], base=16) + s = int(signature[64:128], base=16) + v = int(signature[128:130], base=16) + vrs = [v, r, s] + + response = session.get('{}/marketplace/getkeysecret'.format(AUTH_SERVER), + headers={ + 'Authorization': 'Digest username="{0}",realm="{1}",' + 'nonce="{2}",uri="/marketplace/getkeysecret",response="{3}",' + 'opaque="{4}"'.format(pubAddr, + d['realm'], + d['nonce'], + ','.join(str(e) for e in vrs+[wallet]), + d['opaque'])}) + + if response.status_code == 200: + + if 'error' in response.json(): + raise MarketplaceHTTPRequest(request=str('obtain key/secret'), + error=str(response.json()['error'])) + else: + addresses = get_user_pubaddr() + + match = next((l for l in addresses if + l['pubAddr'] == pubAddr), None) + match['key'] = response.json()['key'] + match['secret'] = response.json()['secret'] + + addresses[addresses.index(match)] = match + + save_user_pubaddr(addresses) + print('Key/secret pair retrieved successfully from server.') + + return match['key'], match['secret'] + + else: + raise MarketplaceHTTPRequest(request=str('obtain key/secret'), + error=response.status_code) def get_signed_headers(ds_name, key, secret):