Beispiel #1
0
def test_create_request_with_content_claims(
    client: Client,
    endorser: PrivateIdentity,
    mocked_requests_200: respx.MockTransport,
    entity: Entity,
) -> None:
    """Create endorsement request provided by a 3rd party endorser."""
    claims = [b"claim-1", b"claim-2"]

    # Create endorsement request with authorisation of endorser
    content, authorisation = endorser.endorse(entity, claims)

    # This will also add the subject holders authorisation
    client.put(
        entity,
        claims=claims,
        content=content,
        authorisations=[authorisation],
        create_claims=True
        # endorse=True # TODO - provide test that this does not have any effect
    )
    http_request, _ = mocked_requests_200["create_entity"].calls[0]

    claims_header = json.loads(
        iov42_decode(http_request.headers["x-iov42-claims"]))
    assert claims_header == {hashed_claim(c): c.decode() for c in claims}
Beispiel #2
0
def read_endorsement(
    ctx: click.core.Context,
    identity: str,
    endorser_id: str,
    entity_type: str,
    entity_id: str,
    asset_type_id: str,
    claim: str,
) -> None:
    """Read specific endorsement of a claim (identity, asset type, unique asset)."""
    if entity_type.lower() == "asset":
        entity = Asset(asset_type_id=asset_type_id, asset_id=entity_id)
    else:
        raise NotImplementedError  # pragma: no cover
    id = _load_identity(identity)
    client = Client(ctx.obj["url"], id)
    response = client.get(
        entity,
        claim=claim.encode(),
        endorser_id=endorser_id,
        request_id=ctx.obj["request_id"],
    )
    print(f"{entity!r}")
    print(f"{claim!r}")
    print(f"endorser: {response.endorser_id!r}")  # type: ignore[union-attr]
Beispiel #3
0
def test_raise_identity_already_exists_2(client: Client) -> None:
    """Raise exception when an identity (with an other key) already exists."""
    client.identity = PrivateIdentity(
        CryptoProtocol.SHA256WithECDSA.generate_private_key(), "test-1234")
    with respx.mock(base_url=PLATFORM_URL) as respx_mock:
        respx_mock.put(
            re.compile("/api/v1/requests/.*$"),
            status_code=400,
            content=
            ('{"errors":[{"errorCode":2503,"errorType":"Authorisation",'
             '"message":"Signature L-IjTeba3wvJn4hHR40GPCG-H7iIeDWOzBo3hCK7x1mLZgif'
             "SdgR-YVxOZtvPzHaI86WdhIL3y-sNOwYUf2c0j7OfT31dAX71W9le-Cp2Mx1PgjjqI09f"
             "i0Nku-h5lgipQ07VKAm3gUx0foeG9GdDQe_I85QuCqtJsaAXWDVc8r0NeWpa3dnQEflIm"
             "W0-gecjO6pYDeyXPALcvp9h8Q_TxkuGVvreqpWvgKzdPMlXHMbN3wYoLNNLM3gpqrqAp"
             "Eze1aTqtlK6gCQUuhsJlKe4Bb2Nj8MRxXXXNpxIJqjJHM0IRps5J0U8gsnEEcny8Zf0tB"
             'h7NGkTteNv554QUbNVA cannot be verified with public credentials of identity test-1234."},'
             '{"errorCode":2503,"errorType":"Authorisation","message":"Signature '
             "L2PIREIx1MZsjV-j0fSMoN3u1eHP2wyqUpAs1mOWdp8k8yrnoBTbyH2Uxw8_9zYTzDHrz"
             "rI16fNKeRFuLlHosWqzoUf41M0Nip5zbW6gmPYiL05AWPdH1pg9qS-cgQa9IFXiMUkZh9"
             "EZltT7HHl9aRn35kcwoJYAoPm96Up1YPI0JWISx1iXXEAcxVOA1N_k-l0tT5Tb7lWNOI4"
             "5eh6flW_vVEeBQDjQhkl94rlP3qDFlDYZ9HZS2A3lTkiIo6MsU57pxeTD9FqwZ8uofJ3O"
             "Yx05TJKl106GPsscf2mnpnQGEzgS20QsJyqUs_u7dpZbAcjfBsaHucVz8gwkz_PoNg "
             'cannot be verified with public credentials of identity test-1234."},'
             '{"errorCode":2602,"errorType":"AssetExistence",'
             '"message":"Another identity with address test-1234 already exists"}],'
             '"requestId":"23343439","proof":"/api/v1/proofs/23343439"}'),
        )
        with pytest.raises(EntityAlreadyExists) as excinfo:
            client.put(client.identity.public_identity, request_id="1234567")
        assert str(excinfo.value) == "identity 'test-1234' already exists"
        assert excinfo.value.request_id == "1234567"
