diff --git a/src/ocean/Ocean.ts b/src/ocean/Ocean.ts index 7b1a54d..39f7361 100644 --- a/src/ocean/Ocean.ts +++ b/src/ocean/Ocean.ts @@ -3,6 +3,7 @@ import deprecated from "deprecated-decorator" import OceanAccounts from "./OceanAccounts" import OceanAgreements from "./OceanAgreements" import OceanAssets from "./OceanAssets" +import OceanSecretStore from "./OceanSecretStore" import AquariusProvider from "../aquarius/AquariusProvider" import { SearchQuery } from "../aquarius/query/SearchQuery" @@ -38,6 +39,7 @@ export default class Ocean { Ocean.instance.accounts = await OceanAccounts.getInstance() Ocean.instance.assets = await OceanAssets.getInstance() Ocean.instance.agreements = await OceanAgreements.getInstance() + Ocean.instance.secretStore = await OceanSecretStore.getInstance() } return Ocean.instance @@ -67,6 +69,12 @@ export default class Ocean { */ public agreements: OceanAgreements + /** + * Ocean secretStore submodule + * @type {OceanSecretStore} + */ + public secretStore: OceanSecretStore + private constructor() { } @@ -106,7 +114,7 @@ export default class Ocean { * Registers a new DDO. * @deprecated Replace by [Ocean.assets.create]{@link #OceanAssets.create} * @param {MetaData} metadata DDO metadata. - * @param {Account} publisher Publicher account. + * @param {Account} publisher Publisher account. * @return {Promise} */ @deprecated("OceanAssets.create") diff --git a/src/ocean/OceanAssets.ts b/src/ocean/OceanAssets.ts index e995c01..2d0c654 100644 --- a/src/ocean/OceanAssets.ts +++ b/src/ocean/OceanAssets.ts @@ -9,9 +9,9 @@ import Keeper from "../keeper/Keeper" import SecretStoreProvider from "../secretstore/SecretStoreProvider" import Account from "./Account" import DID from "./DID" +import OceanAgreements from "./OceanAgreements" import ServiceAgreementTemplate from "./ServiceAgreements/ServiceAgreementTemplate" import Access from "./ServiceAgreements/Templates/Access" -import OceanAgreements from "./OceanAgreements" /** * Assets submodule of Ocean Protocol. @@ -49,7 +49,7 @@ export default class OceanAssets { /** * Creates a new DDO. * @param {MetaData} metadata DDO metadata. - * @param {Account} publisher Publicher account. + * @param {Account} publisher Publisher account. * @return {Promise} */ public async create(metadata: MetaData, publisher: Account, services: Service[] = []): Promise { diff --git a/src/ocean/OceanSecretStore.ts b/src/ocean/OceanSecretStore.ts new file mode 100644 index 0000000..5dc5400 --- /dev/null +++ b/src/ocean/OceanSecretStore.ts @@ -0,0 +1,64 @@ +import SecretStoreProvider from "../secretstore/SecretStoreProvider" +import Account from "./Account" + +/** + * SecretStore submodule of Ocean Protocol. + */ +export default class OceanSecretStore { + + /** + * Returns the instance of OceanSecretStore. + * @return {Promise} + */ + public static async getInstance(): Promise { + if (!OceanSecretStore.instance) { + OceanSecretStore.instance = new OceanSecretStore() + } + + return OceanSecretStore.instance + } + + /** + * OceanSecretStore instance. + * @type {OceanSecretStore} + */ + private static instance: OceanSecretStore = null + + /** + * Encrypt the given text and store the encryption keys using the `did`. + * The encrypted text can be decrypted using the same keys identified by the `did`. + * @param {string} did Decentralized ID. + * @param {string} content Content to be encrypted. + * @param {string} publisher Publisher account. + * @return {Promise} Encrypted text. + */ + public async encrypt(did: string, content: string, publisher: Account): Promise { + console.warn("TODO") + return await this.getSecretStoreByAccount(publisher) + // TODO did to id + .encryptDocument(did, content) + } + + /** + * Decrypt an encrypted text using the stored encryption keys associated with the `did`. + * Decryption requires that the account owner has access permissions for this `did` + * @param {string} did Decentralized ID. + * @param {string} content Content to be encrypted. + * @param {string} consumer cONSUMER account. + * @return {Promise} Encrypted text. + */ + public async decrypt(did: string, content: string, consumer: Account): Promise { + console.warn("TODO") + return await this.getSecretStoreByAccount(consumer) + // TODO did to id + .decryptDocument(did, content) + } + + private getSecretStoreByAccount(account: Account) { + const config: any = {address: account.getId()} + if (account.getPassword()) { + config.password = account.getPassword() + } + return SecretStoreProvider.getSecretStore(config) + } +} diff --git a/src/secretstore/SecretStoreProvider.ts b/src/secretstore/SecretStoreProvider.ts index beaa7f7..21c7368 100644 --- a/src/secretstore/SecretStoreProvider.ts +++ b/src/secretstore/SecretStoreProvider.ts @@ -1,6 +1,7 @@ import SecretStore from "@oceanprotocol/secret-store-client" import SecretStoreConfig from "@oceanprotocol/secret-store-client/dist/models/SecretStoreConfig" import ConfigProvider from "../ConfigProvider" +import Config from "../models/Config" export default class SecretStoreProvider { diff --git a/test/ocean/OceanSecretStore.test.ts b/test/ocean/OceanSecretStore.test.ts new file mode 100644 index 0000000..b9a2c3e --- /dev/null +++ b/test/ocean/OceanSecretStore.test.ts @@ -0,0 +1,69 @@ +import { assert, expect, spy, use } from "chai" +import * as spies from "chai-spies" + +import ConfigProvider from "../../src/ConfigProvider" +import Account from "../../src/ocean/Account" +import Ocean from "../../src/ocean/Ocean" +import OceanSecretStore from "../../src/ocean/OceanSecretStore" +import SecretStoreProvider from "../../src/secretstore/SecretStoreProvider" +import config from "../config" + +use(spies) + +describe("OceanSecretStore", () => { + + let oceanSecretStore: OceanSecretStore + let accounts: Account[] + + const did = `did:op:${"a".repeat(64)}` + + before(async () => { + oceanSecretStore = await OceanSecretStore.getInstance() + ConfigProvider.setConfig(config) + + const ocean = await Ocean.getInstance(config) + accounts = await ocean.getAccounts() + }) + + afterEach(() => { + spy.restore() + }) + + describe("#getInstance()", () => { + it("should get an instance of OceanSecretStore", async () => { + const oceanSecretStore: OceanSecretStore = await OceanSecretStore.getInstance() + + assert.instanceOf(oceanSecretStore, OceanSecretStore, "No returned OceanSecretStore instance") + }) + }) + + describe("#encrypt()", () => { + it("should encrypt a content", async () => { + const secretStoreToSpy = SecretStoreProvider.getSecretStore() + const secretStoreEncryptSpy = spy.on(secretStoreToSpy, "encryptDocument", () => "encryptedResult") + const secretStoreProviderGetInstanceSpy = spy.on(SecretStoreProvider, "getSecretStore", () => secretStoreToSpy) + + const result = await oceanSecretStore.encrypt(did, "test", accounts[0]) + + expect(secretStoreProviderGetInstanceSpy).to.have.been.called.with({address: accounts[0].getId()}) + expect(secretStoreEncryptSpy).to.have.been.called.with(did, "test") + + assert.equal(result, "encryptedResult", "Result doesn't match") + }) + }) + + describe("#decrypt()", () => { + it("should decrypt a content", async () => { + const secretStoreToSpy = SecretStoreProvider.getSecretStore() + const secretStoreEncryptSpy = spy.on(secretStoreToSpy, "decryptDocument", () => "decryptedResult") + const secretStoreProviderGetInstanceSpy = spy.on(SecretStoreProvider, "getSecretStore", () => secretStoreToSpy) + + const result = await oceanSecretStore.decrypt(did, "encryptedContent", accounts[0]) + + expect(secretStoreProviderGetInstanceSpy).to.have.been.called.with({address: accounts[0].getId()}) + expect(secretStoreEncryptSpy).to.have.been.called.with(did, "encryptedContent") + + assert.equal(result, "decryptedResult", "Result doesn't match") + }) + }) +}) diff --git a/test/ocean/ServiceAgreement.test.ts b/test/ocean/ServiceAgreement.test.ts index 30ba9aa..a362544 100644 --- a/test/ocean/ServiceAgreement.test.ts +++ b/test/ocean/ServiceAgreement.test.ts @@ -161,6 +161,8 @@ describe("ServiceAgreement", () => { assert(serviceAgreement) // get funds + // TODO: remove small delay to prevent failtures + await new Promise((resolve) => setTimeout(resolve, 100)) await consumerAccount.requestTokens(metaDataService.metadata.base.price) const paid: boolean = await serviceAgreement.payAsset(did.getId(), metaDataService.metadata.base.price,