@bedrock/vc-delivery 4.2.0 → 4.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/index.js CHANGED
@@ -1,28 +1,34 @@
1
1
  /*!
2
- * Copyright (c) 2022-2023 Digital Bazaar, Inc. All rights reserved.
2
+ * Copyright (c) 2022-2024 Digital Bazaar, Inc. All rights reserved.
3
3
  */
4
4
  import * as bedrock from '@bedrock/core';
5
- import * as exchangerSchemas from '../schemas/bedrock-vc-exchanger.js';
5
+ import * as workflowSchemas from '../schemas/bedrock-vc-workflow.js';
6
6
  import {createService, schemas} from '@bedrock/service-core';
7
7
  import {addRoutes} from './http.js';
8
8
  import {initializeServiceAgent} from '@bedrock/service-agent';
9
9
  import {klona} from 'klona';
10
+ import {parseLocalId} from './helpers.js';
10
11
  import '@bedrock/express';
11
12
 
12
13
  // load config defaults
13
14
  import './config.js';
14
15
 
15
- const serviceType = 'vc-exchanger';
16
16
  const {util: {BedrockError}} = bedrock;
17
17
 
18
18
  bedrock.events.on('bedrock.init', async () => {
19
+ await _initService({serviceType: 'vc-workflow', routePrefix: '/workflows'});
20
+ // backwards compatibility: deprecrated `exchangers` service
21
+ await _initService({serviceType: 'vc-exchanger', routePrefix: '/exchangers'});
22
+ });
23
+
24
+ async function _initService({serviceType, routePrefix}) {
19
25
  // add customizations to config validators...
20
26
  const createConfigBody = klona(schemas.createConfigBody);
21
27
  const updateConfigBody = klona(schemas.updateConfigBody);
22
28
  const schemasToUpdate = [createConfigBody, updateConfigBody];
23
- const {credentialTemplates, steps, initialStep} = exchangerSchemas;
29
+ const {credentialTemplates, steps, initialStep} = workflowSchemas;
24
30
  for(const schema of schemasToUpdate) {
25
- // add config requirements to exchanger configs
31
+ // add config requirements to workflow configs
26
32
  schema.properties.credentialTemplates = credentialTemplates;
27
33
  schema.properties.steps = steps;
28
34
  schema.properties.initialStep = initialStep;
@@ -31,10 +37,14 @@ bedrock.events.on('bedrock.init', async () => {
31
37
  // schema.required.push('credentialTemplates');
32
38
  }
33
39
 
34
- // create `vc-exchanger` service
40
+ // allow `id` property in `createConfigBody`, to be more rigorously validated
41
+ // below in `validateConfigFn`
42
+ createConfigBody.properties.id = updateConfigBody.properties.id;
43
+
44
+ // create workflow service
35
45
  const service = await createService({
36
46
  serviceType,
37
- routePrefix: '/exchangers',
47
+ routePrefix,
38
48
  storageCost: {
39
49
  config: 1,
40
50
  revocation: 1
@@ -42,7 +52,9 @@ bedrock.events.on('bedrock.init', async () => {
42
52
  validation: {
43
53
  createConfigBody,
44
54
  updateConfigBody,
45
- validateConfigFn,
55
+ async validateConfigFn({config, op} = {}) {
56
+ return validateConfigFn({config, op, routePrefix});
57
+ },
46
58
  // these zcaps are optional (by reference ID)
47
59
  zcapReferenceIds: [{
48
60
  referenceId: 'issue',
@@ -67,7 +79,7 @@ bedrock.events.on('bedrock.init', async () => {
67
79
  await addRoutes({app, service});
68
80
  });
69
81
 
70
- // initialize vc-exchanger service agent early (after database is ready) if
82
+ // initialize vc-workflow service agent early (after database is ready) if
71
83
  // KMS system is externalized; otherwise we must wait until KMS system
72
84
  // is ready
73
85
  const externalKms = !bedrock.config['service-agent'].kms.baseUrl.startsWith(
@@ -76,7 +88,7 @@ bedrock.events.on('bedrock.init', async () => {
76
88
  bedrock.events.on(event, async () => {
77
89
  await initializeServiceAgent({serviceType});
78
90
  });
79
- });
91
+ }
80
92
 
81
93
  async function usageAggregator({meter, signal, service} = {}) {
82
94
  const {id: meterId} = meter;
@@ -84,8 +96,25 @@ async function usageAggregator({meter, signal, service} = {}) {
84
96
  return service.configStorage.getUsage({meterId, signal});
85
97
  }
86
98
 
87
- async function validateConfigFn({config} = {}) {
99
+ async function validateConfigFn({config, op, routePrefix} = {}) {
88
100
  try {
101
+ // validate any `id` in a new config
102
+ if(op === 'create' && config.id !== undefined) {
103
+ try {
104
+ _validateId({id: config.id, routePrefix});
105
+ } catch(e) {
106
+ throw new BedrockError(
107
+ `Invalid client-provided configuration ID: ${e.message}.`, {
108
+ name: 'DataError',
109
+ details: {
110
+ httpStatusCode: 400,
111
+ public: true
112
+ },
113
+ cause: e
114
+ });
115
+ }
116
+ }
117
+
89
118
  // if credential templates are specified, then `zcaps` MUST include at
90
119
  // least `issue`
91
120
  const {credentialTemplates = [], zcaps = {}} = config;
@@ -118,3 +147,29 @@ async function validateConfigFn({config} = {}) {
118
147
  }
119
148
  return {valid: true};
120
149
  }
150
+
151
+ function _validateId({id, routePrefix} = {}) {
152
+ // format: <base>/<localId>
153
+
154
+ // ensure `id` starts with appropriate base URL
155
+ const {baseUri} = bedrock.config.server;
156
+ const base = `${baseUri}${routePrefix}/`;
157
+ if(id.startsWith(base)) {
158
+ // ensure `id` ends with appropriate local ID
159
+ const expectedLastSlashIndex = base.length - 1;
160
+ const idx = id.lastIndexOf('/');
161
+ if(idx === expectedLastSlashIndex) {
162
+ return parseLocalId({id});
163
+ }
164
+ }
165
+
166
+ throw new BedrockError(
167
+ `Configuration "id" must start with "${base}" and end in a multibase, ` +
168
+ 'base58-encoded local identifier.', {
169
+ name: 'DataError',
170
+ details: {
171
+ httpStatusCode: 400,
172
+ public: true
173
+ }
174
+ });
175
+ }
package/lib/issue.js CHANGED
@@ -1,24 +1,24 @@
1
1
  /*!
2
- * Copyright (c) 2022-2023 Digital Bazaar, Inc. All rights reserved.
2
+ * Copyright (c) 2022-2024 Digital Bazaar, Inc. All rights reserved.
3
3
  */
4
4
  import {evaluateTemplate, getZcapClient} from './helpers.js';
5
5
  import {createPresentation} from '@digitalbazaar/vc';
6
6
 
7
- export async function issue({exchanger, exchange} = {}) {
8
- // use any templates from exchanger and variables from exchange to produce
7
+ export async function issue({workflow, exchange} = {}) {
8
+ // use any templates from workflow and variables from exchange to produce
9
9
  // credentials to be issued; issue via the configured issuer instance
10
10
  const verifiableCredential = [];
11
- const {credentialTemplates = []} = exchanger;
11
+ const {credentialTemplates = []} = workflow;
12
12
  if(!credentialTemplates || credentialTemplates.length === 0) {
13
13
  // nothing to issue
14
14
  return {};
15
15
  }
16
16
 
17
17
  // evaluate template
18
- const credentials = await Promise.all(credentialTemplates.map(
19
- typedTemplate => evaluateTemplate({exchanger, exchange, typedTemplate})));
18
+ const credentialRequests = await Promise.all(credentialTemplates.map(
19
+ typedTemplate => evaluateTemplate({workflow, exchange, typedTemplate})));
20
20
  // issue all VCs
21
- const vcs = await _issue({exchanger, credentials});
21
+ const vcs = await _issue({workflow, credentialRequests});
22
22
  verifiableCredential.push(...vcs);
23
23
 
24
24
  // generate VP to return VCs
@@ -32,9 +32,9 @@ export async function issue({exchanger, exchange} = {}) {
32
32
  return {verifiablePresentation};
33
33
  }
34
34
 
35
- async function _issue({exchanger, credentials} = {}) {
35
+ async function _issue({workflow, credentialRequests} = {}) {
36
36
  // create zcap client for issuing VCs
37
- const {zcapClient, zcaps} = await getZcapClient({exchanger});
37
+ const {zcapClient, zcaps} = await getZcapClient({workflow});
38
38
 
39
39
  // issue VCs in parallel
40
40
  const capability = zcaps.issue;
@@ -42,14 +42,15 @@ async function _issue({exchanger, credentials} = {}) {
42
42
  // is not specific to it
43
43
  let url = capability.invocationTarget;
44
44
  if(!capability.invocationTarget.endsWith('/credentials/issue')) {
45
- if(!capability.invocationTarget.endsWith('/credentials')) {
46
- url += '/credentials/issue';
47
- } else {
48
- url += '/issue';
49
- }
45
+ url += capability.invocationTarget.endsWith('/credentials') ?
46
+ '/issue' : '/credentials/issue';
50
47
  }
51
- const results = await Promise.all(credentials.map(
52
- credential => zcapClient.write({url, capability, json: {credential}})));
48
+ const results = await Promise.all(credentialRequests.map(request => {
49
+ // normalize credential templates that return full VC API issue credential
50
+ // requests and those that return only the `credential` param directly
51
+ const json = request.credential ? request : {credential: request};
52
+ return zcapClient.write({url, capability, json});
53
+ }));
53
54
 
54
55
  // parse VCs from results
55
56
  const verifiableCredentials = results.map(
package/lib/logger.js CHANGED
@@ -1,6 +1,6 @@
1
1
  /*!
2
- * Copyright (c) 2020-2022 Digital Bazaar, Inc. All rights reserved.
2
+ * Copyright (c) 2020-2024 Digital Bazaar, Inc. All rights reserved.
3
3
  */
4
4
  import {loggers} from '@bedrock/core';
5
5
 
6
- export const logger = loggers.get('app').child('bedrock-vc-exchanger');
6
+ export const logger = loggers.get('app').child('bedrock-vc-workflow');