Beispiel #4
0
def test_create_request_with_content(
    client: Client,
    endorser: PrivateIdentity,
    mocked_requests_200: respx.MockTransport,
    entity: Entity,
) -> None:
    """Create endorsement request provided by a 3rd party endorser."""
    claims = [b"claim-1", b"claim-2"]

    # Create endorsement request with authorisation of endorser
    content, authorisation = endorser.endorse(entity, claims)

    # This will also add the subject holders authorisation
    client.put(
        entity,
        claims=claims,
        content=content,
        authorisations=[authorisation],
        # endorse=True # TODO - provide test that this does not have any effect
    )
    http_request, _ = mocked_requests_200["create_entity"].calls[0]

    request_id = json.loads(content.decode())["requestId"]
    assert http_request.url.path.rsplit("/", 1)[1] == request_id

    claims_header = json.loads(
        iov42_decode(http_request.headers["x-iov42-claims"]))
    assert claims_header == {}

    authorisations = json.loads(
        iov42_decode(http_request.headers["x-iov42-authorisations"].encode()))
    expected_identities = [a["identityId"] for a in authorisations]
    assert client.identity.identity_id in expected_identities
    assert endorser.identity_id in expected_identities
Beispiel #5
0
def test_raises_claims_missing(client: Client, entity: Entity) -> None:
    """Raise TyepError if no claims are provided for endorsement."""
    with pytest.raises(TypeError) as excinfo:
        client.put(entity, endorse=True)
    assert (str(
        excinfo.value
    ) == "missing required keyword argument needed for endorsement: 'claims'")
Beispiel #6
0
def create_asset_type(ctx: click.core.Context, identity: str,
                      asset_type_id: str, scale: int) -> None:
    """Create an asset type."""
    asset_type = AssetType(asset_type_id)
    id = _load_identity(identity)
    client = Client(ctx.obj["url"], id)
    _ = client.put(asset_type, request_id=ctx.obj["request_id"])
    print(f"asset_type_id: {asset_type_id}")
Beispiel #7
0
def create_asset(ctx: click.core.Context, identity: str, asset_type_id: str,
                 asset_id: str) -> None:
    """Create an asset."""
    asset = Asset(asset_type_id=asset_type_id, asset_id=asset_id)
    id = _load_identity(identity)
    client = Client(ctx.obj["url"], id)
    _ = client.put(asset, request_id=ctx.obj["request_id"])
    print(f"asset_id: {asset}")
Beispiel #8
0
def test_propagate_close(identity: PrivateIdentity) -> None:
    """Propagate close to the wrapped HTTP client implementation."""
    with mock.patch.object(HttpClient, "close") as mocked_close:
        client = Client("https://example.org", identity)

        client.close()

        mocked_close.assert_called_once_with()
