Skip to content

Commit

Permalink
Move SLC publish code into slcs.js.
Browse files Browse the repository at this point in the history
  • Loading branch information
dlongley committed Feb 18, 2024
1 parent 43e9128 commit e24c4a6
Show file tree
Hide file tree
Showing 3 changed files with 77 additions and 66 deletions.
8 changes: 3 additions & 5 deletions lib/http.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
/*!
* Copyright (c) 2018-2023 Digital Bazaar, Inc. All rights reserved.
* Copyright (c) 2018-2024 Digital Bazaar, Inc. All rights reserved.
*/
import * as bedrock from '@bedrock/core';
import * as slcs from './slcs.js';
import {
getMatchingStatusListConfig, issue, publishSlc, setStatus
} from './issuer.js';
import {getMatchingStatusListConfig, issue, setStatus} from './issuer.js';
import {
issueCredentialBody,
publishSlcBody,
Expand Down Expand Up @@ -124,7 +122,7 @@ export async function addRoutes({app, service} = {}) {
const {slcId} = req.params;
const id = `${config.id}${cfg.routes.slcs}/${encodeURIComponent(slcId)}`;

await publishSlc({id, config});
await slcs.publish({id, config});
res.sendStatus(204);

// meter operation usage
Expand Down
65 changes: 5 additions & 60 deletions lib/issuer.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
* Copyright (c) 2020-2024 Digital Bazaar, Inc. All rights reserved.
*/
import * as bedrock from '@bedrock/core';
import * as slcs from './slcs.js';
import {
issue as _issue, getCredentialStatusInfo, getDocumentStore, getIssuerAndSuite
} from './helpers.js';
Expand All @@ -11,6 +10,7 @@ import {createDocumentLoader} from './documentLoader.js';
import {CredentialStatusIssuer} from './CredentialStatusIssuer.js';
import {CredentialStatusWriter} from './CredentialStatusWriter.js';
import {decodeList} from '@digitalbazaar/vc-status-list';
import {publish} from './slcs.js';

const {util: {BedrockError}} = bedrock;

Expand Down Expand Up @@ -122,36 +122,6 @@ export async function issue({credential, config} = {}) {
return verifiableCredential;
}

export async function publishSlc({id, config} = {}) {
assert.string(id, 'id');
assert.object(config, 'config');

// do not use cache to ensure latest doc is published
const documentStore = await getDocumentStore({config});
const slcDoc = await documentStore.get({id, useCache: false});
const {content: credential, meta, sequence} = slcDoc;
if(!(meta.type === 'VerifiableCredential' &&
_isStatusListCredential({credential}))) {
throw new BedrockError(
`Credential "${id}" is not a supported status list credential.`,
'DataError', {
httpStatusCode: 400,
public: true
});
}
try {
// store SLC Doc for public serving
await slcs.set({credential, sequence});
} catch(e) {
// safe to ignore conflicts, a newer version of the SLC was published
// than the one that was retrieved
if(e.name === 'InvalidStateError' || e.name === 'DuplicateError') {
return;
}
throw e;
}
}

export async function setStatus({id, config, statusListConfig, status} = {}) {
assert.string(id, 'id');
assert.object(config, 'config');
Expand Down Expand Up @@ -207,8 +177,8 @@ export async function setStatus({id, config, statusListConfig, status} = {}) {
// express date without milliseconds
const now = (new Date()).toJSON();
slc.issuanceDate = `${now.slice(0, -5)}Z`;
// TODO: we want to be using `issued` and/or `validFrom`, right?
//slc.issued = issuanceDate;
// FIXME: add `slc.expirationDate`
// TODO: use `validFrom` and `validUntil` for v2 VCs

// delete existing proof and reissue SLC VC
delete slc.proof;
Expand All @@ -226,8 +196,9 @@ export async function setStatus({id, config, statusListConfig, status} = {}) {
}
}

// FIXME: auto-publish should handle this
// publish latest version of SLC for non-authz consumption
await publishSlc({id: slcId, config});
await publish({id: slcId, config});
}

export function getMatchingStatusListConfig({config, credentialStatus} = {}) {
Expand Down Expand Up @@ -261,29 +232,3 @@ export function getMatchingStatusListConfig({config, credentialStatus} = {}) {
public: true
});
}

// check if `credential` is some known type of status list credential
function _isStatusListCredential({credential}) {
// FIXME: check for VC context as well
if(!(credential['@context'] && Array.isArray(credential['@context']))) {
return false;
}
if(!(credential.type && Array.isArray(credential.type) &&
credential.type.includes('VerifiableCredential'))) {
return false;
}

for(const type of credential.type) {
if(type === 'RevocationList2020Credential') {
// FIXME: check for matching `@context` as well
return true;
}
if(type === 'StatusList2021Credential') {
// FIXME: check for matching `@context as well
return true;
}
}
// FIXME: check other types

return false;
}
70 changes: 69 additions & 1 deletion lib/slcs.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
/*!
* Copyright (c) 2020-2023 Digital Bazaar, Inc. All rights reserved.
* Copyright (c) 2020-2024 Digital Bazaar, Inc. All rights reserved.
*/
import * as bedrock from '@bedrock/core';
import * as database from '@bedrock/mongodb';
import assert from 'assert-plus';
import {getDocumentStore} from './helpers.js';
import {LruCache} from '@digitalbazaar/lru-memoize';

const {util: {BedrockError}} = bedrock;
Expand Down Expand Up @@ -115,6 +116,47 @@ export async function exists({id}) {
return !!record;
}

/**
* Publishes the status list credential with the given ID for the given
* issuer `config` -- if a newer version (since the time at which this function
* was called) has not already been published.
*
* @param {object} options - The options to use.
* @param {string} options.id - The SLC ID.
* @param {object} options.config - The issuer config.
*
* @returns {Promise<object>} Settles once the operation completes.
*/
export async function publish({id, config} = {}) {
assert.string(id, 'id');
assert.object(config, 'config');

// do not use cache to ensure latest doc is published
const documentStore = await getDocumentStore({config});
const slcDoc = await documentStore.get({id, useCache: false});
const {content: credential, meta, sequence} = slcDoc;
if(!(meta.type === 'VerifiableCredential' &&
_isStatusListCredential({credential}))) {
throw new BedrockError(
`Credential "${id}" is not a supported status list credential.`,
'DataError', {
httpStatusCode: 400,
public: true
});
}
try {
// store SLC Doc for public serving
await set({credential, sequence});
} catch(e) {
// safe to ignore conflicts, a newer version of the SLC was published
// than the one that was retrieved
if(e.name === 'InvalidStateError' || e.name === 'DuplicateError') {
return;
}
throw e;
}
}

async function _getUncachedRecord({id}) {
const collection = database.collections[COLLECTION_NAME];
const record = await collection.findOne(
Expand All @@ -130,3 +172,29 @@ async function _getUncachedRecord({id}) {
}
return record;
}

// check if `credential` is some known type of status list credential
function _isStatusListCredential({credential}) {
// FIXME: check for VC context as well
if(!(credential['@context'] && Array.isArray(credential['@context']))) {
return false;
}
if(!(credential.type && Array.isArray(credential.type) &&
credential.type.includes('VerifiableCredential'))) {
return false;
}

for(const type of credential.type) {
if(type === 'RevocationList2020Credential') {
// FIXME: check for matching `@context` as well
return true;
}
if(type === 'StatusList2021Credential') {
// FIXME: check for matching `@context as well
return true;
}
}
// FIXME: check other types

return false;
}

0 comments on commit e24c4a6

Please sign in to comment.