Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enforce algo credentials check #667

Merged
merged 25 commits into from
Oct 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
fdb435a
Add algo credentials check. Tests.
mariacarmina Oct 3, 2023
1d55dc5
tweak.
mariacarmina Oct 3, 2023
4eba2a3
add condition back in tests helpers.
mariacarmina Oct 3, 2023
6142a78
Try to download the algo asset as denied wallet address.
mariacarmina Oct 3, 2023
5a54bc3
Change service.
mariacarmina Oct 3, 2023
81e4586
debug.
mariacarmina Oct 3, 2023
22da96a
Add check in WorkflowValidator class.
mariacarmina Oct 3, 2023
14df586
Commented cond in ImputValidator class.
mariacarmina Oct 3, 2023
91c6af3
debug test.
mariacarmina Oct 3, 2023
b12cbf5
Modified download endpoint to verify the asset.
mariacarmina Oct 4, 2023
57cd9bb
JSON loads.
mariacarmina Oct 4, 2023
0e75795
revert.
mariacarmina Oct 4, 2023
7129cf7
Fixed credentials for use case. Added check also for download endpoint.
mariacarmina Oct 4, 2023
843968f
Test use case 2 for credentials.
mariacarmina Oct 5, 2023
9dfea0d
Added test for algo validation.
mariacarmina Oct 5, 2023
2c9de9f
Add minting dt part.
mariacarmina Oct 5, 2023
f9f6dd2
Added the test suite back.
mariacarmina Oct 5, 2023
20d6019
Changed test for algo validation.
mariacarmina Oct 5, 2023
8e8b8d8
removed test.
mariacarmina Oct 5, 2023
3757b7b
Added back the test.
mariacarmina Oct 5, 2023
cc85578
Added ssh sesh.
mariacarmina Oct 5, 2023
f1d60b1
removed unwanted imagesfrom the docker images.
mariacarmina Oct 5, 2023
3ca9e3c
fix.
mariacarmina Oct 5, 2023
276a4bb
tweak.
mariacarmina Oct 5, 2023
bd7bf6b
Merge branch 'main' into fix-algo-credentials
mariacarmina Oct 6, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions ocean_provider/routes/consume.py
Original file line number Diff line number Diff line change
Expand Up @@ -336,6 +336,11 @@ def download():
# grab asset for did from the metadatastore associated with
# the datatoken address
asset = get_asset_from_metadatastore(get_metadata_url(), did)

consumable, message = check_asset_consumable(asset, consumer_address, logger)
if not consumable:
return error_response(message, 400, logger)

service = asset.get_service_by_id(service_id)

if service.type != ServiceType.ACCESS:
Expand Down
3 changes: 2 additions & 1 deletion ocean_provider/utils/asset.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,10 +100,11 @@ def get_asset_from_metadatastore(metadata_url, document_id) -> Optional[Asset]:
def check_asset_consumable(asset, consumer_address, logger, custom_url=None):
if not asset.nft or "address" not in asset.nft or not asset.chain_id:
return False, "Asset malformed or disabled."

web3 = get_web3(asset.chain_id)
nft_contract = get_data_nft_contract(web3, asset.nft["address"])

if nft_contract.caller.getMetaData()[2] not in [0, 5]:
if nft_contract.functions.getMetaData().call()[2] not in [0, 5]:
return False, "Asset is not consumable."

code = asset.is_consumable({"type": "address", "value": consumer_address})
Expand Down
8 changes: 7 additions & 1 deletion ocean_provider/utils/credentials.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
# Copyright 2023 Ocean Protocol Foundation
# SPDX-License-Identifier: Apache-2.0
#
import json
from typing import Optional