Beispiel #9
0
def register_product(client: Client, tag_type: AssetType,
                     product: Tuple[str, str]) -> None:
    """Register product on iov42 platform and add product information as claim."""
    tag_id, claim = product
    tag = Asset(asset_id=tag_id, asset_type_id=tag_type.asset_type_id)
    client.put(tag)
    print(f"Created tag: {tag_id}")
    client.put(tag, claims=[claim.encode()], endorse=True)
    print(f"Tag [{tag_id}]: added enrosement on claim '{claim}'")
def existing_asset_claims(alice_client: Client,
                          existing_asset: Asset) -> List[bytes]:
    """Return a list of claims endorsed against an asset owned by Alice."""
    claims = [b"asset-claim-1", b"asset-claim-2"]
    alice_client.put(existing_asset,
                     claims=claims,
                     endorse=True,
                     create_claims=True)
    return claims
Beispiel #11
0
def create_identity(ctx: click.core.Context, identity_id: str,
                    crypto_protocol: str) -> None:
    """Create an identity."""
    private_key = generate_private_key(crypto_protocol)
    identity = PrivateIdentity(private_key, identity_id)
    client = Client(ctx.obj["url"], identity)

    _ = client.put(identity.public_identity, request_id=ctx.obj["request_id"])

    print(_identity_json(identity))
Beispiel #12
0
def test_invalid_request_id(client: Client, entity: Entity,
                            invalid_request_id: str) -> None:
    """Raise exception if the provided request ID contains invalid charatcers."""
    with pytest.raises(ValueError) as excinfo:
        client.put(entity, request_id=invalid_request_id)
    # No request is sent
    assert not respx.calls
    assert (
        str(excinfo.value) ==
        f"invalid identifier '{invalid_request_id}' - valid characters are [a-zA-Z0-9._\\-+]"
    )
Beispiel #13
0
def test_read_unique_asset_endorsement_header(
    client: Client,
    endorser: PublicIdentity,
    mocked_requests_200: respx.MockTransport,
) -> None:
    """GET request has only x-iov42-authentication header."""
    asset = Asset(asset_type_id="1234567")
    client.get(asset, claim=b"claim-1", endorser_id=endorser.identity_id)

    assert mocked_requests_200["read_asset_endorsement"].call_count == 1
    http_request, _ = mocked_requests_200["read_asset_endorsement"].calls[0]
    assert "x-iov42-authentication" in [*http_request.headers]
Beispiel #14
0
def test_raise_on_request_error(client: Client) -> None:
    """If raise exception on a request error."""
    respx.put(
        re.compile(PLATFORM_URL + "/api/v1/requests/.*$"),
        content=httpcore.ConnectError(),
    )

    # TODO: do we really want to leak httpx to our clients?
    # We could catch all exception thrown by httpx, wrap it in a few library
    # exceptions and rethrow those.
    with pytest.raises(httpx.ConnectError):
        client.put(client.identity.public_identity)
Beispiel #15
0
def test_get_node_id(
    client: Client,
    mocked_requests_200: respx.MockTransport,
) -> None:
    """Retrieve node_id on the very first GET request."""
    client.get(
        Asset(asset_type_id="1234567"),
        claim=b"claim-1",
        endorser_id=client.identity.identity_id,
    )

    assert client.node_id == "node-1"
    assert mocked_requests_200["read_node_info"].call_count == 1
Beispiel #16
0
def test_raise_duplicate_request_id(client: Client) -> None:
    """Raise exception when the request_id already exists."""
    with respx.mock(base_url=PLATFORM_URL) as respx_mock:
        respx_mock.put(
            re.compile("/api/v1/requests/.*$"),
            status_code=409,
            content=
            ('{"errors":[{"errorCode":2701,"errorType":"RequestId",'
             '"message":"Found duplicate request id"}],"requestId":"1234567"}'
             ),
        )
        with pytest.raises(DuplicateRequestId) as excinfo:
            client.put(client.identity.public_identity, request_id="1234567")
        assert str(excinfo.value) == "request ID already exists"
        assert excinfo.value.request_id == "1234567"
