Skip to content
Snippets Groups Projects
Commit af343389 authored by Berend Sliedrecht's avatar Berend Sliedrecht
Browse files

feat(ssi-abstraction): create a custom revocation index instead of it being manually defined


Signed-off-by: default avatarBerend Sliedrecht <berend@animo.id>
parent 77c7bdea
No related branches found
No related tags found
2 merge requests!37Draft: Modifications for manual testing,!25feat(ssi): revocation ssi-abstraction
......@@ -55,7 +55,6 @@ export type EventDidcommAnonCredsCredentialsOfferInput = BaseEventInput<{
credentialDefinitionId: string;
attributes: Array<{ name: string; value: string; mimeType?: string }>;
revocationRegistryDefinitionId?: string;
revocationRegistryIndex?: number;
}>;
export class EventDidcommAnonCredsCredentialsOffer extends BaseEvent<CredentialExchangeRecord> {
public static token = 'didcomm.anoncreds.credentials.offer';
......
import type { TenantAgent } from '../agent.service.js';
import type { CredentialExchangeRecord } from '@credo-ts/core';
import type {
EventAnonCredsCredentialOfferGetAll,
......@@ -19,10 +20,11 @@ import type {
} from '@ocm/shared';
import { AutoAcceptCredential, CredentialState } from '@credo-ts/core';
import { GenericRecord } from '@credo-ts/core/build/modules/generic-records/repository/GenericRecord.js';
import { Injectable } from '@nestjs/common';
import { logger } from '@ocm/shared';
import { MetadataTokens } from '../../common/constants.js';
import { GenericRecordTokens, MetadataTokens } from '../../common/constants.js';
import { WithTenantService } from '../withTenantService.js';
@Injectable()
......@@ -141,13 +143,13 @@ export class AnonCredsCredentialsService {
connectionId,
credentialDefinitionId,
attributes,
revocationRegistryIndex,
revocationRegistryDefinitionId,
}: EventDidcommAnonCredsCredentialsOfferInput): Promise<
EventDidcommAnonCredsCredentialsOffer['data']
> {
return this.withTenantService.invoke(tenantId, (t) =>
t.credentials.offerCredential({
return this.withTenantService.invoke(tenantId, async (t) => {
const revocationRegistryIndex = await this.getNextRevocationIdx(t);
return t.credentials.offerCredential({
protocolVersion: 'v2',
connectionId,
credentialFormats: {
......@@ -158,15 +160,14 @@ export class AnonCredsCredentialsService {
revocationRegistryIndex,
},
},
}),
);
});
});
}
public async offerToSelf({
tenantId,
credentialDefinitionId,
attributes,
revocationRegistryIndex,
revocationRegistryDefinitionId,
}: EventDidcommAnonCredsCredentialsOfferToSelfInput): Promise<
EventDidcommAnonCredsCredentialsOfferToSelf['data']
......@@ -175,7 +176,7 @@ export class AnonCredsCredentialsService {
const connections = await t.connections.getAll();
const connection = connections.find((c) => {
const metadata = c.metadata.get<{ withSelf: boolean }>(
MetadataTokens.GAIA_X_CONNECTION_METADATA_KEY,
MetadataTokens.CONNECTION_METADATA_KEY,
);
return metadata && metadata.withSelf === true;
});
......@@ -190,6 +191,8 @@ export class AnonCredsCredentialsService {
throw new Error('Connection with yourself is not ready, yet');
}
const revocationRegistryIndex = await this.getNextRevocationIdx(t);
return t.credentials.offerCredential({
protocolVersion: 'v2',
autoAcceptCredential: AutoAcceptCredential.Always,
......@@ -205,4 +208,48 @@ export class AnonCredsCredentialsService {
});
});
}
// TODO: this is mainly focussed on one revocation status list per issuer. But this is normally not the case. The credential should store the record id in metadata to support multiple generic records. One per revocation status list
private async getNextRevocationIdx(
tenantAgent: TenantAgent,
): Promise<number> {
let recordExists = true;
let genericRecord = await tenantAgent.genericRecords.findById(
GenericRecordTokens.REVOCATION,
);
if (!genericRecord) {
recordExists = false;
genericRecord = new GenericRecord({
id: GenericRecordTokens.REVOCATION,
content: {
revocationIndices: [],
},
});
}
if (
!genericRecord.content.revocationIndices ||
!Array.isArray(genericRecord.content.revocationIndices)
) {
throw new Error(
`Revocation record '${GenericRecordTokens.REVOCATION}' does not have the required revocationIndices in the content. Invalid state has been reached`,
);
}
const highestIdx =
genericRecord.content.revocationIndices.length > 0
? Math.max(...genericRecord.content.revocationIndices)
: 0;
const credentialIdx = highestIdx + 1;
genericRecord.content.revocationIndices.push(credentialIdx);
recordExists
? await tenantAgent.genericRecords.update(genericRecord)
: await tenantAgent.genericRecords.save(genericRecord);
return credentialIdx;
}
}
......@@ -153,7 +153,7 @@ export class ConnectionsService {
async ({ payload: { connectionRecord } }) => {
if (connectionRecord.state !== DidExchangeState.Completed) return;
connectionRecord.metadata.set(
MetadataTokens.GAIA_X_CONNECTION_METADATA_KEY,
MetadataTokens.CONNECTION_METADATA_KEY,
{
trusted: true,
withSelf: true,
......
......@@ -3,5 +3,9 @@ export enum NATSServices {
}
export enum MetadataTokens {
GAIA_X_CONNECTION_METADATA_KEY = 'gaia_x_connection_metadata_key',
CONNECTION_METADATA_KEY = 'connection_metadata_key',
}
export enum GenericRecordTokens {
REVOCATION = 'revocation_generic_record',
}
......@@ -152,7 +152,7 @@ describe('Connections', () => {
expect(eventInstance.instance).toHaveProperty('id');
const metadata = eventInstance.instance.metadata.get(
MetadataTokens.GAIA_X_CONNECTION_METADATA_KEY,
MetadataTokens.CONNECTION_METADATA_KEY,
);
expect(metadata).toMatchObject({ trusted: true });
});
......
......@@ -233,7 +233,6 @@ describe('Revocation', () => {
{ name: 'Age', value: '30' },
],
revocationRegistryDefinitionId: revRegDefId,
revocationRegistryIndex: 10,
credentialDefinitionId,
});
const response = await firstValueFrom(response$);
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment