UNPKG

ula-vc-data-management

Version:
231 lines (213 loc) 8.86 kB
/* * Copyright 2020 Coöperatieve Rabobank U.A. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import { Attestation, Attestor, EventHandler, IAttestation, Message, Plugin, Transaction, UlaResponse } from 'universal-ledger-agent' import { VCRepository } from './interface/vc-repository' import { AddressRepository } from './repository/address-repository' import { Address } from './model/address' import { VerifiableCredential } from 'vp-toolkit-models' import { VcTransaction } from './model/vc-transaction' import { VCTransactionRepository } from './interface/vc-transaction-repository' export class VcDataManagement implements Plugin { private _eventHandler?: EventHandler private readonly _listeningToTypes = [ 'save-vcs', 'get-vcs-by-context', 'get-vcs-by-subject', 'save-address', 'save-vc-transaction', 'get-address-details', 'get-attestations', 'get-attestors', 'get-new-key-id', 'data-clear-all', 'get-transactions' ] constructor (private _verifiableCredentialRepo: VCRepository, private _addressRepo: AddressRepository, private _vcTransactionRepo: VCTransactionRepository) { } get name () { return 'VerifiableCredentialDataManagement' } initialize (eventHandler: EventHandler) { this._eventHandler = eventHandler } async handleEvent (message: Message, callback: any): Promise<string> { if (!this._eventHandler && this._listeningToTypes.includes(message.properties.type)) { throw new Error('Plugin not initialized. Did you forget to call initialize() ?') } if (message.properties.type === 'save-vcs') { await this._verifiableCredentialRepo.saveMultiple(message.properties.verifiableCredentials) return 'success' } else if (message.properties.type === 'get-vcs-by-context') { const dataResult = await this._verifiableCredentialRepo.findByContext(message.properties.contextRegex) callback(dataResult) return 'success' } else if (message.properties.type === 'get-vcs-by-subject') { const dataResult = await this._verifiableCredentialRepo.findByCredentialSubject(message.properties.contextRegex) callback(dataResult) return 'success' } else if (message.properties.type === 'save-address') { await this._addressRepo.saveOne(new Address(message.properties.address)) return 'success' } else if (message.properties.type === 'save-vc-transaction') { await this._vcTransactionRepo.saveOne(new VcTransaction(message.properties.transaction)) return 'success' } else if (message.properties.type === 'get-address-details') { const dataResult = await this._addressRepo.findOneByPubAddress(message.properties.publicAddress) callback(dataResult) return 'success' } else if (message.properties.type === 'get-new-key-id') { const allAddresses = await this._addressRepo.findAll() let highestKeyId = 0 for (const address of allAddresses) { highestKeyId = Math.max(address.keyId, highestKeyId) } callback(highestKeyId + 1) return 'success' } else if (message.properties.type === 'get-attestations') { const dataResult = await this._verifiableCredentialRepo.findAll() const attestations: Attestation[] = [] for (const credential of dataResult) { attestations.push(new Attestation(this.transformCredentialToIAttestation(credential))) } callback(new UlaResponse({ statusCode: 200, body: attestations })) return 'success' } else if (message.properties.type === 'get-attestors') { const attestors: Attestor[] = [] const dataResult = await this._verifiableCredentialRepo.findAll() for (const verifiableCredential of dataResult) { const attestor = await this.extractAttestorFromVerifiableCredential(verifiableCredential) if (attestors.every((ar) => ar.pubKey !== attestor.pubKey)) { attestors.push(attestor) } } callback(new UlaResponse({ statusCode: 200, body: attestors })) return 'success' } else if (message.properties.type === 'data-clear-all') { await this._verifiableCredentialRepo.clearAll() await this._addressRepo.clearAll() return 'success' } else if (message.properties.type === 'get-transactions') { const vcTransactions = await this._vcTransactionRepo.findAll() const transactions = await this.transformVcTransactionsToTransactions(vcTransactions) callback(new UlaResponse({ statusCode: 200, body: transactions })) return 'success' } return 'ignored' } /** * Will extract attestor information from the * given VerifiableCredential. * * @param {VerifiableCredential} vc * @return {Attestor} */ private async extractAttestorFromVerifiableCredential (vc: VerifiableCredential): Promise<Attestor> { const credentials = await this._verifiableCredentialRepo.findByIssuer(vc.issuer) const attestations: Attestation[] = [] for (const credential of credentials) { attestations.push(new Attestation(this.transformCredentialToIAttestation(credential))) } return new Attestor({ name: vc.additionalFields['issuerName'] ? vc.additionalFields['issuerName'] : 'Unknown', pubKey: vc.proof.verificationMethod, datetime: vc.issuanceDate, icon: vc.additionalFields['issuerIcon'] ? vc.additionalFields['issuerIcon'] : 'Unknown', transactions: [], receivedAttestations: [], issuedAttestations: attestations }) } /** * Takes an array of VerifiableCredential * objects and maps them to the Attestor * data model so it can be displayed to * the holder. * * @todo: Switch attestorPubKey to credential.issuer after ChallengeRequest contains the issuer DID as well * @param {VerifiableCredential} credential * @return {Attestation} */ private transformCredentialToIAttestation (credential: VerifiableCredential): IAttestation { const statements = this.transformCredentialSubjectsToStatements(credential.credentialSubject) return { uuid: credential.proof.nonce, attestorPubKey: credential.proof.verificationMethod, forPubKey: credential.credentialSubject.id, type: [credential.proof.type], datetime: credential.issuanceDate, statements: statements, context: credential.context || ['https://www.w3.org/2018/credentials/v1'] } } /** * Transforms CredentialSubjects to * statements by leaving out the 'id' * field and only using the predicate * without the entire URL, * so 'http://schema.org/test/fullName' * will become 'fullName'. * * @param credentialSubject * @return {any} */ private transformCredentialSubjectsToStatements (credentialSubject: any): any { const statements: any = {} for (const csKey of Object.keys(credentialSubject)) { const splittedKey = csKey.split('/') const keyWithoutUrl = splittedKey[splittedKey.length - 1] statements[keyWithoutUrl] = credentialSubject[csKey] } delete statements.id return statements } /** * Transforms VC Transactions to ULA Transactions * * @param vcTransactions * @return {Transaction[]} */ private async transformVcTransactionsToTransactions (vcTransactions: VcTransaction[]): Promise<Transaction[]> { const transactions: Transaction[] = [] for (const vcTransaction of vcTransactions) { const attestedVcs: VerifiableCredential[] = [] const verifiedVcs: VerifiableCredential[] = [] for (const issuedVc of vcTransaction.issuedVcs) { attestedVcs.push(await this._verifiableCredentialRepo.findOneByNonce(issuedVc)) } for (const verifiedVc of vcTransaction.verifiedVcs) { verifiedVcs.push(await this._verifiableCredentialRepo.findOneByNonce(verifiedVc)) } transactions.push(new Transaction({ uuid: vcTransaction.uuid, attestorPubKey: vcTransaction.counterpartyId, datetime: vcTransaction.created, attest: attestedVcs.map(vc => this.transformCredentialToIAttestation(vc)), verifyRequest: verifiedVcs.map(vc => this.transformCredentialToIAttestation(vc)), revoke: vcTransaction.revokedVcs.map(vc => this.transformCredentialToIAttestation(vc)) })) } return transactions } }