Beispiel #17
0
def test_authentication_header(
    client: Client,
    endorser: PublicIdentity,
    mocked_requests_200: respx.MockTransport,
) -> None:
    """The x-iov42-authentication header is signed by the identity."""
    asset = Asset(asset_type_id="1234567")
    client.get(asset, claim=b"claim-1", endorser_id=endorser.identity_id)

    http_request, _ = mocked_requests_200["read_asset_endorsement"].calls[0]
    authentication = json.loads(
        iov42_decode(http_request.headers["x-iov42-authentication"].encode()))
    assert len(authentication) == 3
    assert authentication["identityId"] == client.identity.identity_id
    assert authentication[
        "protocolId"] == client.identity.private_key.protocol.name
Beispiel #18
0
def test_response(client: Client, mocked_requests_200: respx.MockTransport,
                  entity: Entity) -> None:
    """Platform response to the create an entity request."""
    request_id = str(uuid.uuid4())
    response = client.put(entity, request_id=request_id)
    assert response.proof == "/api/v1/proofs/" + request_id
    assert len(response.resources) == 1  # type: ignore[union-attr]
Beispiel #19
0
def test_header_content_type(client: Client,
                             mocked_requests_200: respx.MockTransport,
                             entity: Entity) -> None:
    """PUT request content-type is JSON."""
    _ = client.put(entity)
    http_request, _ = mocked_requests_200["create_entity"].calls[0]
    assert http_request.headers["content-type"] == "application/json"
Beispiel #20
0
def test_context_manager(identity: PrivateIdentity) -> None:
    """Close connection when leaving the context manager."""
    with mock.patch.object(HttpClient, "close") as mocked_close:
        with Client("https://example.org", identity) as _:
            pass

        mocked_close.assert_called_once_with()
def test_create_asset_claims_with_endorsement(alice_client: Client,
                                              existing_asset: Asset) -> None:
    """Create asset claims and (self-) endorsements on an unique asset all at once."""
    claims = [b"claim-1", b"claim-2"]

    response = alice_client.put(existing_asset,
                                claims=claims,
                                endorse=True,
                                create_claims=True)

    prefix = "/".join((
        "/api/v1/asset-types",
        existing_asset.asset_type_id,
        "assets",
        existing_asset.asset_id,
        "claims",
    ))
    # Affected resources: for each endorsements we also created the claim.
    assert len(
        response.resources) == 2 * len(claims)  # type: ignore[union-attr]
    for c in [hashed_claim(c) for c in claims]:
        assert "/".join(
            (prefix, c)) in response.resources  # type: ignore[union-attr]
        assert ("/".join(
            (prefix, c, "endorsements", alice_client.identity.identity_id))
                in response.resources  # type: ignore[union-attr]
                )
Beispiel #22
0
def main() -> None:
    """Create identity, asset type and register products."""
    product_data = read_product_data("nfc-tags.csv")

    # Usually we would store the identity (ID and key) on a safe place.
    manufacturer = PrivateIdentity(
        CryptoProtocol.SHA256WithECDSA.generate_private_key())

    with Client(cfg.iov42_platform["url"], manufacturer) as client:
        # Create the identity
        client.put(manufacturer.public_identity)
        print(f"Created manufacturer identity: {manufacturer.identity_id}")

        # Create the asset typ used for the NFC tags.
        tag_type = AssetType()
        client.put(tag_type)
        print(f"Created tag asset type: {tag_type}")

        # Register the NFC tags on the distributed ledger in parallel.
        with ThreadPoolExecutor(max_workers=20) as executor:
            _ = executor.map(
                register_product,
                [client] * len(product_data),
                [tag_type] * len(product_data),
                product_data,
            )
            executor.shutdown(wait=True)
