Skip to content

Commit

Permalink
feat: Refactor revocation module and improve credential handling
Browse files Browse the repository at this point in the history
  • Loading branch information
EmadAnwer committed Jun 2, 2024
1 parent 513b712 commit 0b61e3d
Show file tree
Hide file tree
Showing 3 changed files with 140 additions and 8 deletions.
3 changes: 0 additions & 3 deletions aries_cloudagent/anoncreds/revocation.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@
CATEGORY_CRED_DEF_PRIVATE,
STATE_FINISHED,
AnonCredsIssuer,
AnonCredsIssuerError,
)
from .models.anoncreds_revocation import (
RevList,
Expand Down Expand Up @@ -719,8 +718,6 @@ async def upload_tails_file(self, rev_reg_def: RevRegDef):
backoff=-0.5,
max_attempts=5, # heuristic: respect HTTP timeout
)
print("#1 tails_server", upload_success)
print("#2 tails_server", result)

if not upload_success:
raise AnonCredsRevocationError(
Expand Down
127 changes: 127 additions & 0 deletions aries_cloudagent/anoncreds/tests/test_revocation.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
RevocationRegistryDefinitionPrivate,
RevocationStatusList,
Schema,
W3cCredential,
)
from aries_askar import AskarError, AskarErrorCode
from requests import RequestException, Session
Expand Down Expand Up @@ -1380,3 +1381,129 @@ async def test_clear_pending_revocations_with_non_anoncreds_session(self):
await self.revocation.clear_pending_revocations(
self.profile.session(), rev_reg_def_id="test-rev-reg-id"
)

@mock.patch.object(
AnonCredsIssuer, "cred_def_supports_revocation", return_value=True
)
async def test_create_credential_w3c(self, mock_supports_revocation):
self.profile.inject = mock.Mock(
return_value=mock.MagicMock(
get_schema=mock.CoroutineMock(
return_value=GetSchemaResult(
schema_id="CsQY9MGeD3CQP4EyuVFo5m:2:MYCO Biomarker:0.0.3",
schema=AnonCredsSchema(
issuer_id="CsQY9MGeD3CQP4EyuVFo5m",
name="MYCO Biomarker:0.0.3",
version="1.0",
attr_names=["attr1", "attr2"],
),
schema_metadata={},
resolution_metadata={},
)
)
)
)
self.revocation.get_or_create_active_registry = mock.CoroutineMock(
return_value=RevRegDefResult(
job_id="test-job-id",
revocation_registry_definition_state=RevRegDefState(
state=RevRegDefState.STATE_FINISHED,
revocation_registry_definition_id="active-reg-reg",
revocation_registry_definition=rev_reg_def,
),
registration_metadata={},
revocation_registry_definition_metadata={},
)
)

self.revocation._create_credential_w3c = mock.CoroutineMock(
return_value=({"cred": "cred"}, 98)
)

result = await self.revocation.create_credential_w3c(
credential_offer={
"schema_id": "CsQY9MGeD3CQP4EyuVFo5m:2:MYCO Biomarker:0.0.3",
"cred_def_id": "CsQY9MGeD3CQP4EyuVFo5m:3:CL:14951:MYCO_Biomarker",
"key_correctness_proof": {},
"nonce": "nonce",
},
credential_request={},
credential_values={},
)

assert isinstance(result, tuple)
assert mock_supports_revocation.call_count == 1

@mock.patch.object(InMemoryProfileSession, "handle")
@mock.patch.object(W3cCredential, "create", return_value=mock.MagicMock())
async def test_create_credential_w3c_private_no_rev_reg_or_tails(
self, mock_create, mock_handle
):
mock_handle.fetch = mock.CoroutineMock(side_effect=[MockEntry(), MockEntry()])
await self.revocation._create_credential_w3c(
credential_definition_id="test-cred-def-id",
schema_attributes=["attr1", "attr2"],
credential_offer={
"schema_id": "CsQY9MGeD3CQP4EyuVFo5m:2:MYCO Biomarker:0.0.3",
"cred_def_id": "CsQY9MGeD3CQP4EyuVFo5m:3:CL:14951:MYCO_Biomarker",
"key_correctness_proof": {},
"nonce": "nonce",
},
credential_request={},
credential_values={
"attr1": "value1",
"attr2": "value2",
},
)
assert mock_create.called

