Module mydata_did.patched_protocols.present_proof.v1_0.routes
Admin routes for presentations.
Expand source code
"""Admin routes for presentations."""
import json
from aiohttp import web
from aiohttp_apispec import (
docs,
match_info_schema,
querystring_schema,
request_schema,
response_schema,
)
from marshmallow import fields, validate, validates_schema
from marshmallow.exceptions import ValidationError
from aries_cloudagent.connections.models.connection_record import ConnectionRecord
from aries_cloudagent.holder.base import BaseHolder, HolderError
from aries_cloudagent.indy.util import generate_pr_nonce
from aries_cloudagent.ledger.error import LedgerError
from aries_cloudagent.messaging.decorators.attach_decorator import AttachDecorator
from aries_cloudagent.messaging.models.base import BaseModelError
from aries_cloudagent.messaging.models.openapi import OpenAPISchema
from aries_cloudagent.messaging.valid import (
INDY_CRED_DEF_ID,
INDY_DID,
INDY_EXTRA_WQL,
INDY_PREDICATE,
INDY_SCHEMA_ID,
INDY_VERSION,
INT_EPOCH,
NATURAL_NUM,
UUIDFour,
UUID4,
WHOLE_NUM,
)
from aries_cloudagent.storage.error import StorageError, StorageNotFoundError
from aries_cloudagent.utils.tracing import trace_event, get_timer, AdminAPIMessageTracingSchema
from aries_cloudagent.wallet.error import WalletNotFoundError
from aries_cloudagent.protocols.problem_report.v1_0 import internal_error
from dexa_sdk.managers.ada_manager import V2ADAManager
from dexa_sdk.agreements.da.v1_0.records.da_template_record import DataAgreementTemplateRecord
from dexa_sdk.utils import clean_and_get_field_from_dict, paginate_records
from .manager import PresentationManager
from .message_types import ATTACH_DECO_IDS, PRESENTATION_REQUEST, SPEC_URI
from .messages.inner.presentation_preview import (
PresentationPreview,
PresentationPreviewSchema,
)
from .messages.presentation_proposal import PresentationProposal
from .messages.presentation_request import PresentationRequest
from .models.presentation_exchange import (
V10PresentationExchange,
V10PresentationExchangeSchema,
)
from ....v1_0.decorators.data_agreement_context_decorator import DataAgreementContextDecorator
PAGINATION_PAGE_SIZE = 10
class V10PresentationExchangeListQueryStringSchema(OpenAPISchema):
"""Parameters and validators for presentation exchange list query."""
connection_id = fields.UUID(
description="Connection identifier",
required=False,
example=UUIDFour.EXAMPLE, # typically but not necessarily a UUID4
)
thread_id = fields.UUID(
description="Thread identifier",
required=False,
example=UUIDFour.EXAMPLE, # typically but not necessarily a UUID4
)
role = fields.Str(
description="Role assigned in presentation exchange",
required=False,
validate=validate.OneOf(
[
getattr(V10PresentationExchange, m)
for m in vars(V10PresentationExchange)
if m.startswith("ROLE_")
]
),
)
state = fields.Str(
description="Presentation exchange state",
required=False,
validate=validate.OneOf(
[
getattr(V10PresentationExchange, m)
for m in vars(V10PresentationExchange)
if m.startswith("STATE_")
]
),
)
# Template identifier
template_id = fields.Str(required=False)
# Page
page = fields.Int(required=False)
# Page size
page_size = fields.Int(required=False)
class V10PresentationExchangeListSchema(OpenAPISchema):
"""Result schema for an Aries RFC 37 v1.0 presentation exchange query."""
results = fields.List(
fields.Nested(V10PresentationExchangeSchema()),
description="Aries RFC 37 v1.0 presentation exchange records",
)
class V10PresentationProposalRequestSchema(AdminAPIMessageTracingSchema):
"""Request schema for sending a presentation proposal admin message."""
connection_id = fields.UUID(
description="Connection identifier", required=True, example=UUIDFour.EXAMPLE
)
comment = fields.Str(
description="Human-readable comment", required=False, allow_none=True
)
presentation_proposal = fields.Nested(
PresentationPreviewSchema(), required=True)
auto_present = fields.Boolean(
description=(
"Whether to respond automatically to presentation requests, building "
"and presenting requested proof"
),
required=False,
default=False,
)
trace = fields.Bool(
description="Whether to trace event (default false)",
required=False,
example=False,
)
class DAIndyProofReqPredSpecRestrictionsSchema(OpenAPISchema):
"""Schema for restrictions in attr or pred specifier indy proof request."""
schema_id = fields.String(
description="Schema identifier", required=False, **INDY_SCHEMA_ID
)
schema_issuer_did = fields.String(
description="Schema issuer (origin) DID", required=False, **INDY_DID
)
schema_name = fields.String(
example="transcript", description="Schema name", required=False
)
schema_version = fields.String(
description="Schema version", required=False, **INDY_VERSION
)
issuer_did = fields.String(
description="Credential issuer DID", required=False, **INDY_DID
)
cred_def_id = fields.String(
description="Credential definition identifier",
required=False,
**INDY_CRED_DEF_ID,
)
class DAIndyProofReqNonRevokedSchema(OpenAPISchema):
"""Non-revocation times specification in indy proof request."""
fro = fields.Int(
description="Earliest epoch of interest for non-revocation proof",
required=False,
data_key="from",
**INT_EPOCH,
)
to = fields.Int(
description="Latest epoch of interest for non-revocation proof",
required=False,
**INT_EPOCH,
)
@validates_schema
def validate_fields(self, data, **kwargs):
"""
Validate schema fields - must have from, to, or both.
Args:
data: The data to validate
Raises:
ValidationError: if data has neither from nor to
"""
if not (data.get("from") or data.get("to")):
raise ValidationError(
"Non-revocation interval must have at least one end", ("fro",
"to")
)
class DAIndyProofReqAttrSpecSchema(OpenAPISchema):
"""Schema for attribute specification in indy proof request."""
name = fields.String(
example="favouriteDrink", description="Attribute name", required=False
)
names = fields.List(
fields.String(example="age"),
description="Attribute name group",
required=False,
)
restrictions = fields.List(
fields.Dict(
keys=fields.Str(
validate=validate.Regexp(
"^schema_id|"
"schema_issuer_did|"
"schema_name|"
"schema_version|"
"issuer_did|"
"cred_def_id|"
"attr::.+::value$" # indy does not support attr::...::marker here
),
example="cred_def_id", # marshmallow/apispec v3.0 ignores
),
values=fields.Str(example=INDY_CRED_DEF_ID["example"]),
),
description=(
"If present, credential must satisfy one of given restrictions: specify "
"schema_id, schema_issuer_did, schema_name, schema_version, "
"issuer_did, cred_def_id, and/or attr::<attribute-name>::value "
"where <attribute-name> represents a credential attribute name"
),
required=False,
)
non_revoked = fields.Nested(
DAIndyProofReqNonRevokedSchema(), required=False)
@validates_schema
def validate_fields(self, data, **kwargs):
"""
Validate schema fields.
Data must have exactly one of name or names; if names then restrictions are
mandatory.
Args:
data: The data to validate
Raises:
ValidationError: if data has both or neither of name and names
"""
if ("name" in data) == ("names" in data):
raise ValidationError(
"Attribute specification must have either name or names but not both"
)
restrictions = data.get("restrictions")
if ("names" in data) and (not restrictions or all(not r for r in restrictions)):
raise ValidationError(
"Attribute specification on 'names' must have non-empty restrictions"
)
class DAIndyProofReqPredSpecSchema(OpenAPISchema):
"""Schema for predicate specification in indy proof request."""
name = fields.String(
example="index", description="Attribute name", required=True)
p_type = fields.String(
description="Predicate type ('<', '<=', '>=', or '>')",
required=True,
**INDY_PREDICATE,
)
p_value = fields.Integer(description="Threshold value", required=True)
restrictions = fields.List(
fields.Nested(DAIndyProofReqPredSpecRestrictionsSchema()),
description="If present, credential must satisfy one of given restrictions",
required=False,
)
non_revoked = fields.Nested(
DAIndyProofReqNonRevokedSchema(), required=False)
class DAIndyProofRequestSchema(OpenAPISchema):
"""Schema for indy proof request."""
nonce = fields.String(description="Nonce",
required=False, example="1234567890")
name = fields.String(
description="Proof request name",
required=False,
example="Proof request",
default="Proof request",
)
version = fields.String(
description="Proof request version",
required=False,
default="1.0",
**INDY_VERSION,
)
requested_attributes = fields.Dict(
description=("Requested attribute specifications of proof request"),
required=True,
# marshmallow/apispec v3.0 ignores
keys=fields.Str(example="0_attr_uuid"),
values=fields.Nested(DAIndyProofReqAttrSpecSchema()),
)
requested_predicates = fields.Dict(
description=("Requested predicate specifications of proof request"),
required=True,
# marshmallow/apispec v3.0 ignores
keys=fields.Str(example="0_age_GE_uuid"),
values=fields.Nested(DAIndyProofReqPredSpecSchema()),
)
non_revoked = fields.Nested(
DAIndyProofReqNonRevokedSchema(), required=False)
class V10PresentationCreateRequestRequestSchema(AdminAPIMessageTracingSchema):
"""Request schema for creating a proof request free of any connection."""
proof_request = fields.Nested(DAIndyProofRequestSchema(), required=True)
comment = fields.Str(required=False, allow_none=True)
trace = fields.Bool(
description="Whether to trace event (default false)",
required=False,
example=False,
)
class V10PresentationSendRequestRequestSchema(
V10PresentationCreateRequestRequestSchema
):
"""Request schema for sending a proof request on a connection."""
connection_id = fields.UUID(
description="Connection identifier", required=True, example=UUIDFour.EXAMPLE
)
class IndyRequestedCredsRequestedAttrSchema(OpenAPISchema):
"""Schema for requested attributes within indy requested credentials structure."""
cred_id = fields.Str(
example="3fa85f64-5717-4562-b3fc-2c963f66afa6",
description=(
"Wallet credential identifier (typically but not necessarily a UUID)"
),
required=True,
)
revealed = fields.Bool(
description="Whether to reveal attribute in proof", required=True
)
timestamp = fields.Int(
description="Epoch timestamp of interest for non-revocation proof",
required=False,
**INT_EPOCH,
)
class IndyRequestedCredsRequestedPredSchema(OpenAPISchema):
"""Schema for requested predicates within indy requested credentials structure."""
cred_id = fields.Str(
description=(
"Wallet credential identifier (typically but not necessarily a UUID)"
),
example="3fa85f64-5717-4562-b3fc-2c963f66afa6",
required=True,
)
timestamp = fields.Int(
description="Epoch timestamp of interest for non-revocation proof",
required=False,
**INT_EPOCH,
)
class V10PresentationRequestSchema(AdminAPIMessageTracingSchema):
"""Request schema for sending a presentation."""
self_attested_attributes = fields.Dict(
description=("Self-attested attributes to build into proof"),
required=True,
# marshmallow/apispec v3.0 ignores
keys=fields.Str(example="attr_name"),
values=fields.Str(
example="self_attested_value",
description=(
"Self-attested attribute values to use in requested-credentials "
"structure for proof construction"
),
),
)
requested_attributes = fields.Dict(
description=(
"Nested object mapping proof request attribute referents to "
"requested-attribute specifiers"
),
required=True,
# marshmallow/apispec v3.0 ignores
keys=fields.Str(example="attr_referent"),
values=fields.Nested(IndyRequestedCredsRequestedAttrSchema()),
)
requested_predicates = fields.Dict(
description=(
"Nested object mapping proof request predicate referents to "
"requested-predicate specifiers"
),
required=True,
# marshmallow/apispec v3.0 ignores
keys=fields.Str(example="pred_referent"),
values=fields.Nested(IndyRequestedCredsRequestedPredSchema()),
)
trace = fields.Bool(
description="Whether to trace event (default false)",
required=False,
example=False,
)
class CredentialsFetchQueryStringSchema(OpenAPISchema):
"""Parameters and validators for credentials fetch request query string."""
referent = fields.Str(
description="Proof request referents of interest, comma-separated",
required=False,
example="1_name_uuid,2_score_uuid",
)
start = fields.Int(description="Start index", required=False, **WHOLE_NUM)
count = fields.Int(
description="Maximum number to retrieve", required=False, **NATURAL_NUM
)
extra_query = fields.Str(
description="(JSON) object mapping referents to extra WQL queries",
required=False,
**INDY_EXTRA_WQL,
)
class PresExIdMatchInfoSchema(OpenAPISchema):
"""Path parameters and validators for request taking presentation exchange id."""
pres_ex_id = fields.Str(
description="Presentation exchange identifier", required=True, **UUID4
)
class SendPresentationRequestForDataAgreementRequestSchema(OpenAPISchema):
"""Request schema for sending a presentation request for a data agreement."""
# Connection identifier
connection_id = fields.UUID(required=True)
# Data agreement template identifier
template_id = fields.Str(required=True)
@docs(tags=["present-proof"], summary="Fetch all present-proof exchange records")
@querystring_schema(V10PresentationExchangeListQueryStringSchema)
@response_schema(V10PresentationExchangeListSchema(), 200)
async def presentation_exchange_list(request: web.BaseRequest):
"""
Request handler for searching presentation exchange records.
Args:
request: aiohttp request object
Returns:
The presentation exchange list response
"""
context = request.app["request_context"]
tag_filter = {}
if "thread_id" in request.query and request.query["thread_id"] != "":
tag_filter["thread_id"] = request.query["thread_id"]
post_filter = {
k: request.query[k]
for k in ("connection_id", "role", "state", "template_id")
if request.query.get(k, "") != ""
}
# Page query param
page = clean_and_get_field_from_dict(request.query, "page")
page = int(page) if page is not None else page
# Page size query param
page_size = clean_and_get_field_from_dict(request.query, "page_size")
page_size = int(page_size) if page_size is not None else page_size
try:
records = await V10PresentationExchange.query(context, tag_filter, post_filter)
# Pagination.
pagination_result = paginate_records(
records,
page if page else 1,
page_size if page_size else 10
)
except (StorageError, BaseModelError) as err:
raise web.HTTPBadRequest(reason=err.roll_up) from err
return web.json_response(pagination_result)
@docs(tags=["present-proof"], summary="Fetch a single presentation exchange record")
@match_info_schema(PresExIdMatchInfoSchema())
@response_schema(V10PresentationExchangeSchema(), 200)
async def presentation_exchange_retrieve(request: web.BaseRequest):
"""
Request handler for fetching a single presentation exchange record.
Args:
request: aiohttp request object
Returns:
The presentation exchange record response
"""
context = request.app["request_context"]
outbound_handler = request.app["outbound_message_router"]
presentation_exchange_id = request.match_info["pres_ex_id"]
pres_ex_record = None
try:
pres_ex_record = await V10PresentationExchange.retrieve_by_id(
context, presentation_exchange_id
)
result = pres_ex_record.serialize()
except StorageNotFoundError as err:
raise web.HTTPNotFound(reason=err.roll_up) from err
except (BaseModelError, StorageError) as err:
await internal_error(err, web.HTTPBadRequest, pres_ex_record, outbound_handler)
return web.json_response(result)
@docs(
tags=["present-proof"],
summary="Fetch credentials for a presentation request from wallet",
)
@match_info_schema(PresExIdMatchInfoSchema())
@querystring_schema(CredentialsFetchQueryStringSchema())
async def presentation_exchange_credentials_list(request: web.BaseRequest):
"""
Request handler for searching applicable credential records.
Args:
request: aiohttp request object
Returns:
The credential list response
"""
context = request.app["request_context"]
outbound_handler = request.app["outbound_message_router"]
presentation_exchange_id = request.match_info["pres_ex_id"]
referents = request.query.get("referent")
presentation_referents = (
(r.strip() for r in referents.split(",")) if referents else ()
)
try:
pres_ex_record = await V10PresentationExchange.retrieve_by_id(
context, presentation_exchange_id
)
except StorageNotFoundError as err:
raise web.HTTPNotFound(reason=err.roll_up) from err
start = request.query.get("start")
count = request.query.get("count")
# url encoded json extra_query
encoded_extra_query = request.query.get("extra_query") or "{}"
extra_query = json.loads(encoded_extra_query)
# defaults
start = int(start) if isinstance(start, str) else 0
count = int(count) if isinstance(count, str) else 10
holder: BaseHolder = await context.inject(BaseHolder)
try:
credentials = await holder.get_credentials_for_presentation_request_by_referent(
pres_ex_record.presentation_request,
presentation_referents,
start,
count,
extra_query,
)
except HolderError as err:
await internal_error(err, web.HTTPBadRequest, pres_ex_record, outbound_handler)
pres_ex_record.log_state(
context,
"Retrieved presentation credentials",
{
"presentation_exchange_id": presentation_exchange_id,
"referents": presentation_referents,
"extra_query": extra_query,
"credentials": credentials,
},
)
return web.json_response(credentials)
@docs(tags=["present-proof"], summary="Sends a presentation proposal")
@request_schema(V10PresentationProposalRequestSchema())
@response_schema(V10PresentationExchangeSchema(), 200)
async def presentation_exchange_send_proposal(request: web.BaseRequest):
"""
Request handler for sending a presentation proposal.
Args:
request: aiohttp request object
Returns:
The presentation exchange details
"""
r_time = get_timer()
context = request.app["request_context"]
outbound_handler = request.app["outbound_message_router"]
body = await request.json()
comment = body.get("comment")
connection_id = body.get("connection_id")
# Aries RFC 37 calls it a proposal in the proposal struct but it's of type preview
presentation_preview = body.get("presentation_proposal")
connection_record = None
try:
connection_record = await ConnectionRecord.retrieve_by_id(
context, connection_id
)
presentation_proposal_message = PresentationProposal(
comment=comment,
presentation_proposal=PresentationPreview.deserialize(
presentation_preview),
)
except (BaseModelError, StorageError) as err:
await internal_error(
err, web.HTTPBadRequest, connection_record, outbound_handler
)
if not connection_record.is_ready:
raise web.HTTPForbidden(reason=f"Connection {connection_id} not ready")
trace_msg = body.get("trace")
presentation_proposal_message.assign_trace_decorator(
context.settings,
trace_msg,
)
auto_present = body.get(
"auto_present", context.settings.get(
"debug.auto_respond_presentation_request")
)
presentation_manager = PresentationManager(context)
pres_ex_record = None
try:
pres_ex_record = await presentation_manager.create_exchange_for_proposal(
connection_id=connection_id,
presentation_proposal_message=presentation_proposal_message,
auto_present=auto_present,
)
result = pres_ex_record.serialize()
except (BaseModelError, StorageError) as err:
await internal_error(
err,
web.HTTPBadRequest,
pres_ex_record or connection_record,
outbound_handler,
)
await outbound_handler(presentation_proposal_message, connection_id=connection_id)
trace_event(
context.settings,
presentation_proposal_message,
outcome="presentation_exchange_propose.END",
perf_counter=r_time,
)
return web.json_response(result)
@docs(
tags=["present-proof"],
summary="""
Creates a presentation request not bound to any proposal or existing connection
""",
)
@request_schema(V10PresentationCreateRequestRequestSchema())
@response_schema(V10PresentationExchangeSchema(), 200)
async def presentation_exchange_create_request(request: web.BaseRequest):
"""
Request handler for creating a free presentation request.
The presentation request will not be bound to any proposal
or existing connection.
Args:
request: aiohttp request object
Returns:
The presentation exchange details
"""
r_time = get_timer()
context = request.app["request_context"]
outbound_handler = request.app["outbound_message_router"]
body = await request.json()
comment = body.get("comment")
indy_proof_request = body.get("proof_request")
if not indy_proof_request.get("nonce"):
indy_proof_request["nonce"] = await generate_pr_nonce()
presentation_request_message = PresentationRequest(
comment=comment,
request_presentations_attach=[
AttachDecorator.from_indy_dict(
indy_dict=indy_proof_request,
ident=ATTACH_DECO_IDS[PRESENTATION_REQUEST],
)
],
)
trace_msg = body.get("trace")
presentation_request_message.assign_trace_decorator(
context.settings,
trace_msg,
)
presentation_manager = PresentationManager(context)
pres_ex_record = None
try:
(pres_ex_record) = await presentation_manager.create_exchange_for_request(
connection_id=None,
presentation_request_message=presentation_request_message,
)
result = pres_ex_record.serialize()
except (BaseModelError, StorageError) as err:
await internal_error(err, web.HTTPBadRequest, pres_ex_record, outbound_handler)
await outbound_handler(presentation_request_message, connection_id=None)
trace_event(
context.settings,
presentation_request_message,
outcome="presentation_exchange_create_request.END",
perf_counter=r_time,
)
return web.json_response(result)
@docs(
tags=["present-proof"],
summary="Sends a free presentation request not bound to any proposal",
)
@request_schema(V10PresentationSendRequestRequestSchema())
@response_schema(V10PresentationExchangeSchema(), 200)
async def presentation_exchange_send_free_request(request: web.BaseRequest):
"""
Request handler for sending a presentation request free from any proposal.
Args:
request: aiohttp request object
Returns:
The presentation exchange details
"""
r_time = get_timer()
context = request.app["request_context"]
outbound_handler = request.app["outbound_message_router"]
body = await request.json()
connection_id = body.get("connection_id")
try:
connection_record = await ConnectionRecord.retrieve_by_id(
context, connection_id
)
except StorageNotFoundError as err:
raise web.HTTPBadRequest(reason=err.roll_up) from err
if not connection_record.is_ready:
raise web.HTTPForbidden(reason=f"Connection {connection_id} not ready")
comment = body.get("comment")
indy_proof_request = body.get("proof_request")
if not indy_proof_request.get("nonce"):
indy_proof_request["nonce"] = await generate_pr_nonce()
presentation_request_message = PresentationRequest(
comment=comment,
request_presentations_attach=[
AttachDecorator.from_indy_dict(
indy_dict=indy_proof_request,
ident=ATTACH_DECO_IDS[PRESENTATION_REQUEST],
)
],
)
trace_msg = body.get("trace")
presentation_request_message.assign_trace_decorator(
context.settings,
trace_msg,
)
presentation_manager = PresentationManager(context)
pres_ex_record = None
try:
(pres_ex_record) = await presentation_manager.create_exchange_for_request(
connection_id=connection_id,
presentation_request_message=presentation_request_message,
)
result = pres_ex_record.serialize()
except (BaseModelError, StorageError) as err:
await internal_error(
err,
web.HTTPBadRequest,
pres_ex_record or connection_record,
outbound_handler,
)
await outbound_handler(presentation_request_message, connection_id=connection_id)
trace_event(
context.settings,
presentation_request_message,
outcome="presentation_exchange_send_request.END",
perf_counter=r_time,
)
return web.json_response(result)
@docs(
tags=["present-proof"],
summary="Sends a presentation request in reference to a proposal",
)
@match_info_schema(PresExIdMatchInfoSchema())
@request_schema(V10PresentationSendRequestRequestSchema())
@response_schema(V10PresentationExchangeSchema(), 200)
async def presentation_exchange_send_bound_request(request: web.BaseRequest):
"""
Request handler for sending a presentation request free from any proposal.
Args:
request: aiohttp request object
Returns:
The presentation exchange details
"""
r_time = get_timer()
context = request.app["request_context"]
outbound_handler = request.app["outbound_message_router"]
presentation_exchange_id = request.match_info["pres_ex_id"]
pres_ex_record = await V10PresentationExchange.retrieve_by_id(
context, presentation_exchange_id
)
if pres_ex_record.state != (V10PresentationExchange.STATE_PROPOSAL_RECEIVED):
raise web.HTTPBadRequest(
reason=(
f"Presentation exchange {presentation_exchange_id} "
f"in {pres_ex_record.state} state "
f"(must be {V10PresentationExchange.STATE_PROPOSAL_RECEIVED})"
)
)
body = await request.json()
connection_id = body.get("connection_id")
try:
connection_record = await ConnectionRecord.retrieve_by_id(
context, connection_id
)
except StorageError as err:
raise web.HTTPBadRequest(reason=err.roll_up) from err
if not connection_record.is_ready:
raise web.HTTPForbidden(reason=f"Connection {connection_id} not ready")
presentation_manager = PresentationManager(context)
try:
(
pres_ex_record,
presentation_request_message,
) = await presentation_manager.create_bound_request(pres_ex_record)
result = pres_ex_record.serialize()
except (BaseModelError, StorageError) as err:
await internal_error(
err,
web.HTTPBadRequest,
pres_ex_record or connection_record,
outbound_handler,
)
trace_msg = body.get("trace")
presentation_request_message.assign_trace_decorator(
context.settings,
trace_msg,
)
await outbound_handler(presentation_request_message, connection_id=connection_id)
trace_event(
context.settings,
presentation_request_message,
outcome="presentation_exchange_send_request.END",
perf_counter=r_time,
)
return web.json_response(result)
@docs(tags=["present-proof"], summary="Sends a proof presentation")
@match_info_schema(PresExIdMatchInfoSchema())
@request_schema(V10PresentationRequestSchema())
@response_schema(V10PresentationExchangeSchema())
async def presentation_exchange_send_presentation(request: web.BaseRequest):
"""
Request handler for sending a presentation.
Args:
request: aiohttp request object
Returns:
The presentation exchange details
"""
r_time = get_timer()
context = request.app["request_context"]
outbound_handler = request.app["outbound_message_router"]
presentation_exchange_id = request.match_info["pres_ex_id"]
pres_ex_record = await V10PresentationExchange.retrieve_by_id(
context, presentation_exchange_id
)
if pres_ex_record.state != (V10PresentationExchange.STATE_REQUEST_RECEIVED):
raise web.HTTPBadRequest(
reason=(
f"Presentation exchange {presentation_exchange_id} "
f"in {pres_ex_record.state} state "
f"(must be {V10PresentationExchange.STATE_REQUEST_RECEIVED})"
)
)
body = await request.json()
connection_id = pres_ex_record.connection_id
try:
connection_record = await ConnectionRecord.retrieve_by_id(
context, connection_id
)
except StorageNotFoundError as err:
raise web.HTTPBadRequest(reason=err.roll_up) from err
if not connection_record.is_ready:
raise web.HTTPForbidden(reason=f"Connection {connection_id} not ready")
presentation_manager = PresentationManager(context)
try:
(
pres_ex_record,
presentation_message,
) = await presentation_manager.create_presentation(
pres_ex_record,
{
"self_attested_attributes": body.get("self_attested_attributes"),
"requested_attributes": body.get("requested_attributes"),
"requested_predicates": body.get("requested_predicates"),
},
comment=body.get("comment"),
)
# Initialize ADA manager
manager = V2ADAManager(context)
# Initialize ADA manager.
manager = V2ADAManager(context)
# Create data agreement accept message.
accept_message = await manager.build_data_agreement_accept_for_data_ex_record(
connection_record,
pres_ex_record
)
# Update presentation message with data agreement context decorator
presentation_message._decorators["data-agreement-context"] = \
DataAgreementContextDecorator(
message_type="protocol",
message=accept_message.serialize()
)
result = pres_ex_record.serialize()
except (
BaseModelError,
HolderError,
LedgerError,
StorageError,
WalletNotFoundError,
) as err:
await internal_error(
err,
web.HTTPBadRequest,
pres_ex_record or connection_record,
outbound_handler,
)
trace_msg = body.get("trace")
presentation_message.assign_trace_decorator(
context.settings,
trace_msg,
)
await outbound_handler(presentation_message, connection_id=connection_id)
trace_event(
context.settings,
presentation_message,
outcome="presentation_exchange_send_request.END",
perf_counter=r_time,
)
return web.json_response(result)
@docs(tags=["present-proof"], summary="Verify a received presentation")
@match_info_schema(PresExIdMatchInfoSchema())
@response_schema(V10PresentationExchangeSchema())
async def presentation_exchange_verify_presentation(request: web.BaseRequest):
"""
Request handler for verifying a presentation request.
Args:
request: aiohttp request object
Returns:
The presentation exchange details
"""
r_time = get_timer()
context = request.app["request_context"]
outbound_handler = request.app["outbound_message_router"]
presentation_exchange_id = request.match_info["pres_ex_id"]
pres_ex_record = await V10PresentationExchange.retrieve_by_id(
context, presentation_exchange_id
)
if pres_ex_record.state != (V10PresentationExchange.STATE_PRESENTATION_RECEIVED):
raise web.HTTPBadRequest(
reason=(
f"Presentation exchange {presentation_exchange_id} "
f"in {pres_ex_record.state} state "
f"(must be {V10PresentationExchange.STATE_PRESENTATION_RECEIVED})"
)
)
connection_id = pres_ex_record.connection_id
try:
connection_record = await ConnectionRecord.retrieve_by_id(
context, connection_id
)
except StorageError as err:
raise web.HTTPBadRequest(reason=err.roll_up) from err
if not connection_record.is_ready:
raise web.HTTPForbidden(reason=f"Connection {connection_id} not ready")
presentation_manager = PresentationManager(context)
try:
pres_ex_record = await presentation_manager.verify_presentation(pres_ex_record)
result = pres_ex_record.serialize()
except (LedgerError, BaseModelError) as err:
await internal_error(err, web.HTTPBadRequest, pres_ex_record, outbound_handler)
trace_event(
context.settings,
pres_ex_record,
outcome="presentation_exchange_verify.END",
perf_counter=r_time,
)
return web.json_response(result)
@docs(tags=["present-proof"], summary="Remove an existing presentation exchange record")
@match_info_schema(PresExIdMatchInfoSchema())
async def presentation_exchange_remove(request: web.BaseRequest):
"""
Request handler for removing a presentation exchange record.
Args:
request: aiohttp request object
"""
context = request.app["request_context"]
outbound_handler = request.app["outbound_message_router"]
presentation_exchange_id = request.match_info["pres_ex_id"]
pres_ex_record = None
try:
pres_ex_record: V10PresentationExchange = await V10PresentationExchange.retrieve_by_id(
context, presentation_exchange_id
)
await pres_ex_record.delete_record(context)
# Initialize ADA manager
manager = V2ADAManager(context)
# Delete data agreement instance
await manager.delete_da_instance_by_data_ex_id(pres_ex_record.presentation_exchange_id)
except StorageNotFoundError as err:
await internal_error(err, web.HTTPNotFound, pres_ex_record, outbound_handler)
except StorageError as err:
await internal_error(err, web.HTTPBadRequest, pres_ex_record, outbound_handler)
return web.json_response({})
@docs(tags=["present-proof"],
summary="Send a presentation request in reference to a data agreement")
@request_schema(SendPresentationRequestForDataAgreementRequestSchema())
async def send_presentation_request_for_data_agreement(request: web.BaseRequest):
"""
Sent a presentation request in reference to a data agreement.
Args:
request: aiohttp request object
"""
# Retrieve context
context = request.app["request_context"]
# Outbound message handler
outbound_handler = request.app["outbound_message_router"]
# Request payload
body = await request.json()
# Data agreement template identifier
template_id = body.get("template_id")
# Connection identifier
connection_id = body.get("connection_id")
# Fetch the connection record
connection_record: ConnectionRecord = await ConnectionRecord.retrieve_by_id(
context,
connection_id
)
if not connection_record.is_ready:
raise web.HTTPForbidden(reason=f"Connection {connection_id} not ready")
# Initialise ADA manager
manager = V2ADAManager(context)
# Fetch data agreement template
template: DataAgreementTemplateRecord = \
await DataAgreementTemplateRecord.latest_published_template_by_id(
context, template_id
)
if not template:
raise web.HTTPException(reason="Data agreement template not found.")
if template.method_of_use != DataAgreementTemplateRecord.METHOD_OF_USE_DATA_USING_SERVICE:
raise web.HTTPException(reason="Data agreement method of use must be data-using-service.")
# Construct presentation request
preset_presentation_request = template.presentation_request
comment = preset_presentation_request.pop("comment")
if not preset_presentation_request.get("nonce"):
preset_presentation_request["nonce"] = await generate_pr_nonce()
presentation_request = PresentationRequest(
comment=comment,
request_presentations_attach=[
AttachDecorator.from_indy_dict(
indy_dict=preset_presentation_request,
ident=ATTACH_DECO_IDS[PRESENTATION_REQUEST],
)
],
)
# Construct presentation exchange record
presentation_manager = PresentationManager(context)
pres_ex_record = None
try:
(pres_ex_record) = await presentation_manager.create_exchange_for_request(
connection_id=connection_id,
presentation_request_message=presentation_request,
)
pres_ex_record.template_id = template_id
await pres_ex_record.save(context)
result = pres_ex_record.serialize()
except (BaseModelError, StorageError) as err:
await internal_error(
err,
web.HTTPBadRequest,
pres_ex_record or connection_record,
outbound_handler,
)
# Build data agreement offer message
da_offer_message = await manager.build_data_agreement_offer_for_presentation_exchange(
template_id,
connection_record,
pres_ex_record
)
# Add data agreement context decorator
presentation_request._decorators["data-agreement-context"] = \
DataAgreementContextDecorator(
message_type="protocol",
message=da_offer_message.serialize()
)
pres_ex_record.presentation_request_dict = presentation_request.serialize()
await pres_ex_record.save(context)
result = pres_ex_record.serialize()
await outbound_handler(presentation_request, connection_id=connection_id)
return web.json_response(result)
async def register(app: web.Application):
"""Register routes."""
app.add_routes(
[
web.get(
"/present-proof/records", presentation_exchange_list, allow_head=False
),
web.get(
"/present-proof/records/{pres_ex_id}",
presentation_exchange_retrieve,
allow_head=False,
),
web.get(
"/present-proof/records/{pres_ex_id}/credentials",
presentation_exchange_credentials_list,
allow_head=False,
),
web.post(
"/present-proof/send-proposal",
presentation_exchange_send_proposal,
),
web.post(
"/present-proof/create-request",
presentation_exchange_create_request,
),
web.post(
"/present-proof/send-request",
presentation_exchange_send_free_request,
),
web.post(
"/present-proof/records/{pres_ex_id}/send-request",
presentation_exchange_send_bound_request,
),
web.post(
"/present-proof/records/{pres_ex_id}/send-presentation",
presentation_exchange_send_presentation,
),
web.post(
"/present-proof/records/{pres_ex_id}/verify-presentation",
presentation_exchange_verify_presentation,
),
web.delete(
"/present-proof/records/{pres_ex_id}",
presentation_exchange_remove,
),
web.post(
"/present-proof/data-agreement-negotiation/offer",
send_presentation_request_for_data_agreement,
)
]
)
def post_process_routes(app: web.Application):
"""Amend swagger API."""
# Add top-level tags description
if "tags" not in app._state["swagger_dict"]:
app._state["swagger_dict"]["tags"] = []
app._state["swagger_dict"]["tags"].append(
{
"name": "present-proof",
"description": "Proof presentation",
"externalDocs": {"description": "Specification", "url": SPEC_URI},
}
)
Functions
def post_process_routes(app: aiohttp.web_app.Application)
-
Amend swagger API.
Expand source code
def post_process_routes(app: web.Application): """Amend swagger API.""" # Add top-level tags description if "tags" not in app._state["swagger_dict"]: app._state["swagger_dict"]["tags"] = [] app._state["swagger_dict"]["tags"].append( { "name": "present-proof", "description": "Proof presentation", "externalDocs": {"description": "Specification", "url": SPEC_URI}, } )
async def presentation_exchange_create_request(request: aiohttp.web_request.BaseRequest)
-
Request handler for creating a free presentation request.
The presentation request will not be bound to any proposal or existing connection.
Args
request
- aiohttp request object
Returns
The presentation exchange details
Expand source code
@docs( tags=["present-proof"], summary=""" Creates a presentation request not bound to any proposal or existing connection """, ) @request_schema(V10PresentationCreateRequestRequestSchema()) @response_schema(V10PresentationExchangeSchema(), 200) async def presentation_exchange_create_request(request: web.BaseRequest): """ Request handler for creating a free presentation request. The presentation request will not be bound to any proposal or existing connection. Args: request: aiohttp request object Returns: The presentation exchange details """ r_time = get_timer() context = request.app["request_context"] outbound_handler = request.app["outbound_message_router"] body = await request.json() comment = body.get("comment") indy_proof_request = body.get("proof_request") if not indy_proof_request.get("nonce"): indy_proof_request["nonce"] = await generate_pr_nonce() presentation_request_message = PresentationRequest( comment=comment, request_presentations_attach=[ AttachDecorator.from_indy_dict( indy_dict=indy_proof_request, ident=ATTACH_DECO_IDS[PRESENTATION_REQUEST], ) ], ) trace_msg = body.get("trace") presentation_request_message.assign_trace_decorator( context.settings, trace_msg, ) presentation_manager = PresentationManager(context) pres_ex_record = None try: (pres_ex_record) = await presentation_manager.create_exchange_for_request( connection_id=None, presentation_request_message=presentation_request_message, ) result = pres_ex_record.serialize() except (BaseModelError, StorageError) as err: await internal_error(err, web.HTTPBadRequest, pres_ex_record, outbound_handler) await outbound_handler(presentation_request_message, connection_id=None) trace_event( context.settings, presentation_request_message, outcome="presentation_exchange_create_request.END", perf_counter=r_time, ) return web.json_response(result)
async def presentation_exchange_credentials_list(request: aiohttp.web_request.BaseRequest)
-
Request handler for searching applicable credential records.
Args
request
- aiohttp request object
Returns
The credential list response
Expand source code
@docs( tags=["present-proof"], summary="Fetch credentials for a presentation request from wallet", ) @match_info_schema(PresExIdMatchInfoSchema()) @querystring_schema(CredentialsFetchQueryStringSchema()) async def presentation_exchange_credentials_list(request: web.BaseRequest): """ Request handler for searching applicable credential records. Args: request: aiohttp request object Returns: The credential list response """ context = request.app["request_context"] outbound_handler = request.app["outbound_message_router"] presentation_exchange_id = request.match_info["pres_ex_id"] referents = request.query.get("referent") presentation_referents = ( (r.strip() for r in referents.split(",")) if referents else () ) try: pres_ex_record = await V10PresentationExchange.retrieve_by_id( context, presentation_exchange_id ) except StorageNotFoundError as err: raise web.HTTPNotFound(reason=err.roll_up) from err start = request.query.get("start") count = request.query.get("count") # url encoded json extra_query encoded_extra_query = request.query.get("extra_query") or "{}" extra_query = json.loads(encoded_extra_query) # defaults start = int(start) if isinstance(start, str) else 0 count = int(count) if isinstance(count, str) else 10 holder: BaseHolder = await context.inject(BaseHolder) try: credentials = await holder.get_credentials_for_presentation_request_by_referent( pres_ex_record.presentation_request, presentation_referents, start, count, extra_query, ) except HolderError as err: await internal_error(err, web.HTTPBadRequest, pres_ex_record, outbound_handler) pres_ex_record.log_state( context, "Retrieved presentation credentials", { "presentation_exchange_id": presentation_exchange_id, "referents": presentation_referents, "extra_query": extra_query, "credentials": credentials, }, ) return web.json_response(credentials)
async def presentation_exchange_list(request: aiohttp.web_request.BaseRequest)
-
Request handler for searching presentation exchange records.
Args
request
- aiohttp request object
Returns
The presentation exchange list response
Expand source code
@docs(tags=["present-proof"], summary="Fetch all present-proof exchange records") @querystring_schema(V10PresentationExchangeListQueryStringSchema) @response_schema(V10PresentationExchangeListSchema(), 200) async def presentation_exchange_list(request: web.BaseRequest): """ Request handler for searching presentation exchange records. Args: request: aiohttp request object Returns: The presentation exchange list response """ context = request.app["request_context"] tag_filter = {} if "thread_id" in request.query and request.query["thread_id"] != "": tag_filter["thread_id"] = request.query["thread_id"] post_filter = { k: request.query[k] for k in ("connection_id", "role", "state", "template_id") if request.query.get(k, "") != "" } # Page query param page = clean_and_get_field_from_dict(request.query, "page") page = int(page) if page is not None else page # Page size query param page_size = clean_and_get_field_from_dict(request.query, "page_size") page_size = int(page_size) if page_size is not None else page_size try: records = await V10PresentationExchange.query(context, tag_filter, post_filter) # Pagination. pagination_result = paginate_records( records, page if page else 1, page_size if page_size else 10 ) except (StorageError, BaseModelError) as err: raise web.HTTPBadRequest(reason=err.roll_up) from err return web.json_response(pagination_result)
async def presentation_exchange_remove(request: aiohttp.web_request.BaseRequest)
-
Request handler for removing a presentation exchange record.
Args
request
- aiohttp request object
Expand source code
@docs(tags=["present-proof"], summary="Remove an existing presentation exchange record") @match_info_schema(PresExIdMatchInfoSchema()) async def presentation_exchange_remove(request: web.BaseRequest): """ Request handler for removing a presentation exchange record. Args: request: aiohttp request object """ context = request.app["request_context"] outbound_handler = request.app["outbound_message_router"] presentation_exchange_id = request.match_info["pres_ex_id"] pres_ex_record = None try: pres_ex_record: V10PresentationExchange = await V10PresentationExchange.retrieve_by_id( context, presentation_exchange_id ) await pres_ex_record.delete_record(context) # Initialize ADA manager manager = V2ADAManager(context) # Delete data agreement instance await manager.delete_da_instance_by_data_ex_id(pres_ex_record.presentation_exchange_id) except StorageNotFoundError as err: await internal_error(err, web.HTTPNotFound, pres_ex_record, outbound_handler) except StorageError as err: await internal_error(err, web.HTTPBadRequest, pres_ex_record, outbound_handler) return web.json_response({})
async def presentation_exchange_retrieve(request: aiohttp.web_request.BaseRequest)
-
Request handler for fetching a single presentation exchange record.
Args
request
- aiohttp request object
Returns
The presentation exchange record response
Expand source code
@docs(tags=["present-proof"], summary="Fetch a single presentation exchange record") @match_info_schema(PresExIdMatchInfoSchema()) @response_schema(V10PresentationExchangeSchema(), 200) async def presentation_exchange_retrieve(request: web.BaseRequest): """ Request handler for fetching a single presentation exchange record. Args: request: aiohttp request object Returns: The presentation exchange record response """ context = request.app["request_context"] outbound_handler = request.app["outbound_message_router"] presentation_exchange_id = request.match_info["pres_ex_id"] pres_ex_record = None try: pres_ex_record = await V10PresentationExchange.retrieve_by_id( context, presentation_exchange_id ) result = pres_ex_record.serialize() except StorageNotFoundError as err: raise web.HTTPNotFound(reason=err.roll_up) from err except (BaseModelError, StorageError) as err: await internal_error(err, web.HTTPBadRequest, pres_ex_record, outbound_handler) return web.json_response(result)
async def presentation_exchange_send_bound_request(request: aiohttp.web_request.BaseRequest)
-
Request handler for sending a presentation request free from any proposal.
Args
request
- aiohttp request object
Returns
The presentation exchange details
Expand source code
@docs( tags=["present-proof"], summary="Sends a presentation request in reference to a proposal", ) @match_info_schema(PresExIdMatchInfoSchema()) @request_schema(V10PresentationSendRequestRequestSchema()) @response_schema(V10PresentationExchangeSchema(), 200) async def presentation_exchange_send_bound_request(request: web.BaseRequest): """ Request handler for sending a presentation request free from any proposal. Args: request: aiohttp request object Returns: The presentation exchange details """ r_time = get_timer() context = request.app["request_context"] outbound_handler = request.app["outbound_message_router"] presentation_exchange_id = request.match_info["pres_ex_id"] pres_ex_record = await V10PresentationExchange.retrieve_by_id( context, presentation_exchange_id ) if pres_ex_record.state != (V10PresentationExchange.STATE_PROPOSAL_RECEIVED): raise web.HTTPBadRequest( reason=( f"Presentation exchange {presentation_exchange_id} " f"in {pres_ex_record.state} state " f"(must be {V10PresentationExchange.STATE_PROPOSAL_RECEIVED})" ) ) body = await request.json() connection_id = body.get("connection_id") try: connection_record = await ConnectionRecord.retrieve_by_id( context, connection_id ) except StorageError as err: raise web.HTTPBadRequest(reason=err.roll_up) from err if not connection_record.is_ready: raise web.HTTPForbidden(reason=f"Connection {connection_id} not ready") presentation_manager = PresentationManager(context) try: ( pres_ex_record, presentation_request_message, ) = await presentation_manager.create_bound_request(pres_ex_record) result = pres_ex_record.serialize() except (BaseModelError, StorageError) as err: await internal_error( err, web.HTTPBadRequest, pres_ex_record or connection_record, outbound_handler, ) trace_msg = body.get("trace") presentation_request_message.assign_trace_decorator( context.settings, trace_msg, ) await outbound_handler(presentation_request_message, connection_id=connection_id) trace_event( context.settings, presentation_request_message, outcome="presentation_exchange_send_request.END", perf_counter=r_time, ) return web.json_response(result)
async def presentation_exchange_send_free_request(request: aiohttp.web_request.BaseRequest)
-
Request handler for sending a presentation request free from any proposal.
Args
request
- aiohttp request object
Returns
The presentation exchange details
Expand source code
@docs( tags=["present-proof"], summary="Sends a free presentation request not bound to any proposal", ) @request_schema(V10PresentationSendRequestRequestSchema()) @response_schema(V10PresentationExchangeSchema(), 200) async def presentation_exchange_send_free_request(request: web.BaseRequest): """ Request handler for sending a presentation request free from any proposal. Args: request: aiohttp request object Returns: The presentation exchange details """ r_time = get_timer() context = request.app["request_context"] outbound_handler = request.app["outbound_message_router"] body = await request.json() connection_id = body.get("connection_id") try: connection_record = await ConnectionRecord.retrieve_by_id( context, connection_id ) except StorageNotFoundError as err: raise web.HTTPBadRequest(reason=err.roll_up) from err if not connection_record.is_ready: raise web.HTTPForbidden(reason=f"Connection {connection_id} not ready") comment = body.get("comment") indy_proof_request = body.get("proof_request") if not indy_proof_request.get("nonce"): indy_proof_request["nonce"] = await generate_pr_nonce() presentation_request_message = PresentationRequest( comment=comment, request_presentations_attach=[ AttachDecorator.from_indy_dict( indy_dict=indy_proof_request, ident=ATTACH_DECO_IDS[PRESENTATION_REQUEST], ) ], ) trace_msg = body.get("trace") presentation_request_message.assign_trace_decorator( context.settings, trace_msg, ) presentation_manager = PresentationManager(context) pres_ex_record = None try: (pres_ex_record) = await presentation_manager.create_exchange_for_request( connection_id=connection_id, presentation_request_message=presentation_request_message, ) result = pres_ex_record.serialize() except (BaseModelError, StorageError) as err: await internal_error( err, web.HTTPBadRequest, pres_ex_record or connection_record, outbound_handler, ) await outbound_handler(presentation_request_message, connection_id=connection_id) trace_event( context.settings, presentation_request_message, outcome="presentation_exchange_send_request.END", perf_counter=r_time, ) return web.json_response(result)
async def presentation_exchange_send_presentation(request: aiohttp.web_request.BaseRequest)
-
Request handler for sending a presentation.
Args
request
- aiohttp request object
Returns
The presentation exchange details
Expand source code
@docs(tags=["present-proof"], summary="Sends a proof presentation") @match_info_schema(PresExIdMatchInfoSchema()) @request_schema(V10PresentationRequestSchema()) @response_schema(V10PresentationExchangeSchema()) async def presentation_exchange_send_presentation(request: web.BaseRequest): """ Request handler for sending a presentation. Args: request: aiohttp request object Returns: The presentation exchange details """ r_time = get_timer() context = request.app["request_context"] outbound_handler = request.app["outbound_message_router"] presentation_exchange_id = request.match_info["pres_ex_id"] pres_ex_record = await V10PresentationExchange.retrieve_by_id( context, presentation_exchange_id ) if pres_ex_record.state != (V10PresentationExchange.STATE_REQUEST_RECEIVED): raise web.HTTPBadRequest( reason=( f"Presentation exchange {presentation_exchange_id} " f"in {pres_ex_record.state} state " f"(must be {V10PresentationExchange.STATE_REQUEST_RECEIVED})" ) ) body = await request.json() connection_id = pres_ex_record.connection_id try: connection_record = await ConnectionRecord.retrieve_by_id( context, connection_id ) except StorageNotFoundError as err: raise web.HTTPBadRequest(reason=err.roll_up) from err if not connection_record.is_ready: raise web.HTTPForbidden(reason=f"Connection {connection_id} not ready") presentation_manager = PresentationManager(context) try: ( pres_ex_record, presentation_message, ) = await presentation_manager.create_presentation( pres_ex_record, { "self_attested_attributes": body.get("self_attested_attributes"), "requested_attributes": body.get("requested_attributes"), "requested_predicates": body.get("requested_predicates"), }, comment=body.get("comment"), ) # Initialize ADA manager manager = V2ADAManager(context) # Initialize ADA manager. manager = V2ADAManager(context) # Create data agreement accept message. accept_message = await manager.build_data_agreement_accept_for_data_ex_record( connection_record, pres_ex_record ) # Update presentation message with data agreement context decorator presentation_message._decorators["data-agreement-context"] = \ DataAgreementContextDecorator( message_type="protocol", message=accept_message.serialize() ) result = pres_ex_record.serialize() except ( BaseModelError, HolderError, LedgerError, StorageError, WalletNotFoundError, ) as err: await internal_error( err, web.HTTPBadRequest, pres_ex_record or connection_record, outbound_handler, ) trace_msg = body.get("trace") presentation_message.assign_trace_decorator( context.settings, trace_msg, ) await outbound_handler(presentation_message, connection_id=connection_id) trace_event( context.settings, presentation_message, outcome="presentation_exchange_send_request.END", perf_counter=r_time, ) return web.json_response(result)
async def presentation_exchange_send_proposal(request: aiohttp.web_request.BaseRequest)
-
Request handler for sending a presentation proposal.
Args
request
- aiohttp request object
Returns
The presentation exchange details
Expand source code
@docs(tags=["present-proof"], summary="Sends a presentation proposal") @request_schema(V10PresentationProposalRequestSchema()) @response_schema(V10PresentationExchangeSchema(), 200) async def presentation_exchange_send_proposal(request: web.BaseRequest): """ Request handler for sending a presentation proposal. Args: request: aiohttp request object Returns: The presentation exchange details """ r_time = get_timer() context = request.app["request_context"] outbound_handler = request.app["outbound_message_router"] body = await request.json() comment = body.get("comment") connection_id = body.get("connection_id") # Aries RFC 37 calls it a proposal in the proposal struct but it's of type preview presentation_preview = body.get("presentation_proposal") connection_record = None try: connection_record = await ConnectionRecord.retrieve_by_id( context, connection_id ) presentation_proposal_message = PresentationProposal( comment=comment, presentation_proposal=PresentationPreview.deserialize( presentation_preview), ) except (BaseModelError, StorageError) as err: await internal_error( err, web.HTTPBadRequest, connection_record, outbound_handler ) if not connection_record.is_ready: raise web.HTTPForbidden(reason=f"Connection {connection_id} not ready") trace_msg = body.get("trace") presentation_proposal_message.assign_trace_decorator( context.settings, trace_msg, ) auto_present = body.get( "auto_present", context.settings.get( "debug.auto_respond_presentation_request") ) presentation_manager = PresentationManager(context) pres_ex_record = None try: pres_ex_record = await presentation_manager.create_exchange_for_proposal( connection_id=connection_id, presentation_proposal_message=presentation_proposal_message, auto_present=auto_present, ) result = pres_ex_record.serialize() except (BaseModelError, StorageError) as err: await internal_error( err, web.HTTPBadRequest, pres_ex_record or connection_record, outbound_handler, ) await outbound_handler(presentation_proposal_message, connection_id=connection_id) trace_event( context.settings, presentation_proposal_message, outcome="presentation_exchange_propose.END", perf_counter=r_time, ) return web.json_response(result)
async def presentation_exchange_verify_presentation(request: aiohttp.web_request.BaseRequest)
-
Request handler for verifying a presentation request.
Args
request
- aiohttp request object
Returns
The presentation exchange details
Expand source code
@docs(tags=["present-proof"], summary="Verify a received presentation") @match_info_schema(PresExIdMatchInfoSchema()) @response_schema(V10PresentationExchangeSchema()) async def presentation_exchange_verify_presentation(request: web.BaseRequest): """ Request handler for verifying a presentation request. Args: request: aiohttp request object Returns: The presentation exchange details """ r_time = get_timer() context = request.app["request_context"] outbound_handler = request.app["outbound_message_router"] presentation_exchange_id = request.match_info["pres_ex_id"] pres_ex_record = await V10PresentationExchange.retrieve_by_id( context, presentation_exchange_id ) if pres_ex_record.state != (V10PresentationExchange.STATE_PRESENTATION_RECEIVED): raise web.HTTPBadRequest( reason=( f"Presentation exchange {presentation_exchange_id} " f"in {pres_ex_record.state} state " f"(must be {V10PresentationExchange.STATE_PRESENTATION_RECEIVED})" ) ) connection_id = pres_ex_record.connection_id try: connection_record = await ConnectionRecord.retrieve_by_id( context, connection_id ) except StorageError as err: raise web.HTTPBadRequest(reason=err.roll_up) from err if not connection_record.is_ready: raise web.HTTPForbidden(reason=f"Connection {connection_id} not ready") presentation_manager = PresentationManager(context) try: pres_ex_record = await presentation_manager.verify_presentation(pres_ex_record) result = pres_ex_record.serialize() except (LedgerError, BaseModelError) as err: await internal_error(err, web.HTTPBadRequest, pres_ex_record, outbound_handler) trace_event( context.settings, pres_ex_record, outcome="presentation_exchange_verify.END", perf_counter=r_time, ) return web.json_response(result)
async def register(app: aiohttp.web_app.Application)
-
Register routes.
Expand source code
async def register(app: web.Application): """Register routes.""" app.add_routes( [ web.get( "/present-proof/records", presentation_exchange_list, allow_head=False ), web.get( "/present-proof/records/{pres_ex_id}", presentation_exchange_retrieve, allow_head=False, ), web.get( "/present-proof/records/{pres_ex_id}/credentials", presentation_exchange_credentials_list, allow_head=False, ), web.post( "/present-proof/send-proposal", presentation_exchange_send_proposal, ), web.post( "/present-proof/create-request", presentation_exchange_create_request, ), web.post( "/present-proof/send-request", presentation_exchange_send_free_request, ), web.post( "/present-proof/records/{pres_ex_id}/send-request", presentation_exchange_send_bound_request, ), web.post( "/present-proof/records/{pres_ex_id}/send-presentation", presentation_exchange_send_presentation, ), web.post( "/present-proof/records/{pres_ex_id}/verify-presentation", presentation_exchange_verify_presentation, ), web.delete( "/present-proof/records/{pres_ex_id}", presentation_exchange_remove, ), web.post( "/present-proof/data-agreement-negotiation/offer", send_presentation_request_for_data_agreement, ) ] )
async def send_presentation_request_for_data_agreement(request: aiohttp.web_request.BaseRequest)
-
Sent a presentation request in reference to a data agreement.
Args
request
- aiohttp request object
Expand source code
@docs(tags=["present-proof"], summary="Send a presentation request in reference to a data agreement") @request_schema(SendPresentationRequestForDataAgreementRequestSchema()) async def send_presentation_request_for_data_agreement(request: web.BaseRequest): """ Sent a presentation request in reference to a data agreement. Args: request: aiohttp request object """ # Retrieve context context = request.app["request_context"] # Outbound message handler outbound_handler = request.app["outbound_message_router"] # Request payload body = await request.json() # Data agreement template identifier template_id = body.get("template_id") # Connection identifier connection_id = body.get("connection_id") # Fetch the connection record connection_record: ConnectionRecord = await ConnectionRecord.retrieve_by_id( context, connection_id ) if not connection_record.is_ready: raise web.HTTPForbidden(reason=f"Connection {connection_id} not ready") # Initialise ADA manager manager = V2ADAManager(context) # Fetch data agreement template template: DataAgreementTemplateRecord = \ await DataAgreementTemplateRecord.latest_published_template_by_id( context, template_id ) if not template: raise web.HTTPException(reason="Data agreement template not found.") if template.method_of_use != DataAgreementTemplateRecord.METHOD_OF_USE_DATA_USING_SERVICE: raise web.HTTPException(reason="Data agreement method of use must be data-using-service.") # Construct presentation request preset_presentation_request = template.presentation_request comment = preset_presentation_request.pop("comment") if not preset_presentation_request.get("nonce"): preset_presentation_request["nonce"] = await generate_pr_nonce() presentation_request = PresentationRequest( comment=comment, request_presentations_attach=[ AttachDecorator.from_indy_dict( indy_dict=preset_presentation_request, ident=ATTACH_DECO_IDS[PRESENTATION_REQUEST], ) ], ) # Construct presentation exchange record presentation_manager = PresentationManager(context) pres_ex_record = None try: (pres_ex_record) = await presentation_manager.create_exchange_for_request( connection_id=connection_id, presentation_request_message=presentation_request, ) pres_ex_record.template_id = template_id await pres_ex_record.save(context) result = pres_ex_record.serialize() except (BaseModelError, StorageError) as err: await internal_error( err, web.HTTPBadRequest, pres_ex_record or connection_record, outbound_handler, ) # Build data agreement offer message da_offer_message = await manager.build_data_agreement_offer_for_presentation_exchange( template_id, connection_record, pres_ex_record ) # Add data agreement context decorator presentation_request._decorators["data-agreement-context"] = \ DataAgreementContextDecorator( message_type="protocol", message=da_offer_message.serialize() ) pres_ex_record.presentation_request_dict = presentation_request.serialize() await pres_ex_record.save(context) result = pres_ex_record.serialize() await outbound_handler(presentation_request, connection_id=connection_id) return web.json_response(result)
Classes
class CredentialsFetchQueryStringSchema (*, only: Union[Sequence[str], Set[str]] = None, exclude: Union[Sequence[str], Set[str]] = (), many: bool = False, context: Dict[~KT, ~VT] = None, load_only: Union[Sequence[str], Set[str]] = (), dump_only: Union[Sequence[str], Set[str]] = (), partial: Union[bool, Sequence[str], Set[str]] = False, unknown: str = None)
-
Parameters and validators for credentials fetch request query string.
Expand source code
class CredentialsFetchQueryStringSchema(OpenAPISchema): """Parameters and validators for credentials fetch request query string.""" referent = fields.Str( description="Proof request referents of interest, comma-separated", required=False, example="1_name_uuid,2_score_uuid", ) start = fields.Int(description="Start index", required=False, **WHOLE_NUM) count = fields.Int( description="Maximum number to retrieve", required=False, **NATURAL_NUM ) extra_query = fields.Str( description="(JSON) object mapping referents to extra WQL queries", required=False, **INDY_EXTRA_WQL, )
Ancestors
- aries_cloudagent.messaging.models.openapi.OpenAPISchema
- marshmallow.schema.Schema
- marshmallow.base.SchemaABC
Class variables
var opts
class DAIndyProofReqAttrSpecSchema (*, only: Union[Sequence[str], Set[str]] = None, exclude: Union[Sequence[str], Set[str]] = (), many: bool = False, context: Dict[~KT, ~VT] = None, load_only: Union[Sequence[str], Set[str]] = (), dump_only: Union[Sequence[str], Set[str]] = (), partial: Union[bool, Sequence[str], Set[str]] = False, unknown: str = None)
-
Schema for attribute specification in indy proof request.
Expand source code
class DAIndyProofReqAttrSpecSchema(OpenAPISchema): """Schema for attribute specification in indy proof request.""" name = fields.String( example="favouriteDrink", description="Attribute name", required=False ) names = fields.List( fields.String(example="age"), description="Attribute name group", required=False, ) restrictions = fields.List( fields.Dict( keys=fields.Str( validate=validate.Regexp( "^schema_id|" "schema_issuer_did|" "schema_name|" "schema_version|" "issuer_did|" "cred_def_id|" "attr::.+::value$" # indy does not support attr::...::marker here ), example="cred_def_id", # marshmallow/apispec v3.0 ignores ), values=fields.Str(example=INDY_CRED_DEF_ID["example"]), ), description=( "If present, credential must satisfy one of given restrictions: specify " "schema_id, schema_issuer_did, schema_name, schema_version, " "issuer_did, cred_def_id, and/or attr::<attribute-name>::value " "where <attribute-name> represents a credential attribute name" ), required=False, ) non_revoked = fields.Nested( DAIndyProofReqNonRevokedSchema(), required=False) @validates_schema def validate_fields(self, data, **kwargs): """ Validate schema fields. Data must have exactly one of name or names; if names then restrictions are mandatory. Args: data: The data to validate Raises: ValidationError: if data has both or neither of name and names """ if ("name" in data) == ("names" in data): raise ValidationError( "Attribute specification must have either name or names but not both" ) restrictions = data.get("restrictions") if ("names" in data) and (not restrictions or all(not r for r in restrictions)): raise ValidationError( "Attribute specification on 'names' must have non-empty restrictions" )
Ancestors
- aries_cloudagent.messaging.models.openapi.OpenAPISchema
- marshmallow.schema.Schema
- marshmallow.base.SchemaABC
Class variables
var opts
Methods
def validate_fields(self, data, **kwargs)
-
Validate schema fields.
Data must have exactly one of name or names; if names then restrictions are mandatory.
Args
data
- The data to validate
Raises
ValidationError
- if data has both or neither of name and names
Expand source code
@validates_schema def validate_fields(self, data, **kwargs): """ Validate schema fields. Data must have exactly one of name or names; if names then restrictions are mandatory. Args: data: The data to validate Raises: ValidationError: if data has both or neither of name and names """ if ("name" in data) == ("names" in data): raise ValidationError( "Attribute specification must have either name or names but not both" ) restrictions = data.get("restrictions") if ("names" in data) and (not restrictions or all(not r for r in restrictions)): raise ValidationError( "Attribute specification on 'names' must have non-empty restrictions" )
class DAIndyProofReqNonRevokedSchema (*, only: Union[Sequence[str], Set[str]] = None, exclude: Union[Sequence[str], Set[str]] = (), many: bool = False, context: Dict[~KT, ~VT] = None, load_only: Union[Sequence[str], Set[str]] = (), dump_only: Union[Sequence[str], Set[str]] = (), partial: Union[bool, Sequence[str], Set[str]] = False, unknown: str = None)
-
Non-revocation times specification in indy proof request.
Expand source code
class DAIndyProofReqNonRevokedSchema(OpenAPISchema): """Non-revocation times specification in indy proof request.""" fro = fields.Int( description="Earliest epoch of interest for non-revocation proof", required=False, data_key="from", **INT_EPOCH, ) to = fields.Int( description="Latest epoch of interest for non-revocation proof", required=False, **INT_EPOCH, ) @validates_schema def validate_fields(self, data, **kwargs): """ Validate schema fields - must have from, to, or both. Args: data: The data to validate Raises: ValidationError: if data has neither from nor to """ if not (data.get("from") or data.get("to")): raise ValidationError( "Non-revocation interval must have at least one end", ("fro", "to") )
Ancestors
- aries_cloudagent.messaging.models.openapi.OpenAPISchema
- marshmallow.schema.Schema
- marshmallow.base.SchemaABC
Class variables
var opts
Methods
def validate_fields(self, data, **kwargs)
-
Validate schema fields - must have from, to, or both.
Args
data
- The data to validate
Raises
ValidationError
- if data has neither from nor to
Expand source code
@validates_schema def validate_fields(self, data, **kwargs): """ Validate schema fields - must have from, to, or both. Args: data: The data to validate Raises: ValidationError: if data has neither from nor to """ if not (data.get("from") or data.get("to")): raise ValidationError( "Non-revocation interval must have at least one end", ("fro", "to") )
class DAIndyProofReqPredSpecRestrictionsSchema (*, only: Union[Sequence[str], Set[str]] = None, exclude: Union[Sequence[str], Set[str]] = (), many: bool = False, context: Dict[~KT, ~VT] = None, load_only: Union[Sequence[str], Set[str]] = (), dump_only: Union[Sequence[str], Set[str]] = (), partial: Union[bool, Sequence[str], Set[str]] = False, unknown: str = None)
-
Schema for restrictions in attr or pred specifier indy proof request.
Expand source code
class DAIndyProofReqPredSpecRestrictionsSchema(OpenAPISchema): """Schema for restrictions in attr or pred specifier indy proof request.""" schema_id = fields.String( description="Schema identifier", required=False, **INDY_SCHEMA_ID ) schema_issuer_did = fields.String( description="Schema issuer (origin) DID", required=False, **INDY_DID ) schema_name = fields.String( example="transcript", description="Schema name", required=False ) schema_version = fields.String( description="Schema version", required=False, **INDY_VERSION ) issuer_did = fields.String( description="Credential issuer DID", required=False, **INDY_DID ) cred_def_id = fields.String( description="Credential definition identifier", required=False, **INDY_CRED_DEF_ID, )
Ancestors
- aries_cloudagent.messaging.models.openapi.OpenAPISchema
- marshmallow.schema.Schema
- marshmallow.base.SchemaABC
Class variables
var opts
class DAIndyProofReqPredSpecSchema (*, only: Union[Sequence[str], Set[str]] = None, exclude: Union[Sequence[str], Set[str]] = (), many: bool = False, context: Dict[~KT, ~VT] = None, load_only: Union[Sequence[str], Set[str]] = (), dump_only: Union[Sequence[str], Set[str]] = (), partial: Union[bool, Sequence[str], Set[str]] = False, unknown: str = None)
-
Schema for predicate specification in indy proof request.
Expand source code
class DAIndyProofReqPredSpecSchema(OpenAPISchema): """Schema for predicate specification in indy proof request.""" name = fields.String( example="index", description="Attribute name", required=True) p_type = fields.String( description="Predicate type ('<', '<=', '>=', or '>')", required=True, **INDY_PREDICATE, ) p_value = fields.Integer(description="Threshold value", required=True) restrictions = fields.List( fields.Nested(DAIndyProofReqPredSpecRestrictionsSchema()), description="If present, credential must satisfy one of given restrictions", required=False, ) non_revoked = fields.Nested( DAIndyProofReqNonRevokedSchema(), required=False)
Ancestors
- aries_cloudagent.messaging.models.openapi.OpenAPISchema
- marshmallow.schema.Schema
- marshmallow.base.SchemaABC
Class variables
var opts
class DAIndyProofRequestSchema (*, only: Union[Sequence[str], Set[str]] = None, exclude: Union[Sequence[str], Set[str]] = (), many: bool = False, context: Dict[~KT, ~VT] = None, load_only: Union[Sequence[str], Set[str]] = (), dump_only: Union[Sequence[str], Set[str]] = (), partial: Union[bool, Sequence[str], Set[str]] = False, unknown: str = None)
-
Schema for indy proof request.
Expand source code
class DAIndyProofRequestSchema(OpenAPISchema): """Schema for indy proof request.""" nonce = fields.String(description="Nonce", required=False, example="1234567890") name = fields.String( description="Proof request name", required=False, example="Proof request", default="Proof request", ) version = fields.String( description="Proof request version", required=False, default="1.0", **INDY_VERSION, ) requested_attributes = fields.Dict( description=("Requested attribute specifications of proof request"), required=True, # marshmallow/apispec v3.0 ignores keys=fields.Str(example="0_attr_uuid"), values=fields.Nested(DAIndyProofReqAttrSpecSchema()), ) requested_predicates = fields.Dict( description=("Requested predicate specifications of proof request"), required=True, # marshmallow/apispec v3.0 ignores keys=fields.Str(example="0_age_GE_uuid"), values=fields.Nested(DAIndyProofReqPredSpecSchema()), ) non_revoked = fields.Nested( DAIndyProofReqNonRevokedSchema(), required=False)
Ancestors
- aries_cloudagent.messaging.models.openapi.OpenAPISchema
- marshmallow.schema.Schema
- marshmallow.base.SchemaABC
Class variables
var opts
class IndyRequestedCredsRequestedAttrSchema (*, only: Union[Sequence[str], Set[str]] = None, exclude: Union[Sequence[str], Set[str]] = (), many: bool = False, context: Dict[~KT, ~VT] = None, load_only: Union[Sequence[str], Set[str]] = (), dump_only: Union[Sequence[str], Set[str]] = (), partial: Union[bool, Sequence[str], Set[str]] = False, unknown: str = None)
-
Schema for requested attributes within indy requested credentials structure.
Expand source code
class IndyRequestedCredsRequestedAttrSchema(OpenAPISchema): """Schema for requested attributes within indy requested credentials structure.""" cred_id = fields.Str( example="3fa85f64-5717-4562-b3fc-2c963f66afa6", description=( "Wallet credential identifier (typically but not necessarily a UUID)" ), required=True, ) revealed = fields.Bool( description="Whether to reveal attribute in proof", required=True ) timestamp = fields.Int( description="Epoch timestamp of interest for non-revocation proof", required=False, **INT_EPOCH, )
Ancestors
- aries_cloudagent.messaging.models.openapi.OpenAPISchema
- marshmallow.schema.Schema
- marshmallow.base.SchemaABC
Class variables
var opts
class IndyRequestedCredsRequestedPredSchema (*, only: Union[Sequence[str], Set[str]] = None, exclude: Union[Sequence[str], Set[str]] = (), many: bool = False, context: Dict[~KT, ~VT] = None, load_only: Union[Sequence[str], Set[str]] = (), dump_only: Union[Sequence[str], Set[str]] = (), partial: Union[bool, Sequence[str], Set[str]] = False, unknown: str = None)
-
Schema for requested predicates within indy requested credentials structure.
Expand source code
class IndyRequestedCredsRequestedPredSchema(OpenAPISchema): """Schema for requested predicates within indy requested credentials structure.""" cred_id = fields.Str( description=( "Wallet credential identifier (typically but not necessarily a UUID)" ), example="3fa85f64-5717-4562-b3fc-2c963f66afa6", required=True, ) timestamp = fields.Int( description="Epoch timestamp of interest for non-revocation proof", required=False, **INT_EPOCH, )
Ancestors
- aries_cloudagent.messaging.models.openapi.OpenAPISchema
- marshmallow.schema.Schema
- marshmallow.base.SchemaABC
Class variables
var opts
class PresExIdMatchInfoSchema (*, only: Union[Sequence[str], Set[str]] = None, exclude: Union[Sequence[str], Set[str]] = (), many: bool = False, context: Dict[~KT, ~VT] = None, load_only: Union[Sequence[str], Set[str]] = (), dump_only: Union[Sequence[str], Set[str]] = (), partial: Union[bool, Sequence[str], Set[str]] = False, unknown: str = None)
-
Path parameters and validators for request taking presentation exchange id.
Expand source code
class PresExIdMatchInfoSchema(OpenAPISchema): """Path parameters and validators for request taking presentation exchange id.""" pres_ex_id = fields.Str( description="Presentation exchange identifier", required=True, **UUID4 )
Ancestors
- aries_cloudagent.messaging.models.openapi.OpenAPISchema
- marshmallow.schema.Schema
- marshmallow.base.SchemaABC
Class variables
var opts
class SendPresentationRequestForDataAgreementRequestSchema (*, only: Union[Sequence[str], Set[str]] = None, exclude: Union[Sequence[str], Set[str]] = (), many: bool = False, context: Dict[~KT, ~VT] = None, load_only: Union[Sequence[str], Set[str]] = (), dump_only: Union[Sequence[str], Set[str]] = (), partial: Union[bool, Sequence[str], Set[str]] = False, unknown: str = None)
-
Request schema for sending a presentation request for a data agreement.
Expand source code
class SendPresentationRequestForDataAgreementRequestSchema(OpenAPISchema): """Request schema for sending a presentation request for a data agreement.""" # Connection identifier connection_id = fields.UUID(required=True) # Data agreement template identifier template_id = fields.Str(required=True)
Ancestors
- aries_cloudagent.messaging.models.openapi.OpenAPISchema
- marshmallow.schema.Schema
- marshmallow.base.SchemaABC
Class variables
var opts
class V10PresentationCreateRequestRequestSchema (*, only: Union[Sequence[str], Set[str]] = None, exclude: Union[Sequence[str], Set[str]] = (), many: bool = False, context: Dict[~KT, ~VT] = None, load_only: Union[Sequence[str], Set[str]] = (), dump_only: Union[Sequence[str], Set[str]] = (), partial: Union[bool, Sequence[str], Set[str]] = False, unknown: str = None)
-
Request schema for creating a proof request free of any connection.
Expand source code
class V10PresentationCreateRequestRequestSchema(AdminAPIMessageTracingSchema): """Request schema for creating a proof request free of any connection.""" proof_request = fields.Nested(DAIndyProofRequestSchema(), required=True) comment = fields.Str(required=False, allow_none=True) trace = fields.Bool( description="Whether to trace event (default false)", required=False, example=False, )
Ancestors
- aries_cloudagent.utils.tracing.AdminAPIMessageTracingSchema
- aries_cloudagent.messaging.models.openapi.OpenAPISchema
- marshmallow.schema.Schema
- marshmallow.base.SchemaABC
Subclasses
Class variables
var opts
class V10PresentationExchangeListQueryStringSchema (*, only: Union[Sequence[str], Set[str]] = None, exclude: Union[Sequence[str], Set[str]] = (), many: bool = False, context: Dict[~KT, ~VT] = None, load_only: Union[Sequence[str], Set[str]] = (), dump_only: Union[Sequence[str], Set[str]] = (), partial: Union[bool, Sequence[str], Set[str]] = False, unknown: str = None)
-
Parameters and validators for presentation exchange list query.
Expand source code
class V10PresentationExchangeListQueryStringSchema(OpenAPISchema): """Parameters and validators for presentation exchange list query.""" connection_id = fields.UUID( description="Connection identifier", required=False, example=UUIDFour.EXAMPLE, # typically but not necessarily a UUID4 ) thread_id = fields.UUID( description="Thread identifier", required=False, example=UUIDFour.EXAMPLE, # typically but not necessarily a UUID4 ) role = fields.Str( description="Role assigned in presentation exchange", required=False, validate=validate.OneOf( [ getattr(V10PresentationExchange, m) for m in vars(V10PresentationExchange) if m.startswith("ROLE_") ] ), ) state = fields.Str( description="Presentation exchange state", required=False, validate=validate.OneOf( [ getattr(V10PresentationExchange, m) for m in vars(V10PresentationExchange) if m.startswith("STATE_") ] ), ) # Template identifier template_id = fields.Str(required=False) # Page page = fields.Int(required=False) # Page size page_size = fields.Int(required=False)
Ancestors
- aries_cloudagent.messaging.models.openapi.OpenAPISchema
- marshmallow.schema.Schema
- marshmallow.base.SchemaABC
Class variables
var opts
class V10PresentationExchangeListSchema (*, only: Union[Sequence[str], Set[str]] = None, exclude: Union[Sequence[str], Set[str]] = (), many: bool = False, context: Dict[~KT, ~VT] = None, load_only: Union[Sequence[str], Set[str]] = (), dump_only: Union[Sequence[str], Set[str]] = (), partial: Union[bool, Sequence[str], Set[str]] = False, unknown: str = None)
-
Result schema for an Aries RFC 37 v1.0 presentation exchange query.
Expand source code
class V10PresentationExchangeListSchema(OpenAPISchema): """Result schema for an Aries RFC 37 v1.0 presentation exchange query.""" results = fields.List( fields.Nested(V10PresentationExchangeSchema()), description="Aries RFC 37 v1.0 presentation exchange records", )
Ancestors
- aries_cloudagent.messaging.models.openapi.OpenAPISchema
- marshmallow.schema.Schema
- marshmallow.base.SchemaABC
Class variables
var opts
class V10PresentationProposalRequestSchema (*, only: Union[Sequence[str], Set[str]] = None, exclude: Union[Sequence[str], Set[str]] = (), many: bool = False, context: Dict[~KT, ~VT] = None, load_only: Union[Sequence[str], Set[str]] = (), dump_only: Union[Sequence[str], Set[str]] = (), partial: Union[bool, Sequence[str], Set[str]] = False, unknown: str = None)
-
Request schema for sending a presentation proposal admin message.
Expand source code
class V10PresentationProposalRequestSchema(AdminAPIMessageTracingSchema): """Request schema for sending a presentation proposal admin message.""" connection_id = fields.UUID( description="Connection identifier", required=True, example=UUIDFour.EXAMPLE ) comment = fields.Str( description="Human-readable comment", required=False, allow_none=True ) presentation_proposal = fields.Nested( PresentationPreviewSchema(), required=True) auto_present = fields.Boolean( description=( "Whether to respond automatically to presentation requests, building " "and presenting requested proof" ), required=False, default=False, ) trace = fields.Bool( description="Whether to trace event (default false)", required=False, example=False, )
Ancestors
- aries_cloudagent.utils.tracing.AdminAPIMessageTracingSchema
- aries_cloudagent.messaging.models.openapi.OpenAPISchema
- marshmallow.schema.Schema
- marshmallow.base.SchemaABC
Class variables
var opts
class V10PresentationRequestSchema (*, only: Union[Sequence[str], Set[str]] = None, exclude: Union[Sequence[str], Set[str]] = (), many: bool = False, context: Dict[~KT, ~VT] = None, load_only: Union[Sequence[str], Set[str]] = (), dump_only: Union[Sequence[str], Set[str]] = (), partial: Union[bool, Sequence[str], Set[str]] = False, unknown: str = None)
-
Request schema for sending a presentation.
Expand source code
class V10PresentationRequestSchema(AdminAPIMessageTracingSchema): """Request schema for sending a presentation.""" self_attested_attributes = fields.Dict( description=("Self-attested attributes to build into proof"), required=True, # marshmallow/apispec v3.0 ignores keys=fields.Str(example="attr_name"), values=fields.Str( example="self_attested_value", description=( "Self-attested attribute values to use in requested-credentials " "structure for proof construction" ), ), ) requested_attributes = fields.Dict( description=( "Nested object mapping proof request attribute referents to " "requested-attribute specifiers" ), required=True, # marshmallow/apispec v3.0 ignores keys=fields.Str(example="attr_referent"), values=fields.Nested(IndyRequestedCredsRequestedAttrSchema()), ) requested_predicates = fields.Dict( description=( "Nested object mapping proof request predicate referents to " "requested-predicate specifiers" ), required=True, # marshmallow/apispec v3.0 ignores keys=fields.Str(example="pred_referent"), values=fields.Nested(IndyRequestedCredsRequestedPredSchema()), ) trace = fields.Bool( description="Whether to trace event (default false)", required=False, example=False, )
Ancestors
- aries_cloudagent.utils.tracing.AdminAPIMessageTracingSchema
- aries_cloudagent.messaging.models.openapi.OpenAPISchema
- marshmallow.schema.Schema
- marshmallow.base.SchemaABC
Class variables
var opts
class V10PresentationSendRequestRequestSchema (*, only: Union[Sequence[str], Set[str]] = None, exclude: Union[Sequence[str], Set[str]] = (), many: bool = False, context: Dict[~KT, ~VT] = None, load_only: Union[Sequence[str], Set[str]] = (), dump_only: Union[Sequence[str], Set[str]] = (), partial: Union[bool, Sequence[str], Set[str]] = False, unknown: str = None)
-
Request schema for sending a proof request on a connection.
Expand source code
class V10PresentationSendRequestRequestSchema( V10PresentationCreateRequestRequestSchema ): """Request schema for sending a proof request on a connection.""" connection_id = fields.UUID( description="Connection identifier", required=True, example=UUIDFour.EXAMPLE )
Ancestors
- V10PresentationCreateRequestRequestSchema
- aries_cloudagent.utils.tracing.AdminAPIMessageTracingSchema
- aries_cloudagent.messaging.models.openapi.OpenAPISchema
- marshmallow.schema.Schema
- marshmallow.base.SchemaABC
Class variables
var opts