forked from hyperledger/aries-cloudagent-python
-
Notifications
You must be signed in to change notification settings - Fork 0
/
routes.py
309 lines (247 loc) · 9.95 KB
/
routes.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
"""Credential definition admin routes."""
from asyncio import ensure_future, shield
from aiohttp import web
from aiohttp_apispec import (
docs,
match_info_schema,
querystring_schema,
request_schema,
response_schema,
)
from marshmallow import fields
from ...issuer.base import BaseIssuer
from ...ledger.base import BaseLedger
from ...storage.base import BaseStorage
from ...tails.base import BaseTailsServer
from ..models.openapi import OpenAPISchema
from ..valid import INDY_CRED_DEF_ID, INDY_SCHEMA_ID, INDY_VERSION
from ...revocation.error import RevocationError, RevocationNotSupportedError
from ...revocation.indy import IndyRevocation
from ...ledger.error import LedgerError
from .util import CredDefQueryStringSchema, CRED_DEF_TAGS, CRED_DEF_SENT_RECORD_TYPE
class CredentialDefinitionSendRequestSchema(OpenAPISchema):
"""Request schema for schema send request."""
schema_id = fields.Str(description="Schema identifier", **INDY_SCHEMA_ID)
support_revocation = fields.Boolean(
required=False, description="Revocation supported flag"
)
revocation_registry_size = fields.Int(required=False)
tag = fields.Str(
required=False,
description="Credential definition identifier tag",
default="default",
example="default",
)
class CredentialDefinitionSendResultsSchema(OpenAPISchema):
"""Results schema for schema send request."""
credential_definition_id = fields.Str(
description="Credential definition identifier", **INDY_CRED_DEF_ID
)
class CredentialDefinitionSchema(OpenAPISchema):
"""Credential definition schema."""
ver = fields.Str(description="Node protocol version", **INDY_VERSION)
ident = fields.Str(
description="Credential definition identifier",
data_key="id",
**INDY_CRED_DEF_ID,
)
schemaId = fields.Str(
description="Schema identifier within credential definition identifier",
example=":".join(INDY_CRED_DEF_ID["example"].split(":")[3:-1]), # long or short
)
typ = fields.Constant(
constant="CL",
description="Signature type: CL for Camenisch-Lysyanskaya",
data_key="type",
example="CL",
)
tag = fields.Str(
description="Tag within credential definition identifier",
example=INDY_CRED_DEF_ID["example"].split(":")[-1],
)
value = fields.Dict(
description="Credential definition primary and revocation values"
)
class CredentialDefinitionGetResultsSchema(OpenAPISchema):
"""Results schema for schema get request."""
credential_definition = fields.Nested(CredentialDefinitionSchema)
class CredentialDefinitionsCreatedResultsSchema(OpenAPISchema):
"""Results schema for cred-defs-created request."""
credential_definition_ids = fields.List(
fields.Str(description="Credential definition identifiers", **INDY_CRED_DEF_ID)
)
class CredDefIdMatchInfoSchema(OpenAPISchema):
"""Path parameters and validators for request taking cred def id."""
cred_def_id = fields.Str(
description="Credential definition identifier",
required=True,
**INDY_CRED_DEF_ID,
)
@docs(
tags=["credential-definition"],
summary="Sends a credential definition to the ledger",
)
@request_schema(CredentialDefinitionSendRequestSchema())
@response_schema(CredentialDefinitionSendResultsSchema(), 200)
async def credential_definitions_send_credential_definition(request: web.BaseRequest):
"""
Request handler for sending a credential definition to the ledger.
Args:
request: aiohttp request object
Returns:
The credential definition identifier
"""
context = request.app["request_context"]
body = await request.json()
schema_id = body.get("schema_id")
support_revocation = bool(body.get("support_revocation"))
tag = body.get("tag")
revocation_registry_size = body.get("revocation_registry_size")
ledger: BaseLedger = await context.inject(BaseLedger, required=False)
if not ledger:
reason = "No ledger available"
if not context.settings.get_value("wallet.type"):
reason += ": missing wallet-type?"
raise web.HTTPForbidden(reason=reason)
issuer: BaseIssuer = await context.inject(BaseIssuer)
try: # even if in wallet, send it and raise if erroneously so
async with ledger:
(cred_def_id, cred_def, novel) = await shield(
ledger.create_and_send_credential_definition(
issuer,
schema_id,
signature_type=None,
tag=tag,
support_revocation=support_revocation,
)
)
except LedgerError as e:
raise web.HTTPBadRequest(reason=e.message) from e
# If revocation is requested and cred def is novel, create revocation registry
if support_revocation and novel:
tails_base_url = context.settings.get("tails_server_base_url")
if not tails_base_url:
raise web.HTTPBadRequest(reason="tails_server_base_url not configured")
try:
# Create registry
issuer_did = cred_def_id.split(":")[0]
revoc = IndyRevocation(context)
registry_record = await revoc.init_issuer_registry(
cred_def_id, issuer_did, max_cred_num=revocation_registry_size,
)
except RevocationNotSupportedError as e:
raise web.HTTPBadRequest(reason=e.message) from e
await shield(registry_record.generate_registry(context))
try:
await registry_record.set_tails_file_public_uri(
context, f"{tails_base_url}/{registry_record.revoc_reg_id}"
)
await registry_record.publish_registry_definition(context)
await registry_record.publish_registry_entry(context)
tails_server: BaseTailsServer = await context.inject(BaseTailsServer)
upload_success, reason = await tails_server.upload_tails_file(
context, registry_record.revoc_reg_id, registry_record.tails_local_path
)
if not upload_success:
raise web.HTTPInternalServerError(
reason=f"Tails file failed to upload: {reason}"
)
pending_registry_record = await revoc.init_issuer_registry(
registry_record.cred_def_id,
registry_record.issuer_did,
max_cred_num=registry_record.max_cred_num,
)
ensure_future(
pending_registry_record.stage_pending_registry_definition(context)
)
except RevocationError as e:
raise web.HTTPBadRequest(reason=e.message) from e
return web.json_response({"credential_definition_id": cred_def_id})
@docs(
tags=["credential-definition"],
summary="Search for matching credential definitions that agent originated",
)
@querystring_schema(CredDefQueryStringSchema())
@response_schema(CredentialDefinitionsCreatedResultsSchema(), 200)
async def credential_definitions_created(request: web.BaseRequest):
"""
Request handler for retrieving credential definitions that current agent created.
Args:
request: aiohttp request object
Returns:
The identifiers of matching credential definitions.
"""
context = request.app["request_context"]
storage = await context.inject(BaseStorage)
found = await storage.search_records(
type_filter=CRED_DEF_SENT_RECORD_TYPE,
tag_query={
tag: request.query[tag] for tag in CRED_DEF_TAGS if tag in request.query
},
).fetch_all()
return web.json_response(
{"credential_definition_ids": [record.value for record in found]}
)
@docs(
tags=["credential-definition"],
summary="Gets a credential definition from the ledger",
)
@match_info_schema(CredDefIdMatchInfoSchema())
@response_schema(CredentialDefinitionGetResultsSchema(), 200)
async def credential_definitions_get_credential_definition(request: web.BaseRequest):
"""
Request handler for getting a credential definition from the ledger.
Args:
request: aiohttp request object
Returns:
The credential definition details.
"""
context = request.app["request_context"]
cred_def_id = request.match_info["cred_def_id"]
ledger: BaseLedger = await context.inject(BaseLedger, required=False)
if not ledger:
reason = "No ledger available"
if not context.settings.get_value("wallet.type"):
reason += ": missing wallet-type?"
raise web.HTTPForbidden(reason=reason)
async with ledger:
cred_def = await ledger.get_credential_definition(cred_def_id)
return web.json_response({"credential_definition": cred_def})
async def register(app: web.Application):
"""Register routes."""
app.add_routes(
[
web.post(
"/credential-definitions",
credential_definitions_send_credential_definition,
),
web.get(
"/credential-definitions/created",
credential_definitions_created,
allow_head=False,
),
web.get(
"/credential-definitions/{cred_def_id}",
credential_definitions_get_credential_definition,
allow_head=False,
),
]
)
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": "credential-definition",
"description": "Credential definition operations",
"externalDocs": {
"description": "Specification",
"url": (
"https://github.com/hyperledger/indy-node/blob/master/"
"design/anoncreds.md#cred_def"
),
},
}
)