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

broaden aries-rfc-36 cred proposal to underspecify cred def, allow is… #259

Merged
merged 1 commit into from
Nov 8, 2019
Merged
Changes from all commits
Commits
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
16 changes: 3 additions & 13 deletions aries_cloudagent/messaging/credential_definitions/routes.py
Original file line number Diff line number Diff line change
@@ -10,17 +10,7 @@
from ...ledger.base import BaseLedger
from ...storage.base import BaseStorage
from ..valid import INDY_CRED_DEF_ID, INDY_SCHEMA_ID, INDY_VERSION
from .util import CRED_DEF_SENT_RECORD_TYPE


CRED_DEF_SENT_PARMS = [
"schema_id",
"schema_issuer_did",
"schema_name",
"schema_version",
"issuer_did",
"cred_def_id",
]
from .util import CRED_DEF_TAGS, CRED_DEF_SENT_RECORD_TYPE


class CredentialDefinitionSendRequestSchema(Schema):
@@ -136,7 +126,7 @@ async def credential_definitions_send_credential_definition(request: web.BaseReq
"in": "query",
"schema": {"type": "string"},
"required": False,
} for p in CRED_DEF_SENT_PARMS
} for p in CRED_DEF_TAGS
],
summary="Search for matching credential definitions that agent originated",
)
@@ -158,7 +148,7 @@ async def credential_definitions_created(request: web.BaseRequest):
found = await storage.search_records(
type_filter=CRED_DEF_SENT_RECORD_TYPE,
tag_query={
p: request.query[p] for p in CRED_DEF_SENT_PARMS if p in request.query
p: request.query[p] for p in CRED_DEF_TAGS if p in request.query
}
).fetch_all()

8 changes: 8 additions & 0 deletions aries_cloudagent/messaging/credential_definitions/util.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
"""Credential definition utilities."""

CRED_DEF_TAGS = [
"schema_id",
"schema_issuer_did",
"schema_name",
"schema_version",
"issuer_did",
"cred_def_id",
]
CRED_DEF_SENT_RECORD_TYPE = "cred_def_sent"
9 changes: 3 additions & 6 deletions aries_cloudagent/messaging/schemas/routes.py
Original file line number Diff line number Diff line change
@@ -10,10 +10,7 @@
from ...ledger.base import BaseLedger
from ...storage.base import BaseStorage
from ..valid import INDY_SCHEMA_ID, INDY_VERSION
from .util import SCHEMA_SENT_RECORD_TYPE


SCHEMA_SENT_PARMS = ["schema_id", "schema_issuer_did", "schema_name", "schema_version"]
from .util import SCHEMA_SENT_RECORD_TYPE, SCHEMA_TAGS


class SchemaSendRequestSchema(Schema):
@@ -137,7 +134,7 @@ async def schemas_send_schema(request: web.BaseRequest):
"in": "query",
"schema": {"type": "string"},
"required": False,
} for p in SCHEMA_SENT_PARMS
} for p in SCHEMA_TAGS
],
summary="Search for matching schema that agent originated",
)
@@ -159,7 +156,7 @@ async def schemas_created(request: web.BaseRequest):
found = await storage.search_records(
type_filter=SCHEMA_SENT_RECORD_TYPE,
tag_query={
p: request.query[p] for p in SCHEMA_SENT_PARMS if p in request.query
p: request.query[p] for p in SCHEMA_TAGS if p in request.query
}
).fetch_all()

1 change: 1 addition & 0 deletions aries_cloudagent/messaging/schemas/util.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
"""Schema utilities."""

SCHEMA_TAGS = ["schema_id", "schema_issuer_did", "schema_name", "schema_version"]
SCHEMA_SENT_RECORD_TYPE = "schema_sent"
112 changes: 61 additions & 51 deletions aries_cloudagent/protocols/issue_credential/v1_0/manager.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
"""Classes to manage credentials."""

import logging
from typing import Tuple
from typing import Mapping, Tuple

from ....cache.base import BaseCache
from ....config.injection_context import InjectionContext
from ....error import BaseError
from ....holder.base import BaseHolder
from ....issuer.base import BaseIssuer
from ....ledger.base import BaseLedger
from ....messaging.credential_definitions.util import (
CRED_DEF_TAGS,
CRED_DEF_SENT_RECORD_TYPE
)
from ....storage.base import BaseStorage
from ....storage.error import StorageNotFoundError