# askar error retrieving cred def
mock_handle.fetch = mock.CoroutineMock(
side_effect=AskarError(AskarErrorCode.UNEXPECTED, "test")
)
with self.assertRaises(test_module.AnonCredsRevocationError):
await self.revocation._create_credential_w3c(
credential_definition_id="test-cred-def-id",
schema_attributes=["attr1", "attr2"],
credential_offer={},
credential_request={},
credential_values={},
)

# missing cred def or cred def private
mock_handle.fetch = mock.CoroutineMock(side_effect=[None, MockEntry()])
with self.assertRaises(test_module.AnonCredsRevocationError):
await self.revocation._create_credential_w3c(
credential_definition_id="test-cred-def-id",
schema_attributes=["attr1", "attr2"],
credential_offer={},
credential_request={},
credential_values={},
)
mock_handle.fetch = mock.CoroutineMock(side_effect=[MockEntry(), None])
with self.assertRaises(test_module.AnonCredsRevocationError):
await self.revocation._create_credential_w3c(
credential_definition_id="test-cred-def-id",
schema_attributes=["attr1", "attr2"],
credential_offer={},
credential_request={},
credential_values={},
)

mock_handle.fetch = mock.CoroutineMock(side_effect=[MockEntry(), None])
with self.assertRaises(test_module.AnonCredsRevocationError):
await self.revocation._create_credential_w3c(
credential_definition_id="test-cred-def-id",
schema_attributes=["attr1", "attr2"],
credential_offer={
"schema_id": "CsQY9MGeD3CQP4EyuVFo5m:2:MYCO Biomarker:0.0.3",
"cred_def_id": "CsQY9MGeD3CQP4EyuVFo5m:3:CL:14951:MYCO_Biomarker",
"key_correctness_proof": {},
"nonce": "nonce",
},
credential_request={},
credential_values={
"x": "value1",
"y": "value2",
},
)
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import json
import logging
from typing import Mapping, Tuple

from anoncreds import W3cCredential
from ...models.cred_ex_record import V20CredExRecord
from ...models.detail.indy import (
V20CredExRecordIndy,
Expand Down Expand Up @@ -566,10 +566,18 @@ async def store_credential(
cred_def_result = await anoncreds_registry.get_credential_definition(
self.profile, cred["proof"][0]["verificationMethod"]
)
if cred["proof"][0].get("rev_reg_id"):
# TODO: remove loading of W3cCredential and use the credential directly
try:
cred_w3c = W3cCredential.load(cred)
rev_reg_id = cred_w3c.rev_reg_id
rev_reg_index = cred_w3c.rev_reg_index
except AnonCredsHolderError as e:
LOGGER.error(f"Error receiving credential: {e.error_code} - {e.message}")
raise e
if rev_reg_id:
rev_reg_def_result = (
await anoncreds_registry.get_revocation_registry_definition(
self.profile, cred["proof"][0]["rev_reg_id"]
self.profile, rev_reg_id
)
)
rev_reg_def = rev_reg_def_result.revocation_registry
Expand Down Expand Up @@ -600,8 +608,8 @@ async def store_credential(
)

detail_record.cred_id_stored = cred_id_stored
detail_record.rev_reg_id = cred["proof"][0].get("rev_reg_id", None)
detail_record.cred_rev_id = cred["proof"][0].get("cred_rev_id", None)
detail_record.rev_reg_id = rev_reg_id
detail_record.cred_rev_id = rev_reg_index

async with self.profile.session() as session:
# Store detail record, emit event
Expand Down

0 comments on commit 0b61e3d

Please sign in to comment.