Skip to content

Commit

Permalink
Fix multiple credential status bug and add tests for it.
Browse files Browse the repository at this point in the history
  • Loading branch information
dlongley committed May 23, 2024
1 parent 4bf9cf9 commit c159173
Show file tree
Hide file tree
Showing 3 changed files with 140 additions and 4 deletions.
3 changes: 2 additions & 1 deletion lib/CredentialStatusWriter.js
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,8 @@ export class CredentialStatusWriter {
}

_addStatusEntry({credential, statusList, statusListIndex}) {
const {type, baseUrl, statusPurpose, options} = this.statusListConfig;
const {type, baseUrl, options} = this.statusListConfig;
const {statusPurpose} = statusList;

// set SLC ID to the ID of the status list as they are one in the same
const statusListCredential = statusList.id;
Expand Down
126 changes: 126 additions & 0 deletions test/mocha/20-credentials.js
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,11 @@ describe('issue APIs', () => {
let bslSuspensionRootZcap;
let bslSuspensionStatusId;
let bslSuspensionStatusRootZcap;
let bslRevocationSuspensionIssuerConfig;
let bslRevocationSuspensionIssuerId;
let bslRevocationSuspensionRootZcap;
let bslRevocationSuspensionStatusId;
let bslRevocationSuspensionStatusRootZcap;
let smallBslIssuerConfig;
let smallBslIssuerId;
let smallBslRootZcap;
Expand Down Expand Up @@ -248,6 +253,36 @@ describe('issue APIs', () => {
`urn:zcap:root:${encodeURIComponent(statusConfig.id)}`;
}

// create issuer instance w/ bitstring status list options
// w/ both revocation and suspension status purposes
{
const {
statusConfig,
issuerCreateStatusListZcap
} = await helpers.provisionDependencies(depOptions);
const statusListOptions = [{
type: 'BitstringStatusList',
statusPurpose: ['revocation', 'suspension'],
zcapReferenceIds: {
createCredentialStatusList: 'createCredentialStatusList'
}
}];
const newZcaps = {
...zcaps,
createCredentialStatusList: issuerCreateStatusListZcap
};
const issuerConfig = await helpers.createIssuerConfig({
capabilityAgent, zcaps: newZcaps, statusListOptions, suiteName
});
bslRevocationSuspensionIssuerConfig = issuerConfig;
bslRevocationSuspensionIssuerId = issuerConfig.id;
bslRevocationSuspensionRootZcap =
`urn:zcap:root:${encodeURIComponent(issuerConfig.id)}`;
bslRevocationSuspensionStatusId = statusConfig.id;
bslRevocationSuspensionStatusRootZcap =
`urn:zcap:root:${encodeURIComponent(statusConfig.id)}`;
}

// create issuer instance w/ small status list
{
const {
Expand Down Expand Up @@ -1035,6 +1070,97 @@ describe('issue APIs', () => {
{verifiableCredential}));
status.should.equal(true);
});
it('updates BitstringStatusList revocation+suspension status',
async () => {
// first issue VC
const credential = klona(mockCredential);
const zcapClient = helpers.createZcapClient({capabilityAgent});
const {data: {verifiableCredential}} = await zcapClient.write({
url: `${bslRevocationSuspensionIssuerId}/credentials/issue`,
capability: bslRevocationSuspensionRootZcap,
json: {credential, options: issueOptions}
});

{
// get VC suspension status
const statusInfo = await helpers.getCredentialStatus(
{verifiableCredential, statusPurpose: 'suspension'});
let {status} = statusInfo;
status.should.equal(false);

// then suspend VC
let error;
try {
const {statusListOptions: [{indexAllocator}]} =
bslRevocationSuspensionIssuerConfig;
await zcapClient.write({
url: `${bslRevocationSuspensionStatusId}/credentials/status`,
capability: bslRevocationSuspensionStatusRootZcap,
json: {
credentialId: verifiableCredential.id,
indexAllocator,
credentialStatus: statusInfo.credentialStatus,
status: true
}
});
} catch(e) {
error = e;
}
assertNoError(error);

// force refresh of new SLC
await zcapClient.write({
url: `${statusInfo.statusListCredential}?refresh=true`,
capability: bslRevocationSuspensionStatusRootZcap,
json: {}
});

// check status of VC has changed
({status} = await helpers.getCredentialStatus(
{verifiableCredential, statusPurpose: 'suspension'}));
status.should.equal(true);
}

{
// get VC revocation status
const statusInfo = await helpers.getCredentialStatus(
{verifiableCredential, statusPurpose: 'revocation'});
let {status} = statusInfo;
status.should.equal(false);

// then revoke VC
let error;
try {
const {statusListOptions: [{indexAllocator}]} =
bslRevocationSuspensionIssuerConfig;
await zcapClient.write({
url: `${bslRevocationSuspensionStatusId}/credentials/status`,
capability: bslRevocationSuspensionStatusRootZcap,
json: {
credentialId: verifiableCredential.id,
indexAllocator,
credentialStatus: statusInfo.credentialStatus,
status: true
}
});
} catch(e) {
error = e;
}
assertNoError(error);

// force refresh of new SLC
await zcapClient.write({
url: `${statusInfo.statusListCredential}?refresh=true`,
capability: bslRevocationSuspensionStatusRootZcap,
json: {}
});

// check status of VC has changed
({status} = await helpers.getCredentialStatus(
{verifiableCredential, statusPurpose: 'revocation'}));
status.should.equal(true);
}
});

it('updates multiple TerseBitstringStatusList statuses',
async () => {
Expand Down
15 changes: 12 additions & 3 deletions test/mocha/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -336,9 +336,16 @@ export async function getCredentialStatus({
verifiableCredential, statusPurpose, listLength
}) {
// get SLC for the VC
const {credentialStatus} = verifiableCredential;
let {credentialStatus} = verifiableCredential;
if(Array.isArray(credentialStatus)) {
throw new Error('Multiple credential statuses not supported.');
// find matching status purpose
credentialStatus = credentialStatus.find(
cs => cs.statusPurpose === statusPurpose);
if(!credentialStatus) {
throw new Error(
`Credential status with matching status purpose "${statusPurpose}" ` +
'not found.');
}
}
let {statusListCredential} = credentialStatus;
let statusListIndex;
Expand Down Expand Up @@ -379,7 +386,9 @@ export async function getCredentialStatus({
list = await decodeList({encodedList});
}
const status = list.getStatus(statusListIndex);
return {status, statusListCredential, expandedCredentialStatus};
return {
status, statusListCredential, expandedCredentialStatus, credentialStatus
};
}

export async function revokeDelegatedCapability({
Expand Down

0 comments on commit c159173

Please sign in to comment.