from .messages.credential_issue import CredentialIssue
@@ -48,6 +53,20 @@ def context(self) -> InjectionContext:
"""
return self._context

async def _match_sent_cred_def_id(self, tag_query: Mapping[str, str]) -> str:
"""Return most recent matching id of cred def that agent sent to ledger."""

storage: BaseStorage = await self.context.inject(BaseStorage)
found = await storage.search_records(
type_filter=CRED_DEF_SENT_RECORD_TYPE,
tag_query=tag_query,
).fetch_all()
if not found:
raise CredentialManagerError(
f"Issuer has no operable cred def for proposal spec {tag_query}"
)
return max(found, key=lambda r: int(r.tags["epoch"])).tags["cred_def_id"]

async def prepare_send(
self, connection_id: str, credential_proposal: CredentialProposal
) -> Tuple[V10CredentialExchange, CredentialOffer]:
@@ -63,19 +82,11 @@ async def prepare_send(
A tuple of the new credential exchange record and credential offer message

"""

credential_definition_id = credential_proposal.cred_def_id
if not credential_definition_id:
raise CredentialManagerError(
"Proposal credential definition ID is required"
)

credential_exchange = V10CredentialExchange(
auto_issue=True,
connection_id=connection_id,
initiator=V10CredentialExchange.INITIATOR_SELF,
role=V10CredentialExchange.ROLE_ISSUER,
credential_definition_id=credential_definition_id,
credential_proposal_dict=credential_proposal.serialize(),
)
(credential_exchange, credential_offer) = await self.create_offer(
@@ -91,7 +102,12 @@ async def create_proposal(
auto_offer: bool = None,
comment: str = None,
credential_preview: CredentialPreview = None,
credential_definition_id: str,
schema_id: str = None,
schema_issuer_did: str = None,
schema_name: str = None,
schema_version: str = None,
cred_def_id: str = None,
issuer_did: str = None,
) -> V10CredentialExchange:
"""
Create a credential proposal.
@@ -103,32 +119,30 @@ async def create_proposal(
comment: Optional human-readable comment to include in proposal
credential_preview: The credential preview to use to create
the credential proposal
credential_definition_id: Credential definition id for the
credential proposal
schema_id: Schema id for credential proposal
schema_issuer_did: Schema issuer DID for credential proposal
schema_name: Schema name for credential proposal
schema_version: Schema version for credential proposal
cred_def_id: Credential definition id for credential proposal
issuer_did: Issuer DID for credential proposal

Returns:
Resulting credential exchange record including credential proposal

"""
# Credential definition id must be present
if not credential_definition_id:
raise CredentialManagerError("credential_definition_id is not set")

# Credential preview must be present
if not credential_preview:
raise CredentialManagerError("credential_preview is not set")

ledger: BaseLedger = await self.context.inject(BaseLedger)
async with ledger:
schema_id = await ledger.credential_definition_id2schema_id(
credential_definition_id
)

credential_proposal_message = CredentialProposal(
comment=comment,
credential_proposal=credential_preview,
schema_id=schema_id,
cred_def_id=credential_definition_id,
schema_issuer_did=schema_issuer_did,
schema_name=schema_name,
schema_version=schema_version,
cred_def_id=cred_def_id,
issuer_did=issuer_did
)

credential_exchange_record = V10CredentialExchange(
@@ -137,8 +151,6 @@ async def create_proposal(
initiator=V10CredentialExchange.INITIATOR_SELF,
role=V10CredentialExchange.ROLE_HOLDER,
state=V10CredentialExchange.STATE_PROPOSAL_SENT,
credential_definition_id=credential_definition_id,
schema_id=schema_id,
credential_proposal_dict=credential_proposal_message.serialize(),
auto_offer=auto_offer,
)
@@ -158,24 +170,14 @@ async def receive_proposal(self) -> V10CredentialExchange:
# go to cred def via ledger to get authoritative schema id
credential_proposal_message = self.context.message
connection_id = self.context.connection_record.connection_id
cred_def_id = credential_proposal_message.cred_def_id
if cred_def_id:
ledger: BaseLedger = await self.context.inject(BaseLedger)
async with ledger:
schema_id = await ledger.credential_definition_id2schema_id(cred_def_id)
else:
raise CredentialManagerError(
"credential definition identifier is not set in proposal"
)

# at this point, cred def and schema still open to potential negotiation
credential_exchange_record = V10CredentialExchange(
connection_id=connection_id,
thread_id=credential_proposal_message._thread_id,
initiator=V10CredentialExchange.INITIATOR_EXTERNAL,
role=V10CredentialExchange.ROLE_ISSUER,
state=V10CredentialExchange.STATE_PROPOSAL_RECEIVED,
credential_definition_id=cred_def_id,
schema_id=schema_id,
credential_proposal_dict=credential_proposal_message.serialize(),
auto_offer=self.context.settings.get(
"debug.auto_respond_credential_proposal"
@@ -204,32 +206,38 @@ async def create_offer(
A tuple (credential exchange record, credential offer message)

"""
credential_definition_id = credential_exchange_record.credential_definition_id
if credential_exchange_record.credential_proposal_dict:
cred_preview = CredentialProposal.deserialize(
credential_proposal_message = CredentialProposal.deserialize(
credential_exchange_record.credential_proposal_dict
).credential_proposal
)
cred_def_id = await self._match_sent_cred_def_id(
{
t: getattr(credential_proposal_message, t)
for t in CRED_DEF_TAGS if getattr(credential_proposal_message, t)
}
)

cred_preview = credential_proposal_message.credential_proposal
else:
cred_def_id = credential_exchange_record.credential_definition_id
cred_preview = None

async def _create():
async def _create(cred_def_id):
issuer: BaseIssuer = await self.context.inject(BaseIssuer)
return await issuer.create_credential_offer(
credential_definition_id
)
return await issuer.create_credential_offer(cred_def_id)

credential_offer = None
cache_key = f"credential_offer::{credential_definition_id}"
cache_key = f"credential_offer::{cred_def_id}"
cache: BaseCache = await self.context.inject(BaseCache, required=False)
if cache:
async with cache.acquire(cache_key) as entry:
if entry.result:
credential_offer = entry.result
else:
credential_offer = await _create()
credential_offer = await _create(cred_def_id)
await entry.set_result(credential_offer, 3600)
if not credential_offer:
credential_offer = await _create()
credential_offer = await _create(cred_def_id)

credential_offer_message = CredentialOffer(
comment=comment,
@@ -267,13 +275,15 @@ async def receive_offer(self) -> V10CredentialExchange:

credential_preview = credential_offer_message.credential_preview
indy_offer = credential_offer_message.indy_offer(0)
schema_id = indy_offer["schema_id"]
cred_def_id = indy_offer["cred_def_id"]

if credential_preview:
credential_proposal_dict = CredentialProposal(
comment=credential_offer_message.comment,
credential_proposal=credential_preview,
cred_def_id=indy_offer["cred_def_id"],
schema_id=indy_offer["schema_id"],
schema_id=schema_id,
cred_def_id=cred_def_id,
).serialize()
else:
credential_proposal_dict = None
@@ -295,13 +305,13 @@ async def receive_offer(self) -> V10CredentialExchange:
thread_id=credential_offer_message._thread_id,
initiator=V10CredentialExchange.INITIATOR_EXTERNAL,
role=V10CredentialExchange.ROLE_HOLDER,
credential_definition_id=indy_offer["cred_def_id"],
schema_id=indy_offer["schema_id"],
credential_proposal_dict=credential_proposal_dict,
)

credential_exchange_record.credential_offer = indy_offer
credential_exchange_record.state = V10CredentialExchange.STATE_OFFER_RECEIVED
credential_exchange_record.schema_id = schema_id
credential_exchange_record.credential_definition_id = cred_def_id

await credential_exchange_record.save(
self.context, reason="receive credential offer"
@@ -462,7 +472,7 @@ async def issue_credential(
)

credential_exchange_record.state = V10CredentialExchange.STATE_ISSUED
await credential_exchange_record.save(self.context, reason="receive credential")
await credential_exchange_record.save(self.context, reason="issue credential")

credential_message = CredentialIssue(
comment=comment,
Original file line number Diff line number Diff line change
@@ -3,6 +3,12 @@
from marshmallow import fields

from .....messaging.agent_message import AgentMessage, AgentMessageSchema
from .....messaging.valid import (
INDY_CRED_DEF_ID,
INDY_DID,
INDY_SCHEMA_ID,
INDY_VERSION,
)

from ..message_types import CREDENTIAL_PROPOSAL, PROTOCOL_PACKAGE

@@ -32,7 +38,11 @@ def __init__(
comment: str = None,
credential_proposal: CredentialPreview = None,
schema_id: str = None,
schema_issuer_did: str = None,
schema_name: str = None,
schema_version: str = None,
cred_def_id: str = None,
issuer_did: str = None,
**kwargs,
):
"""
@@ -42,15 +52,23 @@ def __init__(
comment: optional human-readable comment
credential_proposal: proposed credential preview
schema_id: schema identifier
schema_issuer_did: schema issuer DID
schema_name: schema name
schema_version: schema version
cred_def_id: credential definition identifier
issuer_did: credential issuer DID
"""
super().__init__(_id, **kwargs)
self.comment = comment
self.credential_proposal = (
credential_proposal if credential_proposal else CredentialPreview()
)
self.schema_id = schema_id
self.schema_issuer_did = schema_issuer_did
self.schema_name = schema_name
self.schema_version = schema_version
self.cred_def_id = cred_def_id
self.issuer_did = issuer_did


class CredentialProposalSchema(AgentMessageSchema):
@@ -63,5 +81,32 @@ class Meta:

comment = fields.Str(required=False, allow_none=False)
credential_proposal = fields.Nested(CredentialPreviewSchema, required=True)
schema_id = fields.Str(required=False, allow_none=False)
cred_def_id = fields.Str(required=False, allow_none=False)
schema_id = fields.Str(
required=False,
allow_none=False,
**INDY_SCHEMA_ID
)
schema_issuer_did = fields.Str(
required=False,
allow_none=False,
**INDY_DID
)
schema_name = fields.Str(
required=False,
allow_none=False,
)
schema_version = fields.Str(
required=False,
allow_none=False,
**INDY_VERSION
)
cred_def_id = fields.Str(
required=False,
allow_none=False,
**INDY_CRED_DEF_ID
)
issuer_did = fields.Str(
required=False,
allow_none=False,
**INDY_DID
)
Original file line number Diff line number Diff line change
@@ -88,15 +88,37 @@ def test_serialize(self):
class TestCredentialProposalSchema(TestCase):
"""Test credential cred proposal schema."""

credential_proposal = CredentialProposal(
comment="Hello World",
credential_proposal=CRED_PREVIEW,
schema_id="GMm4vMw8LLrLJjp81kRRLp:2:ahoy:1560364003.0",
cred_def_id="GMm4vMw8LLrLJjp81kRRLp:3:CL:12:tag",
)
credential_proposals = [
CredentialProposal(
credential_proposal=CRED_PREVIEW,
),
CredentialProposal(
comment="Hello World",
credential_proposal=CRED_PREVIEW,
cred_def_id="GMm4vMw8LLrLJjp81kRRLp:3:CL:12:tag"
),
CredentialProposal(
comment="Hello World",
credential_proposal=CRED_PREVIEW,
schema_id="GMm4vMw8LLrLJjp81kRRLp:2:ahoy:1.0",
),
CredentialProposal(
comment="Hello World",
credential_proposal=CRED_PREVIEW,
schema_issuer_did="GMm4vMw8LLrLJjp81kRRLp",
),
CredentialProposal(
comment="Hello World",
credential_proposal=CRED_PREVIEW,
schema_name="ahoy",
schema_version="1.0",
issuer_did="GMm4vMw8LLrLJjp81kRRLp",
),
]

def test_make_model(self):
"""Test making model."""
data = self.credential_proposal.serialize()
model_instance = CredentialProposal.deserialize(data)
assert isinstance(model_instance, CredentialProposal)
for credential_proposal in self.credential_proposals:
data = credential_proposal.serialize()
model_instance = CredentialProposal.deserialize(data)
assert isinstance(model_instance, CredentialProposal)
77 changes: 50 additions & 27 deletions aries_cloudagent/protocols/issue_credential/v1_0/routes.py
Original file line number Diff line number Diff line change
@@ -6,7 +6,14 @@

from ....connections.models.connection_record import ConnectionRecord
from ....holder.base import BaseHolder
from ....messaging.valid import INDY_CRED_DEF_ID, UUIDFour
from ....messaging.credential_definitions.util import CRED_DEF_TAGS
from ....messaging.valid import (
INDY_CRED_DEF_ID,
INDY_DID,
INDY_SCHEMA_ID,
INDY_VERSION,
UUIDFour,
)
from ....storage.error import StorageNotFoundError

from ...problem_report.message import ProblemReport
@@ -44,13 +51,38 @@ class V10CredentialProposalRequestSchema(Schema):
required=True,
example=UUIDFour.EXAMPLE, # typically but not necessarily a UUID4
)
credential_definition_id = fields.Str(
cred_def_id = fields.Str(
description="Credential definition identifier",
required=True,
required=False,
**INDY_CRED_DEF_ID,
)
schema_id = fields.Str(
description="Schema identifier",
required=False,
**INDY_SCHEMA_ID,
)
schema_issuer_did = fields.Str(
description="Schema issuer DID",
required=False,
**INDY_DID,
)
schema_name = fields.Str(
description="Schema name",
required=False,
example="preferences",
)
schema_version = fields.Str(
description="Schema version",
required=False,
**INDY_VERSION,
)
issuer_did = fields.Str(
description="Credential issuer DID",
required=False,
**INDY_DID,
)
comment = fields.Str(description="Human-readable comment", required=False)
credential_preview = fields.Nested(CredentialPreviewSchema, required=True)
credential_proposal = fields.Nested(CredentialPreviewSchema, required=True)


class V10CredentialOfferRequestSchema(Schema):
@@ -61,7 +93,7 @@ class V10CredentialOfferRequestSchema(Schema):
required=True,
example=UUIDFour.EXAMPLE, # typically but not necessarily a UUID4
)
credential_definition_id = fields.Str(
cred_def_id = fields.Str(
description="Credential definition identifier",
required=True,
**INDY_CRED_DEF_ID,
@@ -136,10 +168,7 @@ async def credential_exchange_list(request: web.BaseRequest):
return web.json_response({"results": [record.serialize() for record in records]})


@docs(
tags=["issue-credential exchange"],
summary="Fetch a single credential exchange record",
)
@docs(tags=["issue-credential"], summary="Fetch a single credential exchange record")
@response_schema(V10CredentialExchangeSchema(), 200)
async def credential_exchange_retrieve(request: web.BaseRequest):
"""
@@ -186,15 +215,11 @@ async def credential_exchange_send(request: web.BaseRequest):

body = await request.json()

connection_id = body.get("connection_id")
credential_definition_id = body.get("credential_definition_id")
comment = body.get("comment")
preview_spec = body.get("credential_preview")

if not credential_definition_id:
raise web.HTTPBadRequest(reason="credential_definition_id must be provided.")
connection_id = body.get("connection_id")
preview_spec = body.get("credential_proposal")
if not preview_spec:
raise web.HTTPBadRequest(reason="credential_preview must be provided.")
raise web.HTTPBadRequest(reason="credential_proposal must be provided.")

try:
connection_record = await ConnectionRecord.retrieve_by_id(
@@ -209,7 +234,7 @@ async def credential_exchange_send(request: web.BaseRequest):
credential_proposal = CredentialProposal(
comment=comment,
credential_proposal=CredentialPreview.deserialize(preview_spec),
cred_def_id=credential_definition_id,
**{t: body.get(t) for t in CRED_DEF_TAGS if body.get(t)},
)

credential_manager = CredentialManager(context)
@@ -247,12 +272,10 @@ async def credential_exchange_send_proposal(request: web.BaseRequest):
body = await request.json()

connection_id = body.get("connection_id")
credential_definition_id = body.get("credential_definition_id")
comment = body.get("comment")
preview_spec = body.get("credential_preview")

preview_spec = body.get("credential_proposal")
if not preview_spec:
raise web.HTTPBadRequest(reason="credential_preview must be provided.")
raise web.HTTPBadRequest(reason="credential_proposal must be provided.")

try:
connection_record = await ConnectionRecord.retrieve_by_id(
@@ -272,7 +295,7 @@ async def credential_exchange_send_proposal(request: web.BaseRequest):
connection_id,
comment=comment,
credential_preview=credential_preview,
credential_definition_id=credential_definition_id,
**{t: body.get(t) for t in CRED_DEF_TAGS if body.get(t)},
)

await outbound_handler(
@@ -312,15 +335,15 @@ async def credential_exchange_send_free_offer(request: web.BaseRequest):
body = await request.json()

connection_id = body.get("connection_id")
credential_definition_id = body.get("credential_definition_id")
cred_def_id = body.get("cred_def_id")
auto_issue = body.get(
"auto_issue", context.settings.get("debug.auto_respond_credential_request")
)
comment = body.get("comment")
preview_spec = body.get("credential_preview")

if not credential_definition_id:
raise web.HTTPBadRequest(reason="credential_definition_id is required")
if not cred_def_id:
raise web.HTTPBadRequest(reason="cred_def_id is required")

if auto_issue and not preview_spec:
raise web.HTTPBadRequest(
@@ -343,7 +366,7 @@ async def credential_exchange_send_free_offer(request: web.BaseRequest):
credential_proposal = CredentialProposal(
comment=comment,
credential_proposal=credential_preview,
cred_def_id=credential_definition_id,
cred_def_id=cred_def_id,
)
credential_proposal_dict = credential_proposal.serialize()
else:
@@ -352,7 +375,7 @@ async def credential_exchange_send_free_offer(request: web.BaseRequest):
credential_exchange_record = V10CredentialExchange(
connection_id=connection_id,
initiator=V10CredentialExchange.INITIATOR_SELF,
credential_definition_id=credential_definition_id,
credential_definition_id=cred_def_id,
credential_proposal_dict=credential_proposal_dict,
auto_issue=auto_issue,
)
Original file line number Diff line number Diff line change
@@ -45,12 +45,13 @@ async def test_prepare_send(self):
ret_exchange, ret_cred_offer = await self.manager.prepare_send(connection_id, proposal)
create_offer.assert_called_once()
assert ret_exchange is create_offer.return_value[0]
exchange = create_offer.call_args[1]["credential_exchange_record"]
assert exchange.auto_issue
assert exchange.connection_id == connection_id
assert exchange.credential_definition_id == cred_def_id
assert exchange.role == exchange.ROLE_ISSUER
assert exchange.credential_proposal_dict == proposal.serialize()
arg_exchange = create_offer.call_args[1]["credential_exchange_record"]
assert arg_exchange.auto_issue
assert arg_exchange.connection_id == connection_id
assert arg_exchange.schema_id == None
assert arg_exchange.credential_definition_id == None
assert arg_exchange.role == V10CredentialExchange.ROLE_ISSUER
assert arg_exchange.credential_proposal_dict == proposal.serialize()

async def test_create_proposal(self):
schema_id = "LjgpST2rjsoxYegQDRm7EL:2:bc-reg:1.0"
@@ -65,22 +66,13 @@ async def test_create_proposal(self):
return_value=schema_id
)

with self.assertRaises(CredentialManagerError):
await self.manager.create_proposal(
connection_id,
auto_offer=True,
comment=comment,
credential_preview=preview,
credential_definition_id=None,
)

with self.assertRaises(CredentialManagerError):
await self.manager.create_proposal(
connection_id,
auto_offer=True,
comment=comment,
credential_preview=None,
credential_definition_id=cred_def_id,
cred_def_id=cred_def_id,
)

with async_mock.patch.object(
@@ -91,15 +83,24 @@ async def test_create_proposal(self):
auto_offer=True,
comment=comment,
credential_preview=preview,
credential_definition_id=cred_def_id,
cred_def_id=cred_def_id,
)
save_ex.assert_called_once()

await self.manager.create_proposal(
connection_id,
auto_offer=True,
comment=comment,
credential_preview=preview,
cred_def_id=None,
) # OK to leave open until offer

proposal = CredentialProposal.deserialize(exchange.credential_proposal_dict)

assert exchange.auto_offer
assert exchange.connection_id == connection_id
assert exchange.credential_definition_id == cred_def_id
assert exchange.schema_id == schema_id
assert not exchange.credential_definition_id # leave open until offer
assert not exchange.schema_id # leave open until offer
assert exchange.thread_id == proposal._thread_id
assert exchange.role == exchange.ROLE_HOLDER
assert exchange.state == V10CredentialExchange.STATE_PROPOSAL_SENT
@@ -120,12 +121,6 @@ async def test_receive_proposal(self):
return_value=schema_id
)

with self.assertRaises(CredentialManagerError):
self.context.message = CredentialProposal(
credential_proposal=preview, cred_def_id=None, schema_id=None
)
await self.manager.receive_proposal()

with async_mock.patch.object(
V10CredentialExchange, "save", autospec=True
) as save_ex:
@@ -138,10 +133,10 @@ async def test_receive_proposal(self):
save_ex.assert_called_once()

assert exchange.connection_id == connection_id
assert exchange.credential_definition_id == cred_def_id
assert exchange.credential_definition_id == None
assert exchange.role == V10CredentialExchange.ROLE_ISSUER
assert exchange.state == V10CredentialExchange.STATE_PROPOSAL_RECEIVED
assert exchange.schema_id == schema_id
assert exchange.schema_id == None
assert exchange.thread_id == proposal._thread_id

ret_proposal: CredentialProposal = CredentialProposal.deserialize(
@@ -150,6 +145,11 @@ async def test_receive_proposal(self):
attrs = ret_proposal.credential_proposal.attributes
assert attrs == preview.attributes

self.context.message = CredentialProposal(
credential_proposal=preview, cred_def_id=None, schema_id=None
)
await self.manager.receive_proposal() # OK to leave open until offer

async def test_create_free_offer(self):
schema_id = "LjgpST2rjsoxYegQDRm7EL:2:bc-reg:1.0"
cred_def_id = "LjgpST2rjsoxYegQDRm7EL:3:CL:18:tag"
@@ -201,7 +201,6 @@ async def test_create_bound_offer(self):
)
proposal = CredentialProposal(credential_proposal=preview)
exchange = V10CredentialExchange(
credential_definition_id=cred_def_id,
credential_proposal_dict=proposal.serialize(),
role=V10CredentialExchange.ROLE_ISSUER,
)
@@ -212,14 +211,17 @@ async def test_create_bound_offer(self):
V10CredentialExchange, "get_cached_key", autospec=True
) as get_cached_key, async_mock.patch.object(
V10CredentialExchange, "set_cached_key", autospec=True
) as set_cached_key:
) as set_cached_key, async_mock.patch.object(
CredentialManager, "_match_sent_cred_def_id", autospec=True
) as match_cred_def_id:
get_cached_key.return_value = None
cred_offer = {"cred_def_id": cred_def_id, "schema_id": schema_id}
issuer = async_mock.MagicMock()
issuer.create_credential_offer = async_mock.CoroutineMock(
return_value=cred_offer
)
self.context.injector.bind_instance(BaseIssuer, issuer)
match_cred_def_id.return_value=cred_def_id

(ret_exchange, ret_offer) = await self.manager.create_offer(
credential_exchange_record=exchange, comment=comment
Original file line number Diff line number Diff line change
@@ -110,7 +110,7 @@ async def test_credential_exchange_send_proposal(self):

mock = async_mock.MagicMock()
mock.json = async_mock.CoroutineMock(
return_value={"connection_id": conn_id, "credential_preview": preview_spec}
return_value={"connection_id": conn_id, "credential_proposal": preview_spec}
)
mock.app = {
"outbound_message_router": async_mock.CoroutineMock(),
@@ -209,7 +209,7 @@ async def test_credential_exchange_send_free_offer(self):
mock.json = async_mock.CoroutineMock(
return_value={
"auto_issue": False,
"credential_definition_id": "cred-def-id",
"cred_def_id": "cred-def-id",
}
)

@@ -251,7 +251,7 @@ async def test_credential_exchange_send_free_offer_no_conn_record(self):
mock.json = async_mock.CoroutineMock(
return_value={
"auto_issue": False,
"credential_definition_id": "cred-def-id",
"cred_def_id": "cred-def-id",
}
)