Beispiel #23
0
def test_raise_identity_already_exists(client: Client) -> None:
    """Raise exception when an identity already exists."""
    client.identity = PrivateIdentity(
        CryptoProtocol.SHA256WithECDSA.generate_private_key(), "test-1234")
    with respx.mock(base_url=PLATFORM_URL) as respx_mock:
        respx_mock.put(
            re.compile("/api/v1/requests/.*$"),
            status_code=400,
            content=
            ('{"errors":[{"errorCode":2602,"errorType":"AssetExistence",'
             '"message":"Another identity with address test-1234 already exists"}],'
             '"requestId":"1234567","proof":"/api/v1/proofs/23343456"}'),
        )
        with pytest.raises(EntityAlreadyExists) as excinfo:
            client.put(client.identity.public_identity, request_id="1234567")
        assert str(excinfo.value) == "identity 'test-1234' already exists"
        assert excinfo.value.request_id == "1234567"
Beispiel #24
0
def test_authentication_header_signature(
    client: Client,
    endorser: PublicIdentity,
    mocked_requests_200: respx.MockTransport,
) -> None:
    """Signature of x-iov42-authentication header is the signed request URL."""
    asset = Asset(asset_type_id="1234567")
    client.get(asset, claim=b"claim-1", endorser_id=endorser.identity_id)

    http_request, _ = mocked_requests_200["read_asset_endorsement"].calls[0]
    authentication = json.loads(
        iov42_decode(http_request.headers["x-iov42-authentication"].encode()))
    try:
        content = http_request.url.raw_path
        client.identity.verify_signature(authentication["signature"], content)
    except InvalidSignature:
        pytest.fail("Signature verification failed")
Beispiel #25
0
def test_call_to_put_endpoint(client: Client,
                              mocked_requests_200: respx.MockTransport,
                              entity: Entity) -> None:
    """Corret endpoint is called once."""
    request_id = str(uuid.uuid4())
    _ = client.put(entity, request_id=request_id)
    http_request, _ = mocked_requests_200["create_entity"].calls[0]
    assert str(http_request.url).rsplit("/", 1)[1] == request_id
Beispiel #26
0
def test_close_on_del(identity: PrivateIdentity) -> None:
    """Resources are freed on deleting the object."""
    with mock.patch.object(HttpClient, "close") as mocked_close:
        client = Client("https://example.org", identity)

        del client

        mocked_close.assert_called_once_with()
Beispiel #27
0
def test_node_id_cached(
    client: Client,
    mocked_requests_200: respx.MockTransport,
) -> None:
    """node_id is cached after the first GET request."""
    client.get(
        Asset(asset_type_id="1234567"),
        claim=b"claim-1",
        endorser_id=client.identity.identity_id,
    )
    client.get(
        Asset(asset_type_id="1234567"),
        claim=b"claim-1",
        endorser_id=client.identity.identity_id,
    )

    assert mocked_requests_200["read_node_info"].call_count == 1
def test_create_asset_type(alice_client: Client,
                           asset_type: AssetType) -> None:
    """Create an asset types on an iov42 platform."""
    response = alice_client.put(asset_type)

    assert ("/".join(("/api/v1/asset-types", asset_type.asset_type_id
                      )) == response.resources[0]  # type: ignore[union-attr]
            )
Beispiel #29
0
def test_response_identity(
    client: Client,
    mocked_requests_200: respx.MockTransport,
) -> None:
    """Platform response to the request to create an identity."""
    response = client.put(client.identity.public_identity)
    assert response.resources == [  # type: ignore[union-attr]
        "/api/v1/identities/" + client.identity.identity_id
    ]
Beispiel #30
0
def test_iov42_headers(client: Client,
                       mocked_requests_200: respx.MockTransport,
                       entity: Entity) -> None:
    """Authentication and authorisations are created with the request."""
    _ = client.put(entity)

    http_request, _ = mocked_requests_200["create_entity"].calls[0]
    assert "x-iov42-authorisations" in [*http_request.headers]
    assert "x-iov42-authentication" in [*http_request.headers]