from ocean_provider.utils.consumable import ConsumableCodes, MalformedCredential
Expand Down Expand Up @@ -100,7 +101,12 @@ def get_address_entry_of_class(self, access_class: str = "allow") -> Optional[di
"""Get address credentials entry of the specified access class. access_class = "allow" or "deny"."""
if not self.asset.credentials:
return None
entries = self.asset.credentials.get(access_class, [])
if isinstance(self.asset.credentials, str):
credentials = json.loads(self.asset.credentials)
else:
credentials = self.asset.credentials

entries = credentials.get(access_class, [])
address_entries = [entry for entry in entries if entry.get("type") == "address"]
return address_entries[0] if address_entries else None

Expand Down
2 changes: 1 addition & 1 deletion ocean_provider/utils/data_nft.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ def get_data_nft_contract(web3: Web3, address: Optional[str] = None) -> Contract
especially the `getMetaData` contract method.
"""
abi = get_contract_definition("ERC721Template")["abi"]
return web3.eth.contract(address=address, abi=abi)
return web3.eth.contract(address=web3.toChecksumAddress(address), abi=abi)


def get_metadata(web3: Web3, address: str) -> Tuple[str, str, MetadataState, bool]:
Expand Down
11 changes: 11 additions & 0 deletions ocean_provider/validation/algo.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
get_asset_from_metadatastore,
)
from ocean_provider.utils.basics import get_metadata_url, get_provider_wallet, get_web3
from ocean_provider.utils.consumable import ConsumableCodes
from ocean_provider.utils.credentials import AddressCredential
from ocean_provider.utils.datatoken import (
record_consume_request,
validate_order,
Expand Down Expand Up @@ -308,6 +310,15 @@ def preliminary_algo_validation(self):
self.message = "file_unavailable"
return False

consumable, message = check_asset_consumable(
algo_ddo, self.consumer_address, logger, service.service_endpoint
)

if not consumable:
self.resource = "algorithm.credentials"
self.message = message
return False

return True


Expand Down
74 changes: 72 additions & 2 deletions ocean_provider/validation/test/test_algo_validation.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,23 @@
# Copyright 2023 Ocean Protocol Foundation
# SPDX-License-Identifier: Apache-2.0
#
import os
import copy
from unittest.mock import Mock, patch

import pytest
from eth_account import Account
from ocean_provider.utils.asset import Asset
from ocean_provider.utils.currency import to_wei
from ocean_provider.utils.services import Service, ServiceType
from ocean_provider.validation.algo import WorkflowValidator
from tests.ddo.ddo_sample1_compute import alg_ddo_dict, ddo_dict
from tests.helpers.compute_helpers import get_future_valid_until
from tests.test_helpers import get_first_service_by_type
from tests.test_helpers import get_first_service_by_type, get_ocean_token_address
from ocean_provider.utils.datatoken import get_datatoken_contract
from tests.helpers.compute_helpers import (
get_future_valid_until,
build_and_send_ddo_with_compute_service,
)

provider_fees_event = Mock()
provider_fees_event.args.providerData = {"environment": "ocean-compute"}
Expand Down Expand Up @@ -1010,3 +1017,66 @@ def side_effect(*args, **kwargs):
assert validator.validate() is False
assert validator.resource == "algorithm"
assert validator.message == "file_unavailable"


@pytest.mark.unit
def test_algo_credentials(
client,
provider_address,
consumer_address,
publisher_wallet,
web3,
consumer_wallet,
free_c2d_env,
):
valid_until = get_future_valid_until()
deployer_wallet = Account.from_key(os.getenv("FACTORY_DEPLOYER_PRIVATE_KEY"))
fee_token = get_datatoken_contract(web3, get_ocean_token_address(web3))
fee_token.functions.mint(consumer_wallet.address, to_wei(80)).transact(
{"from": deployer_wallet.address}
)
custom_algo_credentials = {
"allow": [],
"deny": [{"type": "address", "values": [consumer_address]}],
}
ddo, tx_id, alg_ddo, alg_tx_id = build_and_send_ddo_with_compute_service(
client,
publisher_wallet,
consumer_wallet,
True,
custom_algo_credentials=custom_algo_credentials,
c2d_address=free_c2d_env["consumerAddress"],
valid_until=valid_until,
c2d_environment=free_c2d_env["id"],
fee_token_args=(fee_token, to_wei(80)),
)
sa = get_first_service_by_type(ddo, ServiceType.COMPUTE)
sa_compute = get_first_service_by_type(alg_ddo, ServiceType.ACCESS)

data = {
"dataset": {"documentId": ddo.did, "serviceId": sa.id, "transferTxId": tx_id},
"algorithm": {
"documentId": alg_ddo.did,
"serviceId": sa_compute.id,
"transferTxId": alg_tx_id,
},
}

def side_effect(*args, **kwargs):
nonlocal ddo, alg_ddo
if ddo.did == args[1]:
return ddo
if alg_ddo.did == args[1]:
return alg_ddo

with patch(
"ocean_provider.validation.algo.get_asset_from_metadatastore",
side_effect=side_effect,
):
validator = WorkflowValidator(consumer_address, data)
assert validator.validate() is False
assert validator.resource == "algorithm.credentials"
assert (
validator.message
== f"Error: Access to asset {alg_ddo.did} was denied with code: ConsumableCodes.CREDENTIAL_IN_DENY_LIST."
)
32 changes: 22 additions & 10 deletions tests/helpers/compute_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ def build_and_send_ddo_with_compute_service(
publisher_wallet,
consumer_wallet,
alg_diff=False,
custom_algo_credentials=None,
asset_type=None,
c2d_address=None,
do_send=True,
Expand All @@ -40,15 +41,27 @@ def build_and_send_ddo_with_compute_service(
if c2d_address is None:
c2d_address = consumer_wallet.address
if alg_diff:
alg_ddo = get_registered_asset(
publisher_wallet,
custom_metadata=algo_metadata,
custom_service_endpoint="http://172.15.0.7:8030",
timeout=timeout,
unencrypted_files_list=[
{"url": this_is_a_gist, "type": "url", "method": "GET"}
],
)
if custom_algo_credentials:
alg_ddo = get_registered_asset(
publisher_wallet,
custom_metadata=algo_metadata,
custom_service_endpoint="http://172.15.0.7:8030",
custom_credentials=custom_algo_credentials,
timeout=timeout,
unencrypted_files_list=[
{"url": this_is_a_gist, "type": "url", "method": "GET"}
],
)
else:
alg_ddo = get_registered_asset(
publisher_wallet,
custom_metadata=algo_metadata,
custom_service_endpoint="http://172.15.0.7:8030",
timeout=timeout,
unencrypted_files_list=[
{"url": this_is_a_gist, "type": "url", "method": "GET"}
],
)
else:
alg_ddo = get_registered_asset(
publisher_wallet,
Expand All @@ -58,7 +71,6 @@ def build_and_send_ddo_with_compute_service(
{"url": this_is_a_gist, "type": "url", "method": "GET"}
],
)

# publish an algorithm asset (asset with metadata of type `algorithm`)
service = get_first_service_by_type(alg_ddo, ServiceType.ACCESS)
mint_100_datatokens(
Expand Down
Loading