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

fix(ssi): do not register did by default but make it an event


Signed-off-by: default avatarBerend Sliedrecht <berend@animo.id>
parent 21a061c8
No related branches found
No related tags found
No related merge requests found
Showing
with 224 additions and 134 deletions
......@@ -4,44 +4,36 @@ import { DidDocument, JsonTransformer } from '@aries-framework/core';
import { BaseEvent } from './baseEvents.js';
/**
*
* @todo: this should be removed as it is a weird event that should not be needed
*
*/
export type EventDidsPublicDidInput = BaseEventInput;
/**
*
* @todo: this should be removed as it is a weird event that should not be needed
*
*/
export class EventDidsPublicDid extends BaseEvent<DidDocument> {
public static token = 'dids.publicDid';
export type EventDidsResolveInput = BaseEventInput<{ did: string }>;
export class EventDidsResolve extends BaseEvent<DidDocument> {
public static token = 'dids.resolve';
public get instance() {
return JsonTransformer.fromJSON(this.data, DidDocument);
}
public static fromEvent(e: EventDidsPublicDid) {
return new EventDidsPublicDid(
e.data,
e.tenantId,
e.id,
e.type,
e.timestamp,
);
public static fromEvent(e: EventDidsResolve) {
return new EventDidsResolve(e.data, e.tenantId, e.id, e.type, e.timestamp);
}
}
export type EventDidsResolveInput = BaseEventInput<{ did: string }>;
export class EventDidsResolve extends BaseEvent<DidDocument> {
public static token = 'dids.resolve';
export type EventDidsRegisterIndyFromSeedInput = BaseEventInput<{
seed: string;
}>;
export class EventDidsRegisterIndyFromSeed extends BaseEvent<Array<string>> {
public static token = 'dids.register.indy.fromSeed';
public get instance() {
return JsonTransformer.fromJSON(this.data, DidDocument);
return this.data;
}
public static fromEvent(e: EventDidsResolve) {
return new EventDidsResolve(e.data, e.tenantId, e.id, e.type, e.timestamp);
public static fromEvent(e: EventDidsRegisterIndyFromSeed) {
return new EventDidsRegisterIndyFromSeed(
e.data,
e.tenantId,
e.id,
e.type,
e.timestamp,
);
}
}
......@@ -16,11 +16,9 @@ import {
JwkDidResolver,
KeyDidRegistrar,
KeyDidResolver,
KeyType,
LogLevel,
PeerDidRegistrar,
PeerDidResolver,
TypedArrayEncoder,
WebDidResolver,
} from '@aries-framework/core';
import {
......@@ -40,7 +38,6 @@ import { logger } from '@ocm/shared';
import { LEDGERS } from '../config/ledger.js';
import { registerPublicDids } from './ledger/register.js';
import { AgentLogger } from './logger.js';
export type AppAgent = Agent<AgentService['modules']>;
......@@ -127,7 +124,7 @@ export class AgentService implements OnApplicationShutdown {
};
}
public get ledgers() {
private get ledgers() {
const ledgerIds = this.configService.get('agent.ledgerIds');
if (!ledgerIds || ledgerIds.length < 1 || ledgerIds[0] === '') {
......@@ -153,41 +150,8 @@ export class AgentService implements OnApplicationShutdown {
});
}
private async registerPublicDid() {
const { publicDidSeed, ledgerIds } = this.configService.get('agent');
if (!publicDidSeed) {
logger.info('No public did seed provided, skipping registration');
return;
}
if (!ledgerIds || ledgerIds.length < 1 || ledgerIds[0] === '') {
return;
}
const registeredPublicDidResponses = await registerPublicDids({
alias: this.config.label,
ledgerIds,
seed: publicDidSeed,
});
for (const publicDidResponse of registeredPublicDidResponses) {
await this.agent.dids.import({
overwrite: true,
did: publicDidResponse.did,
privateKeys: [
{
keyType: KeyType.Ed25519,
privateKey: TypedArrayEncoder.fromString(publicDidSeed),
},
],
});
}
}
public async onModuleInit() {
await this.agent.initialize();
await this.registerPublicDid();
logger.info('Agent initialized');
}
......
......@@ -34,4 +34,20 @@ describe('DidsController', () => {
expect(event.data).toStrictEqual(result);
});
});
describe('register indy did from seed', () => {
it('should register an indy did from seed', async () => {
const result = ['did:indy:bcovrin:test:mock'];
jest
.spyOn(didsService, 'registerDidIndyFromSeed')
.mockResolvedValue(result);
const event = await didsController.registerFromSeed({
seed: 'random-secure-seed',
tenantId: 'some-id',
});
expect(event.data).toStrictEqual(result);
});
});
});
import { Controller } from '@nestjs/common';
import { MessagePattern } from '@nestjs/microservices';
import {
EventDidsPublicDid,
EventDidsPublicDidInput,
EventDidsRegisterIndyFromSeed,
EventDidsRegisterIndyFromSeedInput,
EventDidsResolve,
EventDidsResolveInput,
} from '@ocm/shared';
......@@ -13,10 +13,10 @@ import { DidsService } from './dids.service.js';
export class DidsController {
public constructor(private didsService: DidsService) {}
@MessagePattern(EventDidsPublicDid.token)
public async publicDid(options: EventDidsPublicDidInput) {
return new EventDidsPublicDid(
await this.didsService.getPublicDid(options),
@MessagePattern(EventDidsRegisterIndyFromSeed.token)
public async registerFromSeed(options: EventDidsRegisterIndyFromSeedInput) {
return new EventDidsRegisterIndyFromSeed(
await this.didsService.registerDidIndyFromSeed(options),
options.tenantId,
);
}
......
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { AgentModule } from '../agent.module.js';
......@@ -6,7 +7,7 @@ import { DidsController } from './dids.controller.js';
import { DidsService } from './dids.service.js';
@Module({
imports: [AgentModule ],
imports: [AgentModule, ConfigModule],
providers: [DidsService],
controllers: [DidsController],
})
......
import type {
EventDidsPublicDidInput,
EventDidsResolveInput,
} from '@ocm/shared';
import type { EventDidsResolveInput } from '@ocm/shared';
import { KeyType, TypedArrayEncoder } from '@aries-framework/core';
import { Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { registerPublicDids } from '../ledger/register.js';
import { WithTenantService } from '../withTenantService.js';
@Injectable()
export class DidsService {
public withTenantService: WithTenantService;
private withTenantService: WithTenantService;
private configService: ConfigService;
public constructor(withTenantService: WithTenantService) {
public constructor(
withTenantService: WithTenantService,
configService: ConfigService,
) {
this.withTenantService = withTenantService;
this.configService = configService;
}
public async resolve({ did, tenantId }: EventDidsResolveInput) {
......@@ -34,26 +39,35 @@ export class DidsService {
});
}
public async getPublicDid({ tenantId }: EventDidsPublicDidInput) {
return this.withTenantService.invoke(tenantId, async (t) => {
const dids = await t.dids.getCreatedDids({ method: 'indy' });
if (dids.length === 0) {
throw new Error('No registered public DIDs');
}
public async registerDidIndyFromSeed({
tenantId,
seed,
}: {
tenantId: string;
seed: string;
}): Promise<Array<string>> {
const ledgerIds = this.configService.get('agent.ledgerIds');
if (dids.length > 1) {
throw new Error('Multiple public DIDs found');
}
const registeredPublicDidResponses = await registerPublicDids({
ledgerIds,
seed,
});
const didRecord = dids[0];
for (const publicDidResponse of registeredPublicDidResponses) {
await this.withTenantService.invoke(tenantId, (t) =>
t.dids.import({
overwrite: true,
did: publicDidResponse.did,
privateKeys: [
{
keyType: KeyType.Ed25519,
privateKey: TypedArrayEncoder.fromString(seed),
},
],
}),
);
}
if (!didRecord.didDocument) {
throw new Error(
'A public DID was found, but did not include a DID Document',
);
}
return didRecord.didDocument;
});
return registeredPublicDidResponses.map((r) => r.did);
}
}
......@@ -6,14 +6,12 @@ import axios from 'axios';
import { LEDGERS } from '../../config/ledger.js';
type RegisterPublicDidOptions = {
alias: string;
ledgerIds: Array<LedgerIds>;
seed: string;
};
type LedgerRegistrationBody = {
role?: 'ENDORSER';
alias?: string;
seed: string;
};
......@@ -25,7 +23,6 @@ type RegisterPublicDidResponse = {
export const registerPublicDids = async ({
ledgerIds,
alias,
seed,
}: RegisterPublicDidOptions): Promise<Array<RegisterPublicDidResponse>> => {
const responses: Array<RegisterPublicDidResponse> = [];
......@@ -36,7 +33,6 @@ export const registerPublicDids = async ({
const body: LedgerRegistrationBody = {
role: 'ENDORSER',
alias,
seed,
};
......
......@@ -58,7 +58,7 @@ export class SchemasService {
},
});
if (schemaState.state !== 'finished') {
if (schemaState.state !== 'finished' && schemaState.state !== 'action') {
throw new Error(
`Error registering schema: ${
schemaState.state === 'failed' ? schemaState.reason : 'Not Finished'
......
......@@ -6,6 +6,7 @@ import { HealthController } from '@ocm/shared';
import { AgentModule } from './agent/agent.module.js';
import { ConnectionsModule } from './agent/connections/connections.module.js';
import { SchemasModule } from './agent/schemas/schemas.module.js';
import { TenantsModule } from './agent/tenants/tenants.module.js';
import { config } from './config/config.js';
import { validationSchema } from './config/validation.js';
......@@ -21,6 +22,7 @@ import { validationSchema } from './config/validation.js';
AgentModule,
ConnectionsModule,
DidsModule,
SchemasModule,
TenantsModule,
],
controllers: [HealthController],
......
......@@ -5,7 +5,7 @@ import { ConfigModule, ConfigService } from '@nestjs/config';
import { validationSchema } from '../validation.js';
const mockConfig = (port: number = 3001): AppConfig => ({
const mockConfig = (port: number = 3001, withLedger = false): AppConfig => ({
agentHost: '',
port: 3000,
jwtSecret: '',
......@@ -16,19 +16,19 @@ const mockConfig = (port: number = 3001): AppConfig => ({
name: 'my-test-agent',
walletId: utils.uuid(),
walletKey: 'some-key',
ledgerIds: [],
ledgerIds: withLedger ? ['BCOVRIN_TEST'] : [],
host: 'http://localhost',
inboundPort: port,
path: '',
publicDidSeed: '',
publicDidSeed: withLedger ? '12312367897123300000000000000000' : '',
autoAcceptConnection: true,
autoAcceptCredential: AutoAcceptCredential.ContentApproved,
},
});
export const mockConfigModule = (port: number = 3000) =>
export const mockConfigModule = (port: number = 3000, withLedger = false) =>
ConfigModule.forRoot({
load: [() => mockConfig(port)],
load: [() => mockConfig(port, withLedger)],
validationSchema,
});
......
import type { INestApplication } from '@nestjs/common';
import type { ClientProxy } from '@nestjs/microservices';
import type {
EventDidsRegisterIndyFromSeedInput,
EventDidsResolveInput,
EventDidsPublicDidInput,
} from '@ocm/shared';
import { DidDocument } from '@aries-framework/core';
import { ClientsModule, Transport } from '@nestjs/microservices';
import { Test } from '@nestjs/testing';
import { EventDidsResolve, EventDidsPublicDid } from '@ocm/shared';
import { EventDidsRegisterIndyFromSeed, EventDidsResolve } from '@ocm/shared';
import { firstValueFrom } from 'rxjs';
import { AgentModule } from '../src/agent/agent.module.js';
import { DidsModule } from '../src/agent/dids/dids.module.js';
import { DidsService } from '../src/agent/dids/dids.service.js';
import { TenantsModule } from '../src/agent/tenants/tenants.module.js';
import { TenantsService } from '../src/agent/tenants/tenants.service.js';
import { mockConfigModule } from '../src/config/__tests__/mockConfig.js';
const mockDidDocument = {
context: ['https://w3id.org/did/v1'],
id: 'did:indy:bcovrin:test:7KuDTpQh3GJ7Gp6kErpWvM',
verificationMethod: [
{
id: 'did:indy:bcovrin:test:7KuDTpQh3GJ7Gp6kErpWvM#verkey',
type: 'Ed25519VerificationKey2018',
controller: 'did:indy:bcovrin:test:7KuDTpQh3GJ7Gp6kErpWvM',
publicKeyBase58: '4SySYXQUtuK26zfC7RpQpWYMThfbXphUf8LWyXXmxyTX',
},
],
authentication: ['did:indy:bcovrin:test:7KuDTpQh3GJ7Gp6kErpWvM#verkey'],
};
describe('Dids', () => {
const TOKEN = 'DIDS_CLIENT_SERVICE';
let app: INestApplication;
......@@ -39,12 +23,9 @@ describe('Dids', () => {
let tenantId: string;
beforeAll(async () => {
jest
.spyOn(DidsService.prototype, 'getPublicDid')
.mockResolvedValue(new DidDocument(mockDidDocument));
const moduleRef = await Test.createTestingModule({
imports: [
mockConfigModule(3005),
mockConfigModule(3005, true),
AgentModule,
DidsModule,
TenantsModule,
......@@ -72,16 +53,21 @@ describe('Dids', () => {
client.close();
});
it(EventDidsPublicDid.token, async () => {
const response$ = client.send<EventDidsPublicDid, EventDidsPublicDidInput>(
EventDidsPublicDid.token,
{ tenantId },
);
it(EventDidsRegisterIndyFromSeed.token, async () => {
const response$ = client.send<
EventDidsRegisterIndyFromSeed,
EventDidsRegisterIndyFromSeedInput
>(EventDidsRegisterIndyFromSeed.token, {
seed: '12312367897123300000000000000000',
tenantId,
});
const response = await firstValueFrom(response$);
const eventInstance = EventDidsPublicDid.fromEvent(response);
const eventInstance = EventDidsRegisterIndyFromSeed.fromEvent(response);
expect(eventInstance.instance).toMatchObject(mockDidDocument);
expect(eventInstance.instance).toMatchObject(
expect.arrayContaining(['did:indy:bcovrin:test:9MMeff63VnCpogD2FWfKnJ']),
);
});
it(EventDidsResolve.token, async () => {
......
import type { INestApplication } from '@nestjs/common';
import type { ClientProxy } from '@nestjs/microservices';
import type {
EventAnonCredsSchemasGetAllInput,
EventAnonCredsSchemasGetByIdInput,
EventAnonCredsSchemasRegisterInput,
} from '@ocm/shared';
import { ClientsModule, Transport } from '@nestjs/microservices';
import { Test } from '@nestjs/testing';
import {
EventAnonCredsSchemasGetAll,
EventAnonCredsSchemasGetById,
EventAnonCredsSchemasRegister,
} from '@ocm/shared';
import { firstValueFrom } from 'rxjs';
import { AgentModule } from '../src/agent/agent.module.js';
import { DidsModule } from '../src/agent/dids/dids.module.js';
import { DidsService } from '../src/agent/dids/dids.service.js';
import { SchemasModule } from '../src/agent/schemas/schemas.module.js';
import { TenantsModule } from '../src/agent/tenants/tenants.module.js';
import { TenantsService } from '../src/agent/tenants/tenants.service.js';
import { mockConfigModule } from '../src/config/__tests__/mockConfig.js';
describe('Schemas', () => {
const TOKEN = 'SCHEMAS_CLIENT_SERVICE';
let app: INestApplication;
let client: ClientProxy;
let tenantId: string;
let issuerDid: string;
beforeAll(async () => {
const moduleRef = await Test.createTestingModule({
imports: [
mockConfigModule(3004, true),
AgentModule,
SchemasModule,
TenantsModule,
DidsModule,
ClientsModule.register([{ name: TOKEN, transport: Transport.NATS }]),
],
}).compile();
app = moduleRef.createNestApplication();
app.connectMicroservice({ transport: Transport.NATS });
await app.startAllMicroservices();
await app.init();
client = app.get(TOKEN);
await client.connect();
const ts = app.get(TenantsService);
const { id } = await ts.create(TOKEN);
tenantId = id;
const ds = app.get(DidsService);
const [did] = await ds.registerDidIndyFromSeed({
tenantId,
seed: '12312367897123300000000000000000',
});
issuerDid = did;
});
afterAll(async () => {
await app.close();
client.close();
});
it(EventAnonCredsSchemasGetAll.token, async () => {
const response$ = client.send<
EventAnonCredsSchemasGetAll,
EventAnonCredsSchemasGetAllInput
>(EventAnonCredsSchemasGetAll.token, { tenantId });
const response = await firstValueFrom(response$);
const eventInstance = EventAnonCredsSchemasGetAll.fromEvent(response);
expect(eventInstance.instance).toEqual(expect.arrayContaining([]));
});
it(EventAnonCredsSchemasGetById.token, async () => {
const response$ = client.send<
EventAnonCredsSchemasGetById,
EventAnonCredsSchemasGetByIdInput
>(EventAnonCredsSchemasGetById.token, { tenantId, schemaId: 'some-id' });
const response = await firstValueFrom(response$);
const eventInstance = EventAnonCredsSchemasGetById.fromEvent(response);
expect(eventInstance.instance).toEqual(null);
});
it(EventAnonCredsSchemasRegister.token, async () => {
const version = new Date().getTime().toString();
const attributeNames = ['names', 'age'];
const name = 'my-schema';
const response$ = client.send<
EventAnonCredsSchemasRegister,
EventAnonCredsSchemasRegisterInput
>(EventAnonCredsSchemasRegister.token, {
tenantId,
name,
version,
issuerDid,
attributeNames,
});
const response = await firstValueFrom(response$);
const eventInstance = EventAnonCredsSchemasRegister.fromEvent(response);
expect(eventInstance.instance).toMatchObject({
attrNames: attributeNames,
issuerId: issuerDid,
name,
version,
});
});
});
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