예제 #1
0
def get_consumer_instance(init_tokens=True,
                          use_ss_mock=True,
                          use_gateway_mock=True):
    nevermined = Nevermined()
    account = get_consumer_account()
    nevermined.main_account = account
    if init_tokens:
        init_ocn_tokens(nevermined, nevermined.main_account)
    if use_ss_mock:
        SecretStoreProvider.set_secret_store_class(SecretStoreMock)
    else:
        SecretStoreProvider.set_secret_store_class(SecretStore)
    if use_gateway_mock:
        GatewayProvider.set_gateway_class(GatewayMock)
    else:
        Gateway.set_http_client(get_requests_session())
        GatewayProvider.set_gateway_class(Gateway)

    return nevermined
예제 #2
0
    def __init__(self, metadata_url):
        """
        The Metadata class is a wrapper on the Metadata Store, which has exposed a REST API.

        :param metadata_url: Url of the metadata instance.
        """
        assert metadata_url, f'Invalid url "{metadata_url}"'
        # :HACK:
        if '/api/v1/metadata/assets' in metadata_url:
            metadata_url = metadata_url[:metadata_url.find('/api/v1/metadata/assets')]

        self._base_url = f'{metadata_url}/api/v1/metadata/assets'
        self._headers = {'content-type': 'application/json'}

        logging.debug(f'Metadata Store connected at {metadata_url}')
        logging.debug(f'Metadata Store API documentation at {metadata_url}/api/v1/docs')
        logging.debug(f'Metadata assets at {self._base_url}')

        self.requests_session = get_requests_session()
예제 #3
0
class Faucet:
    """
    `Faucet`.

    The main functions available are:
    - Request eht for an account

    """
    _http_client = get_requests_session()

    @staticmethod
    def set_http_client(http_client):
        """Set the http client to something other than the default `requests`"""
        Faucet._http_client = http_client

    @staticmethod
    def get_faucet_url(config):
        """
        Return the Gateway component url.

        :param config: Config
        :return: Url, str
        """
        faucet_url = 'http://localhost:3001'
        if config.has_option('resources', 'faucet.url'):
            faucet_url = config.get('resources', 'faucet.url') or faucet_url

        faucet_path = '/faucet'
        return f'{faucet_url}{faucet_path}'

    @staticmethod
    def get_eth_from_faucet(config, address):
        payload = {'address': Web3Provider.get_web3().toChecksumAddress(address), 'agent': 'sdk-py'}
        response = Faucet._http_client.post(Faucet.get_faucet_url(config), data=payload, headers={'Accept': 'application/json'})

        if not response.ok:
            raise ValueError(response.text)

        return response
