Skip to content

Commit

Permalink
Use anoncreds registry for holder credential endpoints
Browse files Browse the repository at this point in the history
Signed-off-by: jamshale <jamiehalebc@gmail.com>
  • Loading branch information
jamshale committed Jun 25, 2024
1 parent f954e26 commit c8faab0
Show file tree
Hide file tree
Showing 7 changed files with 134 additions and 68 deletions.
6 changes: 5 additions & 1 deletion aries_cloudagent/anoncreds/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,11 @@ async def get_revocation_registry_definition(

@abstractmethod
async def get_revocation_list(
self, profile: Profile, revocation_registry_id: str, timestamp: int
self,
profile: Profile,
revocation_registry_id: str,
timestamp_from: Optional[int] = 0,
timestamp_to: Optional[int] = None,
) -> GetRevListResult:
"""Get a revocation list from the registry."""

Expand Down
6 changes: 5 additions & 1 deletion aries_cloudagent/anoncreds/default/did_indy/registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,11 @@ async def register_revocation_registry_definition(
raise NotImplementedError()

async def get_revocation_list(
self, profile: Profile, revocation_registry_id: str, timestamp: int
self,
profile: Profile,
revocation_registry_id: str,
timestamp_from: Optional[int] = 0,
timestamp_to: Optional[int] = None,
) -> GetRevListResult:
"""Get a revocation list from the registry."""
raise NotImplementedError()
Expand Down
16 changes: 12 additions & 4 deletions aries_cloudagent/anoncreds/default/legacy_indy/registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -740,14 +740,18 @@ async def _get_ledger(self, profile: Profile, rev_reg_def_id: str):
return ledger_id, ledger

async def get_revocation_registry_delta(
self, profile: Profile, rev_reg_def_id: str, timestamp: None
self,
profile: Profile,
rev_reg_def_id: str,
timestamp_from: Optional[int] = 0,
timestamp_to: Optional[int] = None,
) -> Tuple[dict, int]:
"""Fetch the revocation registry delta."""
ledger_id, ledger = await self._get_ledger(profile, rev_reg_def_id)

async with ledger:
delta, timestamp = await ledger.get_revoc_reg_delta(
rev_reg_def_id, timestamp_to=timestamp
rev_reg_def_id, timestamp_from=timestamp_from, timestamp_to=timestamp_to
)

if delta is None:
Expand All @@ -759,13 +763,17 @@ async def get_revocation_registry_delta(
return delta, timestamp

async def get_revocation_list(
self, profile: Profile, rev_reg_def_id: str, timestamp: int
self,
profile: Profile,
rev_reg_def_id: str,
timestamp_from: Optional[int] = 0,
timestamp_to: Optional[int] = None,
) -> GetRevListResult:
"""Get the revocation registry list."""
_, ledger = await self._get_ledger(profile, rev_reg_def_id)

delta, timestamp = await self.get_revocation_registry_delta(
profile, rev_reg_def_id, timestamp
profile, rev_reg_def_id, timestamp_from, timestamp_to
)

async with ledger:
Expand Down
33 changes: 16 additions & 17 deletions aries_cloudagent/anoncreds/holder.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,10 @@
from ..askar.profile_anon import AskarAnoncredsProfile
from ..core.error import BaseError
from ..core.profile import Profile
from ..ledger.base import BaseLedger
from ..wallet.error import WalletNotFoundError
from .error_messages import ANONCREDS_PROFILE_REQUIRED_MSG
from .models.anoncreds_cred_def import CredDef
from .registry import AnonCredsRegistry

LOGGER = logging.getLogger(__name__)

Expand Down Expand Up @@ -477,7 +477,7 @@ async def _get_credential(self, credential_id: str) -> Credential:
raise AnonCredsHolderError("Error loading requested credential") from err

async def credential_revoked(
self, ledger: BaseLedger, credential_id: str, fro: int = None, to: int = None
self, credential_id: str, fro: int = None, to: int = None
) -> bool:
"""Check ledger for revocation status of credential by cred id.
Expand All @@ -488,18 +488,18 @@ async def credential_revoked(
cred = await self._get_credential(credential_id)
rev_reg_id = cred.rev_reg_id

# TODO Use anoncreds registry
# check if cred.rev_reg_id is returning None or 'None'
if rev_reg_id:
cred_rev_id = cred.rev_reg_index
(rev_reg_delta, _) = await ledger.get_revoc_reg_delta(
rev_reg_id,
fro,
to,
anoncreds_registry = self.profile.inject(AnonCredsRegistry)
rev_list = (
await anoncreds_registry.get_revocation_list(
self.profile, rev_reg_id, fro, to
)
return cred_rev_id in rev_reg_delta["value"].get("revoked", [])
else:
return False
).revocation_list

set_revoked = {
index for index, value in enumerate(rev_list.revocation_list) if value == 1
}

return cred.rev_reg_index in set_revoked

async def delete_credential(self, credential_id: str):
"""Remove a credential stored in the wallet.
Expand All @@ -515,10 +515,9 @@ async def delete_credential(self, credential_id: str):
AnonCredsHolder.RECORD_TYPE_MIME_TYPES, credential_id
)
except AskarError as err:
if err.code == AskarErrorCode.NOT_FOUND:
pass
else:
raise AnonCredsHolderError("Error deleting credential") from err
raise AnonCredsHolderError(
"Error deleting credential", error_code=err.code
) from err # noqa: E501

async def get_mime_type(
self, credential_id: str, attr: str = None
Expand Down
16 changes: 9 additions & 7 deletions aries_cloudagent/anoncreds/registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,7 @@
BaseAnonCredsRegistrar,
BaseAnonCredsResolver,
)
from .models.anoncreds_cred_def import (
CredDef,
CredDefResult,
GetCredDefResult,
)
from .models.anoncreds_cred_def import CredDef, CredDefResult, GetCredDefResult
from .models.anoncreds_revocation import (
GetRevListResult,
GetRevRegDefResult,
Expand Down Expand Up @@ -149,11 +145,17 @@ async def register_revocation_registry_definition(
)

async def get_revocation_list(
self, profile: Profile, rev_reg_def_id: str, timestamp: int
self,
profile: Profile,
rev_reg_def_id: str,
timestamp_from: Optional[int] = 0,
timestamp_to: Optional[int] = None,
) -> GetRevListResult:
"""Get a revocation list from the registry."""
resolver = await self._resolver_for_identifier(rev_reg_def_id)
return await resolver.get_revocation_list(profile, rev_reg_def_id, timestamp)
return await resolver.get_revocation_list(
profile, rev_reg_def_id, timestamp_from, timestamp_to
)

async def register_revocation_list(
self,
Expand Down
2 changes: 1 addition & 1 deletion aries_cloudagent/anoncreds/verifier.py
Original file line number Diff line number Diff line change
Expand Up @@ -423,7 +423,7 @@ async def process_pres_identifiers(
result = await anoncreds_registry.get_revocation_list(
self.profile,
identifier["rev_reg_id"],
identifier["timestamp"],
timestamp_to=identifier["timestamp"],
)
rev_lists[identifier["rev_reg_id"]][
identifier["timestamp"]
Expand Down
123 changes: 86 additions & 37 deletions aries_cloudagent/holder/routes.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""Holder admin routes."""

import json
from profile import Profile

from aiohttp import web
from aiohttp_apispec import (
Expand All @@ -10,10 +11,12 @@
request_schema,
response_schema,
)
from aries_askar import AskarErrorCode
from marshmallow import fields

from ..admin.decorators.auth import tenant_authentication
from ..admin.request_context import AdminRequestContext
from ..anoncreds.holder import AnonCredsHolder, AnonCredsHolderError
from ..indy.holder import IndyHolder, IndyHolderError
from ..indy.models.cred_precis import IndyCredInfoSchema
from ..ledger.base import BaseLedger
Expand Down Expand Up @@ -207,7 +210,11 @@ async def credentials_get(request: web.BaseRequest):
context: AdminRequestContext = request["context"]
credential_id = request.match_info["credential_id"]

holder = context.profile.inject(IndyHolder)
if context.settings.get("wallet.type") == "askar-anoncreds":
holder = AnonCredsHolder(context.profile)
else:
holder = context.profile.inject(IndyHolder)

try:
credential = await holder.get_credential(credential_id)
except WalletNotFoundError as err:
Expand Down Expand Up @@ -236,28 +243,43 @@ async def credentials_revoked(request: web.BaseRequest):
credential_id = request.match_info["credential_id"]
fro = request.query.get("from")
to = request.query.get("to")
profile = context.profile
wallet_type = profile.settings.get_value("wallet.type")

async def get_revoked_using_anoncreds(profile: Profile):
holder = AnonCredsHolder(profile)
return await holder.credential_revoked(
credential_id,
int(fro) if fro else None,
int(to) if to else None,
)

async with context.profile.session() as session:
ledger = session.inject_or(BaseLedger)
if not ledger:
reason = "No ledger available"
if not context.settings.get_value("wallet.type"):
reason += ": missing wallet-type?"
raise web.HTTPForbidden(reason=reason)

async with ledger:
try:
holder = session.inject(IndyHolder)
revoked = await holder.credential_revoked(
ledger,
credential_id,
int(fro) if fro else None,
int(to) if to else None,
)
except WalletNotFoundError as err:
raise web.HTTPNotFound(reason=err.roll_up) from err
except LedgerError as err:
raise web.HTTPBadRequest(reason=err.roll_up) from err
async def get_revoked_using_indy(profile: Profile):
async with profile.session() as session:
holder = session.inject(IndyHolder)

ledger = session.inject_or(BaseLedger)
if not ledger:
raise web.HTTPForbidden(reason="No ledger available")

async with ledger:
try:
return await holder.credential_revoked(
ledger,
credential_id,
int(fro) if fro else None,
int(to) if to else None,
)
except LedgerError as err:
raise web.HTTPBadRequest(reason=err.roll_up) from err

try:
if wallet_type == "askar-anoncreds":
revoked = await get_revoked_using_anoncreds(profile)
else:
revoked = await get_revoked_using_indy(profile)
except WalletNotFoundError as err:
raise web.HTTPNotFound(reason=err.roll_up) from err

return web.json_response({"revoked": revoked})

Expand All @@ -279,9 +301,14 @@ async def credentials_attr_mime_types_get(request: web.BaseRequest):
context: AdminRequestContext = request["context"]
credential_id = request.match_info["credential_id"]

async with context.profile.session() as session:
holder = session.inject(IndyHolder)
if context.settings.get("wallet.type") == "askar-anoncreds":
holder = AnonCredsHolder(context.profile)
mime_types = await holder.get_mime_type(credential_id)
else:
async with context.profile.session() as session:
holder = session.inject(IndyHolder)
mime_types = await holder.get_mime_type(credential_id)

return web.json_response({"results": mime_types})


Expand All @@ -301,15 +328,33 @@ async def credentials_remove(request: web.BaseRequest):
"""
context: AdminRequestContext = request["context"]
credential_id = request.match_info["credential_id"]
profile: Profile = context.profile

try:
async with context.profile.session() as session:
holder = session.inject(IndyHolder)
async def delete_credential_using_anoncreds(profile: Profile):
try:
holder = AnonCredsHolder(profile)
await holder.delete_credential(credential_id)
topic = "acapy::record::credential::delete"
await context.profile.notify(topic, {"id": credential_id, "state": "deleted"})
except WalletNotFoundError as err:
raise web.HTTPNotFound(reason=err.roll_up) from err
except AnonCredsHolderError as err:
if err.error_code == AskarErrorCode.NOT_FOUND:
raise web.HTTPNotFound(reason=err.roll_up) from err
raise web.HTTPBadRequest(reason=err.roll_up) from err

async def delete_credential_using_indy(profile: Profile):
async with profile.session() as session:
try:
holder = session.inject(IndyHolder)
await holder.delete_credential(credential_id)
except WalletNotFoundError as err:
raise web.HTTPNotFound(reason=err.roll_up) from err

if context.settings.get("wallet.type") == "askar-anoncreds":
await delete_credential_using_anoncreds(profile)
else:
await delete_credential_using_indy(profile)

# Notify event subscribers
topic = "acapy::record::credential::delete"
await profile.notify(topic, {"id": credential_id, "state": "deleted"})

return web.json_response({})

Expand Down Expand Up @@ -343,12 +388,16 @@ async def credentials_list(request: web.BaseRequest):
start = int(start) if isinstance(start, str) else 0
count = int(count) if isinstance(count, str) else 10

async with context.profile.session() as session:
holder = session.inject(IndyHolder)
try:
credentials = await holder.get_credentials(start, count, wql)
except IndyHolderError as err:
raise web.HTTPBadRequest(reason=err.roll_up) from err
if context.settings.get("wallet.type") == "askar-anoncreds":
holder = AnonCredsHolder(context.profile)
credentials = await holder.get_credentials(start, count, wql)
else:
async with context.profile.session() as session:
holder = session.inject(IndyHolder)
try:
credentials = await holder.get_credentials(start, count, wql)
except IndyHolderError as err:
raise web.HTTPBadRequest(reason=err.roll_up) from err

return web.json_response({"results": credentials})

Expand Down

0 comments on commit c8faab0

Please sign in to comment.