From a33cd25b7aa17972afd83c64937ed571f4bf1e2b Mon Sep 17 00:00:00 2001 From: DaevMithran <61043607+DaevMithran@users.noreply.github.com> Date: Wed, 7 Jun 2023 19:15:44 +0530 Subject: [PATCH] fix: External db toggle panic (#248) * fix: External db toggle panic * fix: Null agent condition * fix: Fix init_agent initialization * feat: Refactor code --- package-lock.json | 4 +- src/app.ts | 3 +- src/controllers/credentials.ts | 4 +- src/controllers/issuer.ts | 16 +-- src/database/entities/customer.entity.ts | 14 +- src/database/types/types.ts | 10 +- src/middleware/authentication.ts | 14 +- src/services/credentials.ts | 6 +- src/services/customer.ts | 4 +- src/services/identity/IIdentity.ts | 2 +- src/services/identity/agent.ts | 174 +++++++++++++++++++++++ src/services/identity/index.ts | 19 ++- src/services/identity/local.ts | 130 ++++++----------- src/services/identity/postgres.ts | 160 +++++++-------------- src/services/store.ts | 2 +- src/types/types.ts | 16 ++- swagger.json | 2 +- 17 files changed, 336 insertions(+), 244 deletions(-) create mode 100644 src/services/identity/agent.ts diff --git a/package-lock.json b/package-lock.json index e7d06df6..bd6ccc0a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@cheqd/credential-service", - "version": "2.1.0-develop.13", + "version": "2.1.0-develop.16", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@cheqd/credential-service", - "version": "2.1.0-develop.13", + "version": "2.1.0-develop.16", "license": "Apache-2.0", "dependencies": { "@cheqd/did-provider-cheqd": "^3.3.0", diff --git a/src/app.ts b/src/app.ts index 6598dba3..227ab8cd 100644 --- a/src/app.ts +++ b/src/app.ts @@ -14,6 +14,7 @@ import { CORS_ERROR_MSG } from './types/constants.js' import swaggerJSONDoc from '../swagger.json' assert { type: "json" } import * as dotenv from 'dotenv' +import { Identity } from './services/identity/index.js' dotenv.config() class App { @@ -28,7 +29,7 @@ class App { private middleware() { this.express.use(express.json({ limit: '50mb' })) - this.express.use(express.urlencoded({ extended: false })) + this.express.use(express.urlencoded({ extended: false })) this.express.use(Helmet()) this.express.use(cors({ origin: function(origin, callback){ diff --git a/src/controllers/credentials.ts b/src/controllers/credentials.ts index 360b92e3..79be76a4 100644 --- a/src/controllers/credentials.ts +++ b/src/controllers/credentials.ts @@ -27,7 +27,7 @@ export class CredentialController { ] public async issue(request: Request, response: Response) { - const result = validationResult(request); + const result = validationResult(request) if (!result.isEmpty()) { return response.status(400).json({ error: result.array()[0].msg }) } @@ -46,7 +46,7 @@ export class CredentialController { return response.status(405).json({ error: 'Unsupported media type.' }) } - const result = validationResult(request); + const result = validationResult(request) if (!result.isEmpty()) { return response.status(400).json({ error: result.array()[0].msg }) } diff --git a/src/controllers/issuer.ts b/src/controllers/issuer.ts index 5932578e..188fb3ed 100644 --- a/src/controllers/issuer.ts +++ b/src/controllers/issuer.ts @@ -46,7 +46,7 @@ export class IssuerController { public async createKey(request: Request, response: Response) { try { - const key = await Identity.createKey('Ed25519', response.locals.customerId) + const key = await Identity.instance.createKey('Ed25519', response.locals.customerId) return response.status(200).json(key) } catch (error) { return response.status(500).json({ @@ -57,7 +57,7 @@ export class IssuerController { public async getKey(request: Request, response: Response) { try { - const key = await Identity.getKey(request.params.kid, response.locals.customerId) + const key = await Identity.instance.getKey(request.params.kid, response.locals.customerId) return response.status(200).json(key) } catch (error) { return response.status(500).json({ @@ -83,7 +83,7 @@ export class IssuerController { if (options.didDocument) { didDocument = options.didDocument } else if (verificationMethod) { - const key = await Identity.createKey('Ed25519', response.locals.customerId) + const key = await Identity.instance.createKey('Ed25519', response.locals.customerId) kids.push(key.kid) didDocument = generateDidDoc({ verificationMethod: verificationMethod.type, @@ -99,7 +99,7 @@ export class IssuerController { }) } - const did = await Identity.createDid(network, didDocument, response.locals.customerId) + const did = await Identity.instance.createDid(network, didDocument, response.locals.customerId) return response.status(200).json(did) } catch (error) { return response.status(500).json({ @@ -122,7 +122,7 @@ export class IssuerController { let resourcePayload: Partial = {} try { // check if did is registered on the ledger - let resolvedDocument = await Identity.resolveDid(did) + let resolvedDocument: any = await Identity.instance.resolveDid(did) if(!resolvedDocument?.didDocument || resolvedDocument.didDocumentMetadata.deactivated) { return response.status(400).send({ error: `${did} is a Deactivated DID` @@ -141,7 +141,7 @@ export class IssuerController { alsoKnownAs } network = network || (did.split(':'))[2] - const result = await Identity.createResource( network, resourcePayload, response.locals.customerId) + const result = await Identity.instance.createResource( network, resourcePayload, response.locals.customerId) if ( result ) { return response.status(201).json({ resource: resourcePayload @@ -162,9 +162,9 @@ export class IssuerController { try { let did: any if(request.params.did) { - did = await Identity.resolveDid(request.params.did) + did = await Identity.instance.resolveDid(request.params.did) } else { - did = await Identity.listDids(response.locals.customerId) + did = await Identity.instance.listDids(response.locals.customerId) } return response.status(200).json(did) diff --git a/src/database/entities/customer.entity.ts b/src/database/entities/customer.entity.ts index 076e7861..14632fd6 100644 --- a/src/database/entities/customer.entity.ts +++ b/src/database/entities/customer.entity.ts @@ -3,26 +3,26 @@ import { Column, Entity, PrimaryGeneratedColumn, ValueTransformer } from 'typeor import * as dotenv from 'dotenv' dotenv.config() -const { ENABLE_EXTERNAL_DB } = process.env; +const { ENABLE_EXTERNAL_DB } = process.env const arrayToJsonTransformer = (shouldTransform: string): ValueTransformer => { return { to: (array: any[]) => { if (shouldTransform == "false") { // Convert the array to a JSON string - return JSON.stringify(array); + return JSON.stringify(array) } - return array; + return array }, from: (jsonString: string) => { if (shouldTransform == "false") { // Parse the JSON string and return the array - return JSON.parse(jsonString); + return JSON.parse(jsonString) } - return jsonString; + return jsonString }, - }; -}; + } +} @Entity('customers') export class CustomerEntity { diff --git a/src/database/types/types.ts b/src/database/types/types.ts index 2f3d273b..3ace438b 100644 --- a/src/database/types/types.ts +++ b/src/database/types/types.ts @@ -8,10 +8,10 @@ import { CreateCustomersTable1683723285946 } from '../migrations/CreateCustomers import * as dotenv from 'dotenv' dotenv.config() -const { EXTERNAL_DB_CONNECTION_URL, EXTERNAL_DB_CERT } = process.env; +const { EXTERNAL_DB_CONNECTION_URL, EXTERNAL_DB_CERT } = process.env export interface AbstractDatabase { - setup(): DataSource; + setup(): DataSource } export class Memory implements AbstractDatabase { @@ -24,13 +24,13 @@ export class Memory implements AbstractDatabase { migrations: [...migrations], migrationsRun: true, logging: ['error', 'info', 'warn'] - }); + }) } } export class Postgres implements AbstractDatabase { setup(): DataSource { - const { parse } = pkg; + const { parse } = pkg const config = parse(EXTERNAL_DB_CONNECTION_URL) if (!(config.host && config.port && config.database)) { throw new Error(`Error: Invalid Database URL`) @@ -49,6 +49,6 @@ export class Postgres implements AbstractDatabase { migrations: [...migrations, CreateCustomersTable1683723285946], entities: [...Entities, CustomerEntity], logging: ['error', 'info', 'warn'] - }); + }) } } diff --git a/src/middleware/authentication.ts b/src/middleware/authentication.ts index 8b5c9ec6..498ccc2f 100644 --- a/src/middleware/authentication.ts +++ b/src/middleware/authentication.ts @@ -1,9 +1,9 @@ import { Request, Response, NextFunction } from 'express' -import { expressjwt, Request as JWTRequest } from 'express-jwt' -import { createRemoteJWKSet, jwtVerify } from 'jose'; +import { Request as JWTRequest } from 'express-jwt' +import { createRemoteJWKSet, jwtVerify } from 'jose' import { CustomerService } from '../services/customer.js' -import { IncomingHttpHeaders } from 'http'; +import { IncomingHttpHeaders } from 'http' import * as dotenv from 'dotenv' dotenv.config() @@ -28,8 +28,8 @@ export const extractBearerTokenFromHeaders = ({ authorization }: IncomingHttpHea throw new Error(`Authorization token type is not supported. Valid type: "${bearerTokenIdentifier}".`) } - return authorization.slice(bearerTokenIdentifier.length + 1); -}; + return authorization.slice(bearerTokenIdentifier.length + 1) +} export class Authentication { @@ -51,7 +51,7 @@ export class Authentication { } break default: - if (request.path != '/account' && !await CustomerService.instance.find(response.locals.customerId, {})) { + if (!['/account', '/', '/store'].includes(request.path) && !await CustomerService.instance.find(response.locals.customerId, {})) { message = 'Customer not found' } break @@ -82,7 +82,7 @@ export class Authentication { // expected audience token, should be the resource indicator of the current API audience: LOGTO_RESOURCE_URL, } - ); + ) // custom payload logic response.locals.customerId = payload.sub diff --git a/src/services/credentials.ts b/src/services/credentials.ts index 66452e19..15007a6d 100644 --- a/src/services/credentials.ts +++ b/src/services/credentials.ts @@ -32,7 +32,7 @@ export class Credentials { credential.expirationDate = request.expirationDate } - let verifiable_credential = await Identity.createCredential(credential, request.format, agentId) + let verifiable_credential = await Identity.instance.createCredential(credential, request.format, agentId) if (ENABLE_VERIDA_CONNECTOR === 'true' && request.subjectDid.startsWith('did:vda')) { await VeridaService.instance.sendCredential( @@ -47,13 +47,13 @@ export class Credentials { } async verify_credentials(credential: W3CVerifiableCredential | string, agentId: string): Promise { - const result = await Identity.verifyCredential(credential, agentId) + const result = await Identity.instance.verifyCredential(credential, agentId) delete(result.payload) return result } async verify_presentation(presentation: W3CVerifiablePresentation, agentId: string): Promise { - const result = await Identity.verifyPresentation(presentation, agentId) + const result = await Identity.instance.verifyPresentation(presentation, agentId) return result } } diff --git a/src/services/customer.ts b/src/services/customer.ts index e0a78b72..c92cc700 100644 --- a/src/services/customer.ts +++ b/src/services/customer.ts @@ -8,8 +8,6 @@ import { Identity } from './identity/index.js' import * as dotenv from 'dotenv' dotenv.config() -const { ENABLE_EXTERNAL_DB } = process.env; - export class CustomerService { public customerRepository: Repository @@ -23,7 +21,7 @@ export class CustomerService { if(await this.find(customerId, {})) { throw new Error('Customer exists') } - const kid = (await Identity.createKey('Secp256k1', customerId)).kid + const kid = (await Identity.instance.createKey('Secp256k1', customerId)).kid const customer = new CustomerEntity(customerId, kid, getCosmosAccount(kid)) return (await this.customerRepository.insert(customer)).identifiers[0] } diff --git a/src/services/identity/IIdentity.ts b/src/services/identity/IIdentity.ts index 34101c1e..e6e4dd0a 100644 --- a/src/services/identity/IIdentity.ts +++ b/src/services/identity/IIdentity.ts @@ -16,7 +16,7 @@ import { CredentialRequest, VeramoAgent } from '../../types/types' dotenv.config() export interface IIdentity { - agent: TAgent + agent?: TAgent privateStore?: AbstractPrivateKeyStore initAgent(): TAgent createAgent?(agentId: string): Promise diff --git a/src/services/identity/agent.ts b/src/services/identity/agent.ts new file mode 100644 index 00000000..42a76e0d --- /dev/null +++ b/src/services/identity/agent.ts @@ -0,0 +1,174 @@ +import { + createAgent, + CredentialPayload, + DIDDocument, + IAgentPlugin, + IDIDManager, + IIdentifier, + IKeyManager, + IResolver, + IVerifyResult, + ManagedKeyInfo, + MinimalImportableIdentifier, + MinimalImportableKey, + TAgent, + VerifiableCredential, + VerifiablePresentation, +} from '@veramo/core' + +import { Cheqd, getResolver as CheqdDidResolver, ResourcePayload } from '@cheqd/did-provider-cheqd' +import { CheqdNetwork } from '@cheqd/sdk' + +import { cheqdDidRegex, CreateAgentRequest, CredentialRequest, VeramoAgent } from '../../types/types.js' +import { VC_PROOF_FORMAT, VC_REMOVE_ORIGINAL_FIELDS } from '../../types/constants.js' +import { KeyManager } from '@veramo/key-manager' +import { DIDStore, KeyStore } from '@veramo/data-store' +import { DIDManager } from '@veramo/did-manager' +import { DIDResolverPlugin } from '@veramo/did-resolver' +import { Resolver, ResolverRegistry } from 'did-resolver' +import { CredentialPlugin } from '@veramo/credential-w3c' +import { CredentialIssuerLD, LdDefaultContexts, VeramoEd25519Signature2018 } from '@veramo/credential-ld' + +export class Veramo { + + static instance = new Veramo() + + public createVeramoAgent({ providers, kms, dbConnection, cheqdProviders, enableResolver, enableCredential }: CreateAgentRequest) : VeramoAgent { + const plugins: IAgentPlugin[] = [] + + if(providers) { + plugins.push(new DIDManager({ + store: new DIDStore(dbConnection), + defaultProvider: 'did:cheqd:testnet', + providers + })) + } + + if(kms) { + plugins.push(new KeyManager({ + store: new KeyStore(dbConnection), + kms + })) + } + + if(cheqdProviders) { + plugins.push(new Cheqd({ + providers: cheqdProviders + })) + } + + if (enableResolver) { + plugins.push( + new DIDResolverPlugin({ + resolver: new Resolver({ + ...CheqdDidResolver({ url: process.env.RESOLVER_URL }) as ResolverRegistry + }) + }) + ) + } + + if (enableCredential) { + plugins.push( + new CredentialPlugin(), + new CredentialIssuerLD({ + contextMaps: [LdDefaultContexts], + suites: [new VeramoEd25519Signature2018()] + }) + ) + } + return createAgent({ plugins }) + } + + async createKey(agent: TAgent, type: 'Ed25519' | 'Secp256k1'='Ed25519'): Promise { + const [kms] = await agent.keyManagerGetKeyManagementSystems() + const key = await agent.keyManagerCreate({ + type: type || 'Ed25519', + kms, + }) + return key + } + + async getKey(agent: TAgent, kid: string) { + return await agent.keyManagerGet({ kid }) + } + + async createDid(agent: TAgent, network: string, didDocument: DIDDocument): Promise { + try { + const [kms] = await agent.keyManagerGetKeyManagementSystems() + + const identifier: IIdentifier = await agent.didManagerCreate({ + provider: `did:cheqd:${network}`, + kms, + options: { + document: didDocument + } + }) + return identifier + } catch (error) { + throw new Error(`${error}`) + } + } + + async listDids(agent: TAgent) { + return (await agent.didManagerFind()).map((res)=>res.did) + } + + async resolveDid(agent: TAgent, did: string) { + return await agent.resolveDid({ didUrl: did }) + } + + async getDid(agent: TAgent, did: string) { + return await agent.didManagerGet({ did }) + } + + async importDid(agent: TAgent, did: string, privateKeyHex: string, publicKeyHex: string): Promise { + const [kms] = await agent.keyManagerGetKeyManagementSystems() + + if (!did.match(cheqdDidRegex)) { + throw new Error('Invalid DID') + } + + const key: MinimalImportableKey = { kms: kms, type: 'Ed25519', privateKeyHex, publicKeyHex } + const identifier: IIdentifier = await agent.didManagerImport({ keys: [key], did, controllerKeyId: key.kid } as MinimalImportableIdentifier) + return identifier + } + + async createResource(agent: VeramoAgent, network: string, payload: ResourcePayload) { + try { + const [kms] = await agent.keyManagerGetKeyManagementSystems() + + const result: boolean = await agent.cheqdCreateLinkedResource({ + kms, + payload, + network: network as CheqdNetwork + }) + return result + } catch (error) { + throw new Error(`${error}`) + } + } + + async createCredential(agent: VeramoAgent, credential: CredentialPayload, format: CredentialRequest['format']): Promise { + try { + const verifiable_credential = await agent.createVerifiableCredential( + { + save: false, + credential, + proofFormat: format == 'jsonld' ? 'lds' : VC_PROOF_FORMAT, + removeOriginalFields: VC_REMOVE_ORIGINAL_FIELDS + } + ) + return verifiable_credential + } catch (error) { + throw new Error(`${error}`) + } + } + + async verifyCredential(agent: VeramoAgent, credential: string | VerifiableCredential): Promise { + return await agent.verifyCredential({ credential, fetchRemoteContexts: true }) + } + + async verifyPresentation(agent: VeramoAgent, presentation: VerifiablePresentation | string): Promise { + return await agent.verifyPresentation({ presentation, fetchRemoteContexts: true }) + } +} diff --git a/src/services/identity/index.ts b/src/services/identity/index.ts index c60f516a..b4bf3962 100644 --- a/src/services/identity/index.ts +++ b/src/services/identity/index.ts @@ -1,6 +1,21 @@ import { LocalIdentity } from './local.js' import { PostgresIdentity } from './postgres.js' - export { IIdentity } from './IIdentity.js' -export const Identity = process.env.ENABLE_EXTERNAL_DB === 'true' ? new PostgresIdentity() : new LocalIdentity() \ No newline at end of file +import * as dotenv from 'dotenv' +dotenv.config() + +export class Identity { + private agent: LocalIdentity | PostgresIdentity + + static instance = new Identity().agent + + constructor() { + if (process.env.ENABLE_EXTERNAL_DB == 'true') { + this.agent = PostgresIdentity.instance + } else { + this.agent = LocalIdentity.instance + } + this.agent.initAgent() + } +} \ No newline at end of file diff --git a/src/services/identity/local.ts b/src/services/identity/local.ts index 0e1a273a..3af319af 100644 --- a/src/services/identity/local.ts +++ b/src/services/identity/local.ts @@ -1,9 +1,6 @@ import { - DIDDocument, IIdentifier, ManagedKeyInfo, - MinimalImportableIdentifier, - MinimalImportableKey, TAgent, createAgent, CredentialPayload, @@ -19,14 +16,16 @@ import { KeyStore, DIDStore } from '@veramo/data-store' import { Cheqd, CheqdDIDProvider, getResolver as CheqdDidResolver, ResourcePayload } from '@cheqd/did-provider-cheqd' import { CheqdNetwork } from '@cheqd/sdk' import { Resolver, ResolverRegistry } from 'did-resolver' +import { CredentialPlugin } from '@veramo/credential-w3c' +import { CredentialIssuerLD, LdDefaultContexts, VeramoEd25519Signature2018 } from '@veramo/credential-ld' -import { cheqdDidRegex, CredentialRequest, DefaultRPCUrl, VeramoAgent } from '../../types/types.js' -import * as dotenv from 'dotenv' +import { CredentialRequest, DefaultRPCUrl, VeramoAgent } from '../../types/types.js' import { Connection } from '../../database/connection/connection.js' import { IIdentity } from './IIdentity.js' -import { VC_PROOF_FORMAT, VC_REMOVE_ORIGINAL_FIELDS } from '../../types/constants.js' -import { CredentialPlugin } from '@veramo/credential-w3c' -import { CredentialIssuerLD, LdDefaultContexts, VeramoEd25519Signature2018 } from '@veramo/credential-ld' +import { Veramo } from './agent.js' + +import * as dotenv from 'dotenv' + dotenv.config() const { @@ -40,18 +39,17 @@ const { } = process.env export class LocalIdentity implements IIdentity { - agent: VeramoAgent + agent?: VeramoAgent privateStore?: AbstractPrivateKeyStore public static instance = new LocalIdentity() - constructor() { - this.agent = this.initAgent() + initAgent() { if (!FEE_PAYER_MNEMONIC) { throw new Error(`No fee payer found`) } - } - - initAgent() { + if(this.agent) { + return this.agent + } const dbConnection = Connection.instance.dbConnection this.privateStore = new MemoryPrivateKeyStore() @@ -59,7 +57,7 @@ export class LocalIdentity implements IIdentity { { defaultKms: 'local', cosmosPayerSeed: FEE_PAYER_MNEMONIC, - networkType: CheqdNetwork.Mainnet as any, + networkType: CheqdNetwork.Mainnet, rpcUrl: MAINNET_RPC_URL || DefaultRPCUrl.Mainnet, } ) @@ -67,51 +65,34 @@ export class LocalIdentity implements IIdentity { { defaultKms: 'local', cosmosPayerSeed: FEE_PAYER_MNEMONIC, - networkType: CheqdNetwork.Testnet as any, + networkType: CheqdNetwork.Testnet, rpcUrl: TESTNET_RPC_URL || DefaultRPCUrl.Testnet, } ) - return createAgent>({ - plugins: [ - new KeyManager({ - store: new KeyStore(dbConnection), - kms: { - local: new KeyManagementSystem( - this.privateStore - ) - } - }), - new DIDManager({ - store: new DIDStore(dbConnection), - defaultProvider: 'did:cheqd:testnet', - providers: { - 'did:cheqd:mainnet': mainnetProvider, - 'did:cheqd:testnet': testnetProvider - } - }), - new DIDResolverPlugin({ - resolver: new Resolver({ - ...CheqdDidResolver({ url: RESOLVER_URL }) as ResolverRegistry - }) - }), - new CredentialPlugin(), - new CredentialIssuerLD({ - contextMaps: [LdDefaultContexts], - suites: [new VeramoEd25519Signature2018()] - }), - new Cheqd({ - providers: [mainnetProvider, testnetProvider] - }) - ] + this.agent = Veramo.instance.createVeramoAgent({ + dbConnection, + kms: { + local: new KeyManagementSystem( + this.privateStore + ) + }, + providers: { + 'did:cheqd:mainnet': mainnetProvider, + 'did:cheqd:testnet': testnetProvider + }, + cheqdProviders: [mainnetProvider, testnetProvider], + enableCredential: true, + enableResolver: true }) + return this.agent } async createKey(): Promise { - throw new Error('Not supported') + throw new Error(`Not supported`) } async getKey(kid: string) { - return await this.agent.keyManagerGet({ kid }) + return Veramo.instance.getKey(this.initAgent(), kid) } async createDid(): Promise { @@ -123,45 +104,27 @@ export class LocalIdentity implements IIdentity { } async resolveDid(did: string) { - return await this.agent.resolveDid({ didUrl: did }) + return Veramo.instance.resolveDid(this.initAgent(), did) } async getDid(did: string) { - return await this.agent.didManagerGet({ did }) + return Veramo.instance.getDid(this.initAgent(), did) } async importDid(): Promise { - if (!this.agent) throw new Error('No initialised agent found.') if (!(ISSUER_DID && ISSUER_ID_PUBLIC_KEY_HEX && ISSUER_ID_PRIVATE_KEY_HEX)) throw new Error('No DIDs and Keys found') - - const [kms] = await this.agent.keyManagerGetKeyManagementSystems() - - if (!ISSUER_DID.match(cheqdDidRegex)) { - throw new Error('Invalid DID') + try { + return await this.getDid(ISSUER_DID) + } catch { + const identifier: IIdentifier = await Veramo.instance.importDid(this.initAgent(), ISSUER_DID, ISSUER_ID_PRIVATE_KEY_HEX, ISSUER_ID_PUBLIC_KEY_HEX) + return identifier } - - const key: MinimalImportableKey = { kms: kms, type: 'Ed25519', kid: ISSUER_ID_PUBLIC_KEY_HEX, privateKeyHex: ISSUER_ID_PRIVATE_KEY_HEX, publicKeyHex: ISSUER_ID_PUBLIC_KEY_HEX } - - const identifier: IIdentifier = await this.agent.didManagerImport({ keys: [key], did: ISSUER_DID, controllerKeyId: key.kid } as MinimalImportableIdentifier) - - return identifier } async createResource(network: string, payload: ResourcePayload) { try { - // import DID await this.importDid() - if (!this.agent) throw new Error('No initialised agent found.') - - const [kms] = await this.agent.keyManagerGetKeyManagementSystems() - - const result: boolean = await this.agent.cheqdCreateLinkedResource({ - kms, - payload, - network: network as CheqdNetwork - } - ) - return result + return await Veramo.instance.createResource(this.initAgent(), network, payload) } catch (error) { throw new Error(`${error}`) } @@ -169,27 +132,18 @@ export class LocalIdentity implements IIdentity { async createCredential(credential: CredentialPayload, format: CredentialRequest['format']): Promise { try { - // import DID await this.importDid() - const verifiable_credential = await this.agent.createVerifiableCredential( - { - save: false, - credential, - proofFormat: format == 'jsonld' ? 'lds' : VC_PROOF_FORMAT, - removeOriginalFields: VC_REMOVE_ORIGINAL_FIELDS - } - ) - return verifiable_credential + return await Veramo.instance.createCredential(this.initAgent(), credential, format) } catch (error) { throw new Error(`${error}`) } } async verifyCredential(credential: VerifiableCredential | string): Promise { - return await this.agent.verifyCredential({ credential, fetchRemoteContexts: true }) + return await Veramo.instance.verifyCredential(this.initAgent(), credential) } async verifyPresentation(presentation: VerifiablePresentation | string): Promise { - return await this.agent.verifyPresentation({ presentation, fetchRemoteContexts: true }) + return await Veramo.instance.verifyPresentation(this.initAgent(), presentation) } } diff --git a/src/services/identity/postgres.ts b/src/services/identity/postgres.ts index 8c9ed6e9..5555faf5 100644 --- a/src/services/identity/postgres.ts +++ b/src/services/identity/postgres.ts @@ -1,11 +1,12 @@ import { - CredentialPayload, + CredentialPayload, DIDDocument, + IDIDManager, IIdentifier, + IKeyManager, + IResolver, IVerifyResult, ManagedKeyInfo, - MinimalImportableIdentifier, - MinimalImportableKey, TAgent, VerifiableCredential, VerifiablePresentation, @@ -21,16 +22,16 @@ import { CredentialIssuerLD, LdDefaultContexts, VeramoEd25519Signature2018 } fro import { CheqdDIDProvider, getResolver as CheqdDidResolver, ResourcePayload, Cheqd } from '@cheqd/did-provider-cheqd' import { CheqdNetwork } from '@cheqd/sdk' import { Resolver, ResolverRegistry } from 'did-resolver' -import { v4 } from 'uuid' import { cheqdDidRegex, CredentialRequest, DefaultRPCUrl, VeramoAgent } from '../../types/types.js' import { Connection } from '../../database/connection/connection.js' import { CustomerEntity } from '../../database/entities/customer.entity.js' import { CustomerService } from '../customer.js' -import * as dotenv from 'dotenv' import { IIdentity } from './IIdentity.js' -import { VC_PROOF_FORMAT, VC_REMOVE_ORIGINAL_FIELDS } from '../../types/constants.js' + +import * as dotenv from 'dotenv' +import { Veramo } from './agent.js' dotenv.config() const { @@ -41,40 +42,31 @@ const { } = process.env export class PostgresIdentity implements IIdentity { - agent: TAgent + agent: TAgent privateStore?: AbstractPrivateKeyStore public static instance = new PostgresIdentity() - + constructor() { this.agent = this.initAgent() } - initAgent(): TAgent { + initAgent(): TAgent { + if(this.agent) return this.agent const dbConnection = Connection.instance.dbConnection this.privateStore = new PrivateKeyStore(dbConnection, new SecretBox(EXTERNAL_DB_ENCRYPTION_KEY)) - return createAgent({ - plugins: [ - new KeyManager({ - store: new KeyStore(dbConnection), - kms: { - postgres: new KeyManagementSystem( - this.privateStore - ) - } - }), - new DIDManager({ - store: new DIDStore(dbConnection), - defaultProvider: 'did:cheqd:testnet', - providers: {} - }), - new DIDResolverPlugin({ - resolver: new Resolver({ - ...CheqdDidResolver({ url: RESOLVER_URL }) as ResolverRegistry - }) - }), - ] + this.agent = Veramo.instance.createVeramoAgent({ + dbConnection, + kms: { + local: new KeyManagementSystem( + this.privateStore + ) + }, + providers: {}, + enableCredential: false, + enableResolver: true }) + return this.agent } async createAgent(agentId: string) : Promise { @@ -103,49 +95,26 @@ export class PostgresIdentity implements IIdentity { } ) - return createAgent({ - plugins: [ - new KeyManager({ - store: new KeyStore(dbConnection), - kms: { - postgres: new KeyManagementSystem( - this.privateStore - ) - } - }), - new DIDManager({ - store: new DIDStore(dbConnection), - defaultProvider: 'did:cheqd:testnet', - providers: { - 'did:cheqd:mainnet': mainnetProvider, - 'did:cheqd:testnet': testnetProvider - } - }), - new DIDResolverPlugin({ - resolver: new Resolver({ - ...CheqdDidResolver({ url: RESOLVER_URL }) as ResolverRegistry - }) - }), - new CredentialPlugin(), - new CredentialIssuerLD({ - contextMaps: [LdDefaultContexts], - suites: [new VeramoEd25519Signature2018()] - }), - new Cheqd({ - providers: [mainnetProvider, testnetProvider] - }) - ] + return Veramo.instance.createVeramoAgent({ + dbConnection, + kms: { + local: new KeyManagementSystem( + this.privateStore + ) + }, + providers: { + 'did:cheqd:mainnet': mainnetProvider, + 'did:cheqd:testnet': testnetProvider + }, + cheqdProviders: [mainnetProvider, testnetProvider], + enableCredential: true, + enableResolver: true }) } async createKey(type: 'Ed25519' | 'Secp256k1'='Ed25519', agentId: string): Promise { - if (!this.agent) throw new Error('No initialised agent found.') - const [kms] = await this.agent.keyManagerGetKeyManagementSystems() - const key = await this.agent.keyManagerCreate({ - type: type || 'Ed25519', - kms, - }) - await CustomerService.instance.update(agentId, { kids: [key.kid] }) + const key = await Veramo.instance.createKey(this.agent, type) + if(await CustomerService.instance.find(agentId, {})) await CustomerService.instance.update(agentId, { kids: [key.kid] }) return key } @@ -154,7 +123,7 @@ export class PostgresIdentity implements IIdentity { if(!isOwner) { throw new Error(`Customer not found`) } - return await this.agent.keyManagerGet({ kid }) + return await Veramo.instance.getKey(this.agent, kid) } private async getPrivateKey(kid: string) { @@ -166,15 +135,7 @@ export class PostgresIdentity implements IIdentity { const agent = await this.createAgent(agentId) if (!agent) throw new Error('No initialised agent found.') - const [kms] = await agent.keyManagerGetKeyManagementSystems() - - const identifier: IIdentifier = await agent.didManagerCreate({ - provider: `did:cheqd:${network}`, - kms, - options: { - document: didDocument - } - }) + const identifier: IIdentifier = await Veramo.instance.createDid(agent, network, didDocument) await CustomerService.instance.update(agentId, { dids: [identifier.did] }) return identifier } catch (error) { @@ -188,42 +149,27 @@ export class PostgresIdentity implements IIdentity { } async resolveDid(did: string) { - return await this.agent.resolveDid({ didUrl: did }) + return await Veramo.instance.resolveDid(this.agent, did) } async getDid(did: string) { - return await this.agent.didManagerGet({ did }) + return await Veramo.instance.getDid(this.agent, did) } - async importDid(did: string, privateKeyHex: string, publicKeyHex: string): Promise { - if (!this.agent) throw new Error('No initialised agent found.') - - const [kms] = await this.agent.keyManagerGetKeyManagementSystems() - + async importDid(did: string, privateKeyHex: string, publicKeyHex: string, agentId: string): Promise { if (!did.match(cheqdDidRegex)) { throw new Error('Invalid DID') } - const key: MinimalImportableKey = { kms: kms, type: 'Ed25519', kid: v4(), privateKeyHex, publicKeyHex } - - const identifier: IIdentifier = await this.agent.didManagerImport({ keys: [key], did, controllerKeyId: key.kid } as MinimalImportableIdentifier) - + const identifier: IIdentifier = await Veramo.instance.importDid(this.agent, did, privateKeyHex, publicKeyHex) + await CustomerService.instance.update(agentId, { dids: [identifier.did]}) return identifier } async createResource(network: string, payload: ResourcePayload, agentId: string) { try { const agent = await this.createAgent(agentId) - if (!agent) throw new Error('No initialised agent found.') - - const [kms] = await agent.keyManagerGetKeyManagementSystems() - - const result: boolean = await agent.cheqdCreateLinkedResource({ - kms, - payload, - network: network as CheqdNetwork - }) - return result + return await Veramo.instance.createResource(agent, network, payload) } catch (error) { throw new Error(`${error}`) } @@ -236,15 +182,7 @@ export class PostgresIdentity implements IIdentity { throw new Error('Customer not found') } const agent = await this.createAgent(agentId) - const verifiable_credential = await agent.createVerifiableCredential( - { - save: false, - credential, - proofFormat: format == 'jsonld' ? 'lds' : VC_PROOF_FORMAT, - removeOriginalFields: VC_REMOVE_ORIGINAL_FIELDS - } - ) - return verifiable_credential + return await Veramo.instance.createCredential(agent, credential, format) } catch (error) { throw new Error(`${error}`) } @@ -252,11 +190,11 @@ export class PostgresIdentity implements IIdentity { async verifyCredential(credential: string | VerifiableCredential, agentId: string): Promise { const agent = await this.createAgent(agentId) - return await agent.verifyCredential({ credential, fetchRemoteContexts: true }) + return await Veramo.instance.verifyCredential(agent, credential) } async verifyPresentation(presentation: VerifiablePresentation | string, agentId: string): Promise { const agent = await this.createAgent(agentId) - return await agent.verifyPresentation({ presentation, fetchRemoteContexts: true }) + return await Veramo.instance.verifyPresentation(agent, presentation) } } diff --git a/src/services/store.ts b/src/services/store.ts index fb537b92..396404e7 100644 --- a/src/services/store.ts +++ b/src/services/store.ts @@ -6,7 +6,7 @@ export class LocalStore { public static instance = new LocalStore() constructor() { - this.cache = new NodeCache(); + this.cache = new NodeCache() } setItem(data: string) : string { diff --git a/src/types/types.ts b/src/types/types.ts index 98bdd7ed..378028e4 100644 --- a/src/types/types.ts +++ b/src/types/types.ts @@ -1,4 +1,3 @@ -import { ContextType } from '@veramo/core-types' import { IDIDManager, IKeyManager, @@ -11,6 +10,10 @@ import { } from '@veramo/core' import { ICheqd } from '@cheqd/did-provider-cheqd/build/types/agent/ICheqd' import { ICredentialIssuerLD } from '@veramo/credential-ld' +import { AbstractIdentifierProvider } from '@veramo/did-manager' +import { AbstractKeyManagementSystem } from '@veramo/key-manager' +import { DataSource } from 'typeorm' +import { CheqdDIDProvider } from '@cheqd/did-provider-cheqd' export type ErrorResponse = { name: string @@ -89,4 +92,13 @@ IResolver & ICredentialIssuer & ICredentialVerifier & ICheqd & -ICredentialIssuerLD> \ No newline at end of file +ICredentialIssuerLD> + +export type CreateAgentRequest = { + providers?: Record, + kms?: Record, + dbConnection: DataSource, + cheqdProviders?: CheqdDIDProvider[], + enableResolver?: boolean, + enableCredential?: boolean +} \ No newline at end of file diff --git a/swagger.json b/swagger.json index aa6edf72..1b858229 100644 --- a/swagger.json +++ b/swagger.json @@ -873,7 +873,7 @@ "name": "Bob" }, "@context": [ - "https://schema.org/Person" + "https://schema.org" ], "type": [ "Person"