예제 #4
0
class Gateway:
    """
    `Gateway` is the name chosen for the asset service provider.

    The main functions available are:
    - Allow to get access to an asset data file
    - encrypt a secret using different methods
    - consume_service
    - run_compute_service

    """
    _http_client = get_requests_session()
    _tokens_cache = {}

    @staticmethod
    def _generate_cache_key(*args):
        return ''.join(args)

    @staticmethod
    def set_http_client(http_client):
        """Set the http client to something other than the default `requests`"""
        Gateway._http_client = http_client

    @staticmethod
    def old_encrypt_files_dict(files_dict, encrypt_endpoint, asset_id,
                               account_address, signed_did):
        payload = json.dumps({
            'documentId': asset_id,
            'signedDocumentId': signed_did,
            'document': json.dumps(files_dict),
            'publisherAddress': account_address
        })
        response = Gateway._http_client.post(
            encrypt_endpoint,
            data=payload,
            headers={'content-type': 'application/json'})
        if response and hasattr(response, 'status_code'):
            if response.status_code != 201:
                msg = (
                    f'Encrypt file urls failed at the encryptEndpoint '
                    f'{encrypt_endpoint}, reason {response.text}, status {response.status_code}'
                )
                logger.error(msg)
                raise EncryptAssetUrlsError(msg)

            logger.info(
                f'Asset urls encrypted successfully, encrypted urls str: {response.text},'
                f' encryptedEndpoint {encrypt_endpoint}')

            return response.text

    @staticmethod
    def encrypt_files_dict(files_dict, encrypt_endpoint, asset_id, method):
        payload = json.dumps({
            'did': asset_id,
            'message': json.dumps(files_dict),
            'method': method
        })
        response = Gateway._http_client.post(
            encrypt_endpoint,
            data=payload,
            headers={'content-type': 'application/json'})
        if response and hasattr(response, 'status_code'):
            if response.status_code != 200:
                msg = (
                    f'Encrypt file urls failed at the encryptEndpoint '
                    f'{encrypt_endpoint}, reason {response.text}, status {response.status_code}'
                )
                logger.error(msg)
                raise EncryptAssetUrlsError(msg)

            logger.info(
                f'Asset urls encrypted successfully, encrypted urls str: {response.text},'
                f' encryptedEndpoint {encrypt_endpoint}')

            return json.loads(response.text)['hash']

    @staticmethod
    def access_service(did, service_agreement_id, service_endpoint, account,
                       destination_folder, config, index):
        cache_key = Gateway._generate_cache_key(account.address,
                                                service_agreement_id, did)
        if cache_key not in Gateway._tokens_cache:
            grant_token = generate_access_grant_token(account,
                                                      service_agreement_id,
                                                      did)
            access_token = Gateway.fetch_token(grant_token, config)
            Gateway._tokens_cache[cache_key] = access_token
        else:
            access_token = Gateway._tokens_cache[cache_key]

        consume_url = Gateway._create_access_url(service_endpoint,
                                                 service_agreement_id, index)
        headers = {"Authorization": f"Bearer {access_token}"}

        response = Gateway._http_client.get(consume_url,
                                            headers=headers,
                                            stream=True)
        if not response.ok:
            raise ValueError(response.text)

        file_name = Gateway._get_file_name(response)
        Gateway.write_file(response, destination_folder, file_name
                           or f'file-{index}')

        return response

    @staticmethod
    def compute_logs(service_agreement_id, execution_id, account, config):
        """Get the logs of a compute workflow.

        Args:
            service_agreement_id (str): The id of the service agreement.
            execution_id (str): The id of the workflow execution.
            account (:py:class:`contracts_lib_py.account.Account`): The account that ordered
                the execution of the workflow.
            config (:py:class:`nevermined_sdk_py.config.Config`): nevermined-sdk config instance.

        Returns:
            :py:class:`requests.Response`: HTTP server response

        """
        cache_key = Gateway._generate_cache_key(account.address,
                                                service_agreement_id,
                                                execution_id)
        if cache_key not in Gateway._tokens_cache:
            grant_token = generate_compute_grant_token(account,
                                                       service_agreement_id,
                                                       execution_id)
            access_token = Gateway.fetch_token(grant_token, config)
            Gateway._tokens_cache[cache_key] = access_token
        else:
            access_token = Gateway._tokens_cache[cache_key]

        headers = {"Authorization": f"Bearer {access_token}"}
        consume_url = Gateway._create_compute_logs_url(config,
                                                       service_agreement_id,
                                                       execution_id)

        response = Gateway._http_client.get(consume_url, headers=headers)
        if not response.ok:
            raise ValueError(response.text)
        return response

    @staticmethod
    def compute_status(service_agreement_id, execution_id, account, config):
        """Get the status of a compute workflow.

        Args:
            service_agreement_id (str): The id of the service agreement.
            execution_id (str): The id of the workflow execution.
            account (:py:class:`contracts_lib_py.account.Account`): The account that ordered
                the execution of the workflow.
            config (:py:class:`nevermined_sdk_py.config.Config`): nevermined-sdk config instance.

        Returns:
            :py:class:`requests.Response`: HTTP server response

        """
        cache_key = Gateway._generate_cache_key(account.address,
                                                service_agreement_id,
                                                execution_id)
        if cache_key not in Gateway._tokens_cache:
            grant_token = generate_compute_grant_token(account,
                                                       service_agreement_id,
                                                       execution_id)
            access_token = Gateway.fetch_token(grant_token, config)
            Gateway._tokens_cache[cache_key] = access_token
        else:
            access_token = Gateway._tokens_cache[cache_key]

        headers = {"Authorization": f"Bearer {access_token}"}
        consume_url = Gateway._create_compute_status_url(
            config, service_agreement_id, execution_id)

        response = Gateway._http_client.get(consume_url, headers=headers)
        if response.status_code != 200:
            raise ValueError(response.text)
        return response

    @staticmethod
    def download(did, account, destination_folder, index, config):
        """Allows an asset data file if the account is the owner or provider of the asset

        Args:
            did (str): The did of the asset.
            account (:py:class:`contracts_lib_py.account.Account`): The account
                that should be the owner or provider of the asset.
            destination_folder (str): The destination where the downloaded file should be put.
            index (int): Index of the file.
            config (:py:class:`nevermined_sdk_py.config.Config`): nevermined-sdk config instance.

        Returns:
            :py:class:`requests.Response`: HTTP server response

        """
        cache_key = Gateway._generate_cache_key(account.address, did)
        if cache_key not in Gateway._tokens_cache:
            grant_token = generate_download_grant_token(account, did)
            access_token = Gateway.fetch_token(grant_token, config)
            Gateway._tokens_cache[cache_key] = access_token
        else:
            access_token = Gateway._tokens_cache[cache_key]

        headers = {"Authorization": f"Bearer {access_token}"}
        consume_url = Gateway._create_download_url(config, index)

        response = Gateway._http_client.get(consume_url,
                                            headers=headers,
                                            stream=True)
        if not response.ok:
            raise ValueError(response.text)

        file_name = Gateway._get_file_name(response)
        Gateway.write_file(response, destination_folder, file_name
                           or f'file-{index}')

        return response

    @staticmethod
    def execute_service(service_agreement_id, service_endpoint, account,
                        workflow_ddo):
        """

        :param service_agreement_id:
        :param service_endpoint:
        :param account:
        :return:
        """
        signature = Keeper.get_instance().sign_hash(
            add_ethereum_prefix_and_hash_msg(service_agreement_id), account)
        execute_url = Gateway._create_execute_url(service_endpoint,
                                                  service_agreement_id,
                                                  account, workflow_ddo.did,
                                                  signature)
        logger.info(f'invoke execute endpoint with this url: {execute_url}')
        response = Gateway._http_client.post(execute_url)
        return response

    @staticmethod
    def execute_compute_service(service_agreement_id, service_endpoint,
                                account, workflow_ddo, config):
        cache_key = Gateway._generate_cache_key(account.address,
                                                service_agreement_id,
                                                workflow_ddo.did)
        if cache_key not in Gateway._tokens_cache:
            grant_token = generate_execute_grant_token(account,
                                                       service_agreement_id,
                                                       workflow_ddo.did)
            access_token = Gateway.fetch_token(grant_token, config)
            Gateway._tokens_cache[cache_key] = access_token
        else:
            access_token = Gateway._tokens_cache[cache_key]

        headers = {"Authorization": f"Bearer {access_token}"}
        execute_url = Gateway._create_compute_url(service_endpoint,
                                                  service_agreement_id)

        response = Gateway._http_client.post(execute_url, headers=headers)
        if not response.ok:
            raise ValueError(response.text)
        return response

    @staticmethod
    def fetch_token(grant_token, config):
        fetch_token_url = Gateway.get_fetch_token_endpoint(config)
        response = Gateway._http_client.post(
            fetch_token_url,
            data={
                "grant_type": NeverminedJWTBearerGrant.GRANT_TYPE,
                "assertion": grant_token
            })
        if not response.ok:
            raise ValueError(response.text)
        return response.json()["access_token"]

    @staticmethod
    def upload_filecoin(file_, config):
        upload_filecoin_url = Gateway.get_upload_filecoin_endpoint(config)
        response = Gateway._http_client.post(upload_filecoin_url,
                                             files={'file': file_})

        if not response.ok:
            raise ValueError(response.text)

        return response.json()

    @staticmethod
    def _prepare_consume_payload(did, service_agreement_id, service_index,
                                 signature, consumer_address):
        """Prepare a payload to send to Nevermined Gateway.

        :param did: DID, str
        :param service_agreement_id: Service Agreement Id, str
        :param service_index: identifier of the service inside the asset DDO, str
        service in the DDO (DID document)
        :param signature: the signed agreement message hash which includes
         conditions and their parameters values and other details of the agreement, str
        :param consumer_address: ethereum address of the consumer signing this agreement, hex-str
        :return: dict
        """
        return json.dumps({
            'did': did,
            'serviceAgreementId': service_agreement_id,
            ServiceAgreement.SERVICE_INDEX: service_index,
            'signature': signature,
            'consumerAddress': consumer_address
        })

    @staticmethod
    def get_gateway_url(config):
        """
        Return the Gateway component url.

        :param config: Config
        :return: Url, str
        """
        gateway_url = 'http://localhost:8030'
        if config.has_option('resources', 'gateway.url'):
            gateway_url = config.get('resources', 'gateway.url') or gateway_url

        gateway_path = '/api/v1/gateway'
        return f'{gateway_url}{gateway_path}'

    @staticmethod
    def get_access_endpoint(config):
        """
        Return the endpoint to access the asset.

        :param config:Config
        :return: Url, str
        """
        return f'{Gateway.get_gateway_url(config)}/services/access'

    @staticmethod
    def get_download_endpoint(config):
        """
        Return he endpoint to download the asset.

        :param config:Config
        :return: Url, str
        """
        return f'{Gateway.get_gateway_url(config)}/services/download'

    @staticmethod
    def get_compute_logs_endpoint(config):
        """
        Return he endpoint to get the logs of a compute workflow.

        :param config:Config
        :return: Url, str
        """
        return f'{Gateway.get_gateway_url(config)}/services/compute/logs'

    @staticmethod
    def get_compute_status_endpoint(config):
        """
        Return he endpoint to get the status of a compute workflow.

        :param config:Config
        :return: Url, str
        """
        return f'{Gateway.get_gateway_url(config)}/services/compute/status'

    @staticmethod
    def get_consume_endpoint(config):
        """
        Return the url to access the asset.

        :param config: Config
        :return: Url, str
        """
        return f'{Gateway.get_gateway_url(config)}/services/access'

    @staticmethod
    def get_execute_endpoint(config):
        """
        Return the url to execute the asset.

        :param config: Config
        :return: Url, str
        """
        return f'{Gateway.get_gateway_url(config)}/services/execute'

    @staticmethod
    def get_encrypt_endpoint(config):
        """
        Return the url to encrypt the asset.

        :param config: Config
        :return: Url, str
        """
        return f'{Gateway.get_gateway_url(config)}/services/encrypt'

    @staticmethod
    def get_exec_endpoint(config):
        """
        Return the url to execute the asset.
        This method has been deprecated.

        :param config: Config
        :return: Url, str
        """
        return f'{Gateway.get_gateway_url(config)}/services/exec'

    @staticmethod
    def get_publish_endpoint(config):
        """
        Return the url to encrypt the asset.
        This method has been deprecated.

        :param config: Config
        :return: Url, str
        """
        return f'{Gateway.get_gateway_url(config)}/services/publish'

    @staticmethod
    def get_fetch_token_endpoint(config):
        """
        Return the url to fetch an access token.

        :param config: Config
        :return: Url, str
        """
        return f'{Gateway.get_gateway_url(config)}/services/oauth/token'

    @staticmethod
    def get_upload_filecoin_endpoint(config):
        """
        Return the url to upload a file to Filecoin.

        :param config: Config,
        :return Url, str
        """
        return f'{Gateway.get_gateway_url(config)}/services/upload/filecoin'

    @staticmethod
    def get_rsa_public_key(config):
        return Gateway._http_client.get(
            config.gateway_url).json()['rsa-public-key']

    @staticmethod
    def get_ecdsa_public_key(config):
        return Gateway._http_client.get(
            config.gateway_url).json()['ecdsa-public-key']

    @staticmethod
    def _get_file_name(response):
        try:
            return re.match(r'attachment;filename=(.+?)($|\?)',
                            response.headers.get('content-disposition'))[1]
        except Exception as e:
            logger.warning(f'It was not possible to get the file name. {e}')

    @staticmethod
    def write_file(response, destination_folder, file_name):
        """
        Write the response content in a file in the destination folder.
        :param response: Response
        :param destination_folder: Destination folder, string
        :param file_name: File name, string
        :return: bool
        """
        if response.status_code == 200:
            with open(os.path.join(destination_folder, file_name), 'wb') as f:
                for chunk in response.iter_content(chunk_size=None):
                    f.write(chunk)
            logger.info(f'Saved downloaded file in {f.name}')
        else:
            logger.warning(f'access failed: {response.reason}')

    @staticmethod
    def _create_access_url(service_endpoint, service_agreement_id, index=None):
        """Return the url to get access to an asset."""
        return f'{service_endpoint}/{service_agreement_id}/{index}'

    @staticmethod
    def _create_download_url(config, index=None):
        """Return the url to download an asset."""
        return f'{Gateway.get_download_endpoint(config)}/{index}'

    @staticmethod
    def _create_compute_logs_url(config, service_agreement_id, execution_id):
        """Return the url to get the execution logs of a compute workflow."""
        return f'{Gateway.get_compute_logs_endpoint(config)}/{service_agreement_id}/{execution_id}'

    @staticmethod
    def _create_compute_status_url(config, service_agreement_id, execution_id):
        """Return the url to get the status of a compute workflow."""
        return f'{Gateway.get_compute_status_endpoint(config)}/{service_agreement_id}/{execution_id}'

    @staticmethod
    def _create_consume_url(service_endpoint,
                            service_agreement_id,
                            account,
                            _file=None,
                            signature=None,
                            index=None):
        if _file is not None and 'url' in _file:
            url = _file['url']
            if url.startswith('"') or url.startswith("'"):
                url = url[1:-1]
            return (f'{service_endpoint}'
                    f'?url={url}'
                    f'&serviceAgreementId={service_agreement_id}'
                    f'&consumerAddress={account.address}')
        else:
            return (f'{service_endpoint}'
                    f'?signature={signature}'
                    f'&serviceAgreementId={service_agreement_id}'
                    f'&consumerAddress={account.address}'
                    f'&index={index}')

    @staticmethod
    def _create_execute_url(service_endpoint,
                            service_agreement_id,
                            account,
                            workflow_did,
                            signature=None):
        return (f'{service_endpoint}'
                f'?signature={signature}'
                f'&serviceAgreementId={service_agreement_id}'
                f'&consumerAddress={account.address}'
                f'&workflowDID={workflow_did}')

    @staticmethod
    def _create_compute_url(service_endpoint, service_agreement_id):
        return f'{service_endpoint}/{service_agreement_id}'
예제 #5
0
from nevermined_gateway.util import (
    check_required_attributes, do_secret_store_encrypt, encrypt,
    generate_password, get_asset_url_at_index, get_config,
    get_provider_account, get_provider_babyjub_key, get_provider_key_file,
    get_provider_password, get_rsa_public_key_file, is_access_granted,
    is_escrow_payment_condition_fulfilled, is_nft_transfer_approved,
    is_nft_transfer_condition_fulfilled, keeper_instance, setup_keeper,
    used_by, verify_signature, was_compute_triggered, get_asset,
    generate_random_id, is_lock_payment_condition_fulfilled,
    get_upload_enabled, upload_content)

setup_logging()
services = Blueprint('services', __name__)
setup_keeper(app.config['CONFIG_FILE'])
provider_acc = get_provider_account()
requests_session = get_requests_session()
authorization = create_authorization_server(app)
require_oauth = create_resource_server()

logger = logging.getLogger(__name__)


@services.route("/encrypt", methods=['POST'])
def encrypt_content():
    """Call the execution of a workflow.
    swagger_from_file: docs/encrypt.yml
    """

    required_attributes = ['message', 'method']
    data = request.json