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
1 merge request!13feat(ssi): schema functionality
Showing
with 224 additions and 134 deletions
...@@ -4,44 +4,36 @@ import { DidDocument, JsonTransformer } from '@aries-framework/core'; ...@@ -4,44 +4,36 @@ import { DidDocument, JsonTransformer } from '@aries-framework/core';
import { BaseEvent } from './baseEvents.js'; import { BaseEvent } from './baseEvents.js';
/** export type EventDidsResolveInput = BaseEventInput<{ did: string }>;
* export class EventDidsResolve extends BaseEvent<DidDocument> {
* @todo: this should be removed as it is a weird event that should not be needed public static token = 'dids.resolve';
*
*/
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';
public get instance() { public get instance() {
return JsonTransformer.fromJSON(this.data, DidDocument); return JsonTransformer.fromJSON(this.data, DidDocument);
} }
public static fromEvent(e: EventDidsPublicDid) { public static fromEvent(e: EventDidsResolve) {
return new EventDidsPublicDid( return new EventDidsResolve(e.data, e.tenantId, e.id, e.type, e.timestamp);
e.data,
e.tenantId,
e.id,
e.type,
e.timestamp,
);
} }
} }
export type EventDidsResolveInput = BaseEventInput<{ did: string }>; export type EventDidsRegisterIndyFromSeedInput = BaseEventInput<{
export class EventDidsResolve extends BaseEvent<DidDocument> { seed: string;
public static token = 'dids.resolve'; }>;
export class EventDidsRegisterIndyFromSeed extends BaseEvent<Array<string>> {
public static token = 'dids.register.indy.fromSeed';
public get instance() { public get instance() {
return JsonTransformer.fromJSON(this.data, DidDocument); return this.data;
} }
public static fromEvent(e: EventDidsResolve) { public static fromEvent(e: EventDidsRegisterIndyFromSeed) {
return new EventDidsResolve(e.data, e.tenantId, e.id, e.type, e.timestamp); return new EventDidsRegisterIndyFromSeed(
e.data,
e.tenantId,
e.id,
e.type,
e.timestamp,
);
} }
} }
...@@ -16,11 +16,9 @@ import { ...@@ -16,11 +16,9 @@ import {
JwkDidResolver, JwkDidResolver,
KeyDidRegistrar, KeyDidRegistrar,
KeyDidResolver, KeyDidResolver,
KeyType,
LogLevel, LogLevel,
PeerDidRegistrar, PeerDidRegistrar,
PeerDidResolver, PeerDidResolver,
TypedArrayEncoder,
WebDidResolver, WebDidResolver,
} from '@aries-framework/core'; } from '@aries-framework/core';
import { import {
...@@ -40,7 +38,6 @@ import { logger } from '@ocm/shared'; ...@@ -40,7 +38,6 @@ import { logger } from '@ocm/shared';
import { LEDGERS } from '../config/ledger.js'; import { LEDGERS } from '../config/ledger.js';
import { registerPublicDids } from './ledger/register.js';
import { AgentLogger } from './logger.js'; import { AgentLogger } from './logger.js';
export type AppAgent = Agent<AgentService['modules']>; export type AppAgent = Agent<AgentService['modules']>;
...@@ -127,7 +124,7 @@ export class AgentService implements OnApplicationShutdown { ...@@ -127,7 +124,7 @@ export class AgentService implements OnApplicationShutdown {
}; };
} }
public get ledgers() { private get ledgers() {
const ledgerIds = this.configService.get('agent.ledgerIds'); const ledgerIds = this.configService.get('agent.ledgerIds');
if (!ledgerIds || ledgerIds.length < 1 || ledgerIds[0] === '') { if (!ledgerIds || ledgerIds.length < 1 || ledgerIds[0] === '') {
...@@ -153,41 +150,8 @@ export class AgentService implements OnApplicationShutdown { ...@@ -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() { public async onModuleInit() {
await this.agent.initialize(); await this.agent.initialize();
await this.registerPublicDid();
logger.info('Agent initialized'); logger.info('Agent initialized');
} }
......
...@@ -34,4 +34,20 @@ describe('DidsController', () => { ...@@ -34,4 +34,20 @@ describe('DidsController', () => {
expect(event.data).toStrictEqual(result); 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 { Controller } from '@nestjs/common';
import { MessagePattern } from '@nestjs/microservices'; import { MessagePattern } from '@nestjs/microservices';
import { import {
EventDidsPublicDid, EventDidsRegisterIndyFromSeed,
EventDidsPublicDidInput, EventDidsRegisterIndyFromSeedInput,
EventDidsResolve, EventDidsResolve,
EventDidsResolveInput, EventDidsResolveInput,
} from '@ocm/shared'; } from '@ocm/shared';
...@@ -13,10 +13,10 @@ import { DidsService } from './dids.service.js'; ...@@ -13,10 +13,10 @@ import { DidsService } from './dids.service.js';
export class DidsController { export class DidsController {
public constructor(private didsService: DidsService) {} public constructor(private didsService: DidsService) {}
@MessagePattern(EventDidsPublicDid.token) @MessagePattern(EventDidsRegisterIndyFromSeed.token)
public async publicDid(options: EventDidsPublicDidInput) { public async registerFromSeed(options: EventDidsRegisterIndyFromSeedInput) {
return new EventDidsPublicDid( return new EventDidsRegisterIndyFromSeed(
await this.didsService.getPublicDid(options), await this.didsService.registerDidIndyFromSeed(options),
options.tenantId, options.tenantId,
); );
} }
......
import { Module } from '@nestjs/common'; import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { AgentModule } from '../agent.module.js'; import { AgentModule } from '../agent.module.js';
...@@ -6,7 +7,7 @@ import { DidsController } from './dids.controller.js'; ...@@ -6,7 +7,7 @@ import { DidsController } from './dids.controller.js';
import { DidsService } from './dids.service.js'; import { DidsService } from './dids.service.js';
@Module({ @Module({
imports: [AgentModule ], imports: [AgentModule, ConfigModule],
providers: [DidsService], providers: [DidsService],
controllers: [DidsController], controllers: [DidsController],
}) })
......
import type { import type { EventDidsResolveInput } from '@ocm/shared';
EventDidsPublicDidInput,
EventDidsResolveInput,
} from '@ocm/shared';
import { KeyType, TypedArrayEncoder } from '@aries-framework/core';
import { Injectable } from '@nestjs/common'; import { Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { registerPublicDids } from '../ledger/register.js';
import { WithTenantService } from '../withTenantService.js'; import { WithTenantService } from '../withTenantService.js';
@Injectable() @Injectable()
export class DidsService { 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.withTenantService = withTenantService;
this.configService = configService;
} }
public async resolve({ did, tenantId }: EventDidsResolveInput) { public async resolve({ did, tenantId }: EventDidsResolveInput) {
...@@ -34,26 +39,35 @@ export class DidsService { ...@@ -34,26 +39,35 @@ export class DidsService {
}); });
} }
public async getPublicDid({ tenantId }: EventDidsPublicDidInput) { public async registerDidIndyFromSeed({
return this.withTenantService.invoke(tenantId, async (t) => { tenantId,
const dids = await t.dids.getCreatedDids({ method: 'indy' }); seed,
if (dids.length === 0) { }: {
throw new Error('No registered public DIDs'); tenantId: string;
} seed: string;
}): Promise<Array<string>> {
const ledgerIds = this.configService.get('agent.ledgerIds');
if (dids.length > 1) { const registeredPublicDidResponses = await registerPublicDids({
throw new Error('Multiple public DIDs found'); 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) { return registeredPublicDidResponses.map((r) => r.did);
throw new Error(
'A public DID was found, but did not include a DID Document',
);
}
return didRecord.didDocument;
});
} }
} }
...@@ -6,14 +6,12 @@ import axios from 'axios'; ...@@ -6,14 +6,12 @@ import axios from 'axios';
import { LEDGERS } from '../../config/ledger.js'; import { LEDGERS } from '../../config/ledger.js';
type RegisterPublicDidOptions = { type RegisterPublicDidOptions = {
alias: string;
ledgerIds: Array<LedgerIds>; ledgerIds: Array<LedgerIds>;
seed: string; seed: string;
}; };
type LedgerRegistrationBody = { type LedgerRegistrationBody = {
role?: 'ENDORSER'; role?: 'ENDORSER';
alias?: string;
seed: string; seed: string;
}; };
...@@ -25,7 +23,6 @@ type RegisterPublicDidResponse = { ...@@ -25,7 +23,6 @@ type RegisterPublicDidResponse = {
export const registerPublicDids = async ({ export const registerPublicDids = async ({
ledgerIds, ledgerIds,
alias,
seed, seed,
}: RegisterPublicDidOptions): Promise<Array<RegisterPublicDidResponse>> => { }: RegisterPublicDidOptions): Promise<Array<RegisterPublicDidResponse>> => {
const responses: Array<RegisterPublicDidResponse> = []; const responses: Array<RegisterPublicDidResponse> = [];
...@@ -36,7 +33,6 @@ export const registerPublicDids = async ({ ...@@ -36,7 +33,6 @@ export const registerPublicDids = async ({
const body: LedgerRegistrationBody = { const body: LedgerRegistrationBody = {
role: 'ENDORSER', role: 'ENDORSER',
alias,
seed, seed,
}; };
......
...@@ -58,7 +58,7 @@ export class SchemasService { ...@@ -58,7 +58,7 @@ export class SchemasService {
}, },
}); });
if (schemaState.state !== 'finished') { if (schemaState.state !== 'finished' && schemaState.state !== 'action') {
throw new Error( throw new Error(
`Error registering schema: ${ `Error registering schema: ${
schemaState.state === 'failed' ? schemaState.reason : 'Not Finished' schemaState.state === 'failed' ? schemaState.reason : 'Not Finished'
......
...@@ -6,6 +6,7 @@ import { HealthController } from '@ocm/shared'; ...@@ -6,6 +6,7 @@ import { HealthController } from '@ocm/shared';
import { AgentModule } from './agent/agent.module.js'; import { AgentModule } from './agent/agent.module.js';
import { ConnectionsModule } from './agent/connections/connections.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 { TenantsModule } from './agent/tenants/tenants.module.js';
import { config } from './config/config.js'; import { config } from './config/config.js';
import { validationSchema } from './config/validation.js'; import { validationSchema } from './config/validation.js';
...@@ -21,6 +22,7 @@ import { validationSchema } from './config/validation.js'; ...@@ -21,6 +22,7 @@ import { validationSchema } from './config/validation.js';
AgentModule, AgentModule,
ConnectionsModule, ConnectionsModule,
DidsModule, DidsModule,
SchemasModule,
TenantsModule, TenantsModule,
], ],
controllers: [HealthController], controllers: [HealthController],
......
...@@ -5,7 +5,7 @@ import { ConfigModule, ConfigService } from '@nestjs/config'; ...@@ -5,7 +5,7 @@ import { ConfigModule, ConfigService } from '@nestjs/config';
import { validationSchema } from '../validation.js'; import { validationSchema } from '../validation.js';
const mockConfig = (port: number = 3001): AppConfig => ({ const mockConfig = (port: number = 3001, withLedger = false): AppConfig => ({
agentHost: '', agentHost: '',
port: 3000, port: 3000,
jwtSecret: '', jwtSecret: '',
...@@ -16,19 +16,19 @@ const mockConfig = (port: number = 3001): AppConfig => ({ ...@@ -16,19 +16,19 @@ const mockConfig = (port: number = 3001): AppConfig => ({
name: 'my-test-agent', name: 'my-test-agent',
walletId: utils.uuid(), walletId: utils.uuid(),
walletKey: 'some-key', walletKey: 'some-key',
ledgerIds: [], ledgerIds: withLedger ? ['BCOVRIN_TEST'] : [],
host: 'http://localhost', host: 'http://localhost',
inboundPort: port, inboundPort: port,
path: '', path: '',
publicDidSeed: '', publicDidSeed: withLedger ? '12312367897123300000000000000000' : '',
autoAcceptConnection: true, autoAcceptConnection: true,
autoAcceptCredential: AutoAcceptCredential.ContentApproved, autoAcceptCredential: AutoAcceptCredential.ContentApproved,
}, },
}); });
export const mockConfigModule = (port: number = 3000) => export const mockConfigModule = (port: number = 3000, withLedger = false) =>
ConfigModule.forRoot({ ConfigModule.forRoot({
load: [() => mockConfig(port)], load: [() => mockConfig(port, withLedger)],
validationSchema, validationSchema,
}); });
......
import type { INestApplication } from '@nestjs/common'; import type { INestApplication } from '@nestjs/common';
import type { ClientProxy } from '@nestjs/microservices'; import type { ClientProxy } from '@nestjs/microservices';
import type { import type {
EventDidsRegisterIndyFromSeedInput,
EventDidsResolveInput, EventDidsResolveInput,
EventDidsPublicDidInput,
} from '@ocm/shared'; } from '@ocm/shared';
import { DidDocument } from '@aries-framework/core';
import { ClientsModule, Transport } from '@nestjs/microservices'; import { ClientsModule, Transport } from '@nestjs/microservices';
import { Test } from '@nestjs/testing'; import { Test } from '@nestjs/testing';
import { EventDidsResolve, EventDidsPublicDid } from '@ocm/shared'; import { EventDidsRegisterIndyFromSeed, EventDidsResolve } from '@ocm/shared';
import { firstValueFrom } from 'rxjs'; import { firstValueFrom } from 'rxjs';
import { AgentModule } from '../src/agent/agent.module.js'; import { AgentModule } from '../src/agent/agent.module.js';
import { DidsModule } from '../src/agent/dids/dids.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 { TenantsModule } from '../src/agent/tenants/tenants.module.js';
import { TenantsService } from '../src/agent/tenants/tenants.service.js'; import { TenantsService } from '../src/agent/tenants/tenants.service.js';
import { mockConfigModule } from '../src/config/__tests__/mockConfig.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', () => { describe('Dids', () => {
const TOKEN = 'DIDS_CLIENT_SERVICE'; const TOKEN = 'DIDS_CLIENT_SERVICE';
let app: INestApplication; let app: INestApplication;
...@@ -39,12 +23,9 @@ describe('Dids', () => { ...@@ -39,12 +23,9 @@ describe('Dids', () => {
let tenantId: string; let tenantId: string;
beforeAll(async () => { beforeAll(async () => {
jest
.spyOn(DidsService.prototype, 'getPublicDid')
.mockResolvedValue(new DidDocument(mockDidDocument));
const moduleRef = await Test.createTestingModule({ const moduleRef = await Test.createTestingModule({
imports: [ imports: [
mockConfigModule(3005), mockConfigModule(3005, true),
AgentModule, AgentModule,
DidsModule, DidsModule,
TenantsModule, TenantsModule,
...@@ -72,16 +53,21 @@ describe('Dids', () => { ...@@ -72,16 +53,21 @@ describe('Dids', () => {
client.close(); client.close();
}); });
it(EventDidsPublicDid.token, async () => { it(EventDidsRegisterIndyFromSeed.token, async () => {
const response$ = client.send<EventDidsPublicDid, EventDidsPublicDidInput>( const response$ = client.send<
EventDidsPublicDid.token, EventDidsRegisterIndyFromSeed,
{ tenantId }, EventDidsRegisterIndyFromSeedInput
); >(EventDidsRegisterIndyFromSeed.token, {
seed: '12312367897123300000000000000000',
tenantId,
});
const response = await firstValueFrom(response$); 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 () => { 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