def test_build_download_response(): request = Mock() request.range = None class Dummy: pass mocked_response = Dummy() mocked_response.content = b'asdsadf' mocked_response.status_code = 200 mocked_response.headers = {} requests_session = Dummy() requests_session.get = MagicMock(return_value=mocked_response) filename = '<<filename>>.xml' content_type = mimetypes.guess_type(filename)[0] url = f'https://source-lllllll.cccc/{filename}' response = build_download_response(request, requests_session, url, url, None) assert response.headers["content-type"] == content_type assert response.headers.get_all('Content-Disposition')[0] == f'attachment;filename={filename}' filename = '<<filename>>' url = f'https://source-lllllll.cccc/{filename}' response = build_download_response(request, requests_session, url, url, None) assert response.headers["content-type"] == get_content_type(response.default_mimetype, response.charset) assert response.headers.get_all('Content-Disposition')[0] == f'attachment;filename={filename}' filename = '<<filename>>' url = f'https://source-lllllll.cccc/{filename}' response = build_download_response(request, requests_session, url, url, content_type) assert response.headers["content-type"] == content_type assert response.headers.get_all('Content-Disposition')[0] == f'attachment;filename={filename+mimetypes.guess_extension(content_type)}' mocked_response_with_attachment = deepcopy(mocked_response) attachment_file_name = 'test.xml' mocked_response_with_attachment.headers = {'content-disposition': f'attachment;filename={attachment_file_name}'} requests_session_with_attachment = Dummy() requests_session_with_attachment.get = MagicMock(return_value=mocked_response_with_attachment) url = 'https://source-lllllll.cccc/not-a-filename' response = build_download_response(request, requests_session_with_attachment, url, url, None) assert response.headers["content-type"] == mimetypes.guess_type(attachment_file_name)[0] assert response.headers.get_all('Content-Disposition')[0] == f'attachment;filename={attachment_file_name}' mocked_response_with_content_type = deepcopy(mocked_response) response_content_type = 'text/csv' mocked_response_with_content_type.headers = {'content-type': response_content_type} requests_session_with_content_type = Dummy() requests_session_with_content_type.get = MagicMock(return_value=mocked_response_with_content_type) filename = 'filename.txt' url = f'https://source-lllllll.cccc/{filename}' response = build_download_response(request, requests_session_with_content_type, url, url, None) assert response.headers["content-type"] == response_content_type assert response.headers.get_all('Content-Disposition')[0] == f'attachment;filename={filename}'
def test_download_ipfs_file(client): cid = 'QmQfpdcMWnLTXKKW9GPV7NgtEugghgD6HgzSF6gSrp2mL9' url = f'ipfs://{cid}' download_url = get_download_url(url, None) requests_session = get_requests_session() request = Mock() request.range = None print(f'got ipfs download url: {download_url}') assert download_url and download_url.endswith(f'ipfs/{cid}') response = build_download_response(request, requests_session, download_url, download_url, None) assert response.data, f'got no data {response.data}'
def simple_flow_consume(): required_attributes = [ 'consumerAddress', 'dataToken', 'transferTxId' ] data = get_request_data(request) msg, status = check_required_attributes( required_attributes, data, 'simple_flow_consume') if msg: return jsonify(error=msg), status consumer = data.get('consumerAddress') dt_address = data.get('dataToken') tx_id = data.get('transferTxId') dt_map = None dt_map_str = os.getenv('CONFIG', '') if dt_map_str: dt_map = json.loads(dt_map_str) if not (dt_map_str and dt_map): # or dt not in dt_map: return jsonify(error='This request is not supported.'), 400 try: dt = DataToken(dt_address) # TODO: Verify that the datatoken is owned by this provider's account # TODO: Enable this check for the token transfer. # validate_order( # consumer, # dt_address, # 1, # tx_id # ) url = list(dt_map.values())[0] # [dt_address] download_url = get_download_url(url, app.config['CONFIG_FILE']) logger.info(f'Done processing consume request for data token {dt_address}, ' f' url {download_url}') return build_download_response(request, requests_session, url, download_url) except Exception as e: logger.error( f'Error: {e}. \n' f'Payload was: dataToken={dt_address}, ' f'consumerAddress={consumer}', exc_info=1 ) return jsonify(error=e), 500
def simple_flow_consume(): data = get_request_data(request) consumer = data.get("consumerAddress") dt_address = data.get("dataToken") dt_map = None dt_map_str = os.getenv("CONFIG", "") if dt_map_str: dt_map = json.loads(dt_map_str) if not (dt_map_str and dt_map): # or dt not in dt_map: return jsonify(error="This request is not supported."), 400 try: _ = DataToken(dt_address) # TODO: verify that the datatoken is owned by this provider's account # TODO: Enable this check for the token transfer. # validate_order( # consumer, # dt_address, # 1, # tx_id # ) url = list(dt_map.values())[0] # [dt_address] download_url = get_download_url(url, app.config["CONFIG_FILE"]) logger.info( f"Done processing consume request for data token {dt_address}, " f" url {download_url}") return build_download_response(request, requests_session, url, download_url) except Exception as e: logger.error( f"Error: {e}. \n" f"Payload was: dataToken={dt_address}, " f"consumerAddress={consumer}", exc_info=1, ) return jsonify(error=str(e)), 500
def download(): """Allows download of asset data file. --- tags: - services consumes: - application/json parameters: - name: consumerAddress in: query description: The consumer address. required: true type: string - name: documentId in: query description: The ID of the asset/document (the DID). required: true type: string - name: url in: query description: This URL is only valid if Provider acts as a proxy. Consumer can't download using the URL if it's not through the Provider. required: true type: string - name: signature in: query description: Signature of the documentId to verify that the consumer has rights to download the asset. - name: index in: query description: Index of the file in the array of files. responses: 200: description: Redirect to valid asset url. 400: description: One of the required attributes is missing. 401: description: Invalid asset data. 500: description: Error """ data = get_request_data(request) try: asset, service, did, consumer_address, token_address = process_consume_request( # noqa data, 'download', user_nonce=user_nonce, additional_params=["transferTxId", "fileIndex"]) service_id = data.get('serviceId') service_type = data.get('serviceType') signature = data.get('signature') tx_id = data.get("transferTxId") if did.startswith('did:'): did = add_0x_prefix(did_to_id(did)) _tx, _order_log, _transfer_log = validate_order( consumer_address, token_address, float(service.get_cost()), tx_id, did, service_id) validate_transfer_not_used_for_other_service(did, service_id, tx_id, consumer_address, token_address) record_consume_request(did, service_id, tx_id, consumer_address, token_address, service.get_cost()) assert service_type == ServiceTypes.ASSET_ACCESS file_index = int(data.get('fileIndex')) file_attributes = asset.metadata['main']['files'][file_index] content_type = file_attributes.get('contentType', None) url = get_asset_url_at_index(file_index, asset, provider_wallet) download_url = get_download_url(url, app.config['CONFIG_FILE']) logger.info(f'Done processing consume request for asset {did}, ' f' url {download_url}') user_nonce.increment_nonce(consumer_address) return build_download_response(request, requests_session, url, download_url, content_type) except InvalidSignatureError as e: msg = f'Consumer signature failed verification: {e}' logger.error(msg, exc_info=1) return jsonify(error=msg), 401 except Exception as e: logger.error( f'Error: {e}. \n' f'Payload was: documentId={did}, ' f'consumerAddress={consumer_address},' f'signature={signature}' f'serviceId={service_id}' f'serviceType={service_type}', exc_info=1) return jsonify(error=str(e)), 500
def download(): """Allows download of asset data file. --- tags: - services consumes: - application/json parameters: - name: consumerAddress in: query description: The consumer address. required: true type: string - name: documentId in: query description: The ID of the asset/document (the DID). required: true type: string - name: url in: query description: This URL is only valid if Provider acts as a proxy. Consumer can't download using the URL if it's not through the Provider. required: true type: string - name: signature in: query description: Signature of the documentId to verify that the consumer has rights to download the asset. - name: index in: query description: Index of the file in the array of files. responses: 200: description: Redirect to valid asset url. 400: description: One of the required attributes is missing. 401: description: Invalid asset data. 500: description: Error """ data = get_request_data(request) try: ( asset, service, did, consumer_address, token_address, ) = process_consume_request(data) service_id = data.get("serviceId") service_type = data.get("serviceType") tx_id = data.get("transferTxId") if did.startswith("did:"): did = add_0x_prefix(did_to_id(did)) _tx, _order_log, _transfer_log = validate_order( consumer_address, token_address, float(service.get_cost()), tx_id, did, service_id, ) validate_transfer_not_used_for_other_service(did, service_id, tx_id, consumer_address, token_address) record_consume_request(did, service_id, tx_id, consumer_address, token_address, service.get_cost()) assert service_type == ServiceTypes.ASSET_ACCESS file_index = int(data.get("fileIndex")) file_attributes = asset.metadata["main"]["files"][file_index] content_type = file_attributes.get("contentType", None) url = get_asset_url_at_index(file_index, asset, provider_wallet) if not url: return jsonify(error="Cannot decrypt files for this asset."), 400 download_url = get_download_url(url, app.config["CONFIG_FILE"]) logger.info(f"Done processing consume request for asset {did}, " f" url {download_url}") increment_nonce(consumer_address) return build_download_response(request, requests_session, url, download_url, content_type) except Exception as e: logger.error( f"Error: {e}. \n" f"Payload was: documentId={data.get('did')}, " f"consumerAddress={data.get('consumerAddress')}," f"serviceId={data.get('serviceId')}" f"serviceType={data.get('serviceType')}", exc_info=1, ) return jsonify(error=str(e)), 500
def download(): """Allows download of asset data file. --- tags: - services consumes: - application/json parameters: - name: consumerAddress in: query description: The consumer address. required: true type: string - name: serviceAgreementId in: query description: The ID of the service agreement. required: true type: string - name: url in: query description: This URL is only valid if Provider acts as a proxy. Consumer can't download using the URL if it's not through the Provider. required: true type: string - name: signature in: query description: Signature of the documentId to verify that the consumer has rights to download the asset. - name: index in: query description: Index of the file in the array of files. responses: 200: description: Redirect to valid asset url. 400: description: One of the required attributes is missing. 401: description: Invalid asset data. 500: description: Error """ data = get_request_data(request) try: asset, service, did, consumer_address, token_address = process_consume_request( data, 'download', additional_params=["transferTxId", "fileIndex"]) service_id = data.get('serviceId') service_type = data.get('serviceType') signature = data.get('signature') tx_id = data.get("transferTxId") validate_token_transfer( consumer_address, provider_acc.address, token_address, # FIXME: Replace this constant price number 5, #service.get_price(), tx_id) file_index = int(data.get('fileIndex')) file_attributes = asset.metadata['main']['files'][file_index] content_type = file_attributes.get('contentType', None) url = get_asset_url_at_index(file_index, asset, provider_acc) download_url = get_download_url(url, app.config['CONFIG_FILE']) logger.info(f'Done processing consume request for asset {did}, ' f' url {download_url}') return build_download_response(request, requests_session, url, download_url, content_type) except InvalidSignatureError as e: msg = f'Consumer signature failed verification: {e}' logger.error(msg, exc_info=1) return jsonify(error=msg), 401 except Exception as e: logger.error( f'Error: {e}. \n' f'Payload was: documentId={did}, ' f'consumerAddress={consumer_address},' f'signature={signature}' f'serviceId={service_id}' f'serviceType={service_type}', exc_info=1) return jsonify(error=e), 500
def download(): return asset_access(request, lambda req, session, url, download_url, content_type: build_download_response(req, session, url, content_type))
def test_build_download_response(): request = Mock() request.range = None class Dummy: pass mocked_response = Dummy() mocked_response.content = b"asdsadf" mocked_response.status_code = 200 mocked_response.headers = {} requests_session = Dummy() requests_session.get = MagicMock(return_value=mocked_response) filename = "<<filename>>.xml" content_type = mimetypes.guess_type(filename)[0] url = f"https://source-lllllll.cccc/{filename}" response = build_download_response(request, requests_session, url, url, None) assert response.headers["content-type"] == content_type assert (response.headers.get_all("Content-Disposition")[0] == f"attachment;filename={filename}") filename = "<<filename>>" url = f"https://source-lllllll.cccc/{filename}" response = build_download_response(request, requests_session, url, url, None) assert response.headers["content-type"] == get_content_type( response.default_mimetype, response.charset) assert (response.headers.get_all("Content-Disposition")[0] == f"attachment;filename={filename}") filename = "<<filename>>" url = f"https://source-lllllll.cccc/{filename}" response = build_download_response(request, requests_session, url, url, content_type) assert response.headers["content-type"] == content_type matched_cd = ( f"attachment;filename={filename+mimetypes.guess_extension(content_type)}" ) assert response.headers.get_all("Content-Disposition")[0] == matched_cd mocked_response_with_attachment = deepcopy(mocked_response) attachment_file_name = "test.xml" mocked_response_with_attachment.headers = { "content-disposition": f"attachment;filename={attachment_file_name}" } requests_session_with_attachment = Dummy() requests_session_with_attachment.get = MagicMock( return_value=mocked_response_with_attachment) url = "https://source-lllllll.cccc/not-a-filename" response = build_download_response(request, requests_session_with_attachment, url, url, None) assert (response.headers["content-type"] == mimetypes.guess_type( attachment_file_name)[0]) # noqa matched_cd = f"attachment;filename={attachment_file_name}" assert response.headers.get_all("Content-Disposition")[0] == matched_cd mocked_response_with_content_type = deepcopy(mocked_response) response_content_type = "text/csv" mocked_response_with_content_type.headers = { "content-type": response_content_type } requests_session_with_content_type = Dummy() requests_session_with_content_type.get = MagicMock( return_value=mocked_response_with_content_type) filename = "filename.txt" url = f"https://source-lllllll.cccc/{filename}" response = build_download_response(request, requests_session_with_content_type, url, url, None) assert response.headers["content-type"] == response_content_type assert (response.headers.get_all("Content-Disposition")[0] == f"attachment;filename={filename}")