diff --git a/src/access-controllers/index.js b/src/access-controllers/index.js index d5650c4..7a47273 100644 --- a/src/access-controllers/index.js +++ b/src/access-controllers/index.js @@ -21,12 +21,12 @@ import IPFSAccessController from './ipfs.js' import OrbitDBAccessController from './orbitdb.js' /** - * An array of available access controller types. - * @name types + * An array of available access controllers. + * @name accessControllers * @†ype [] - * @return [] An array of access controller types. + * @return [] An array of access controllers. */ -const types = { +const accessControllers = { ipfs: IPFSAccessController, orbitdb: OrbitDBAccessController } @@ -36,25 +36,15 @@ const types = { * @param {string} type A valid access controller type. * @return {AccessController} The access controller module. */ -const get = (type) => { - if (!isSupported(type)) { +const getAccessController = (type) => { + if (!Object.keys(accessControllers).includes(type)) { throw new Error(`AccessController type '${type}' is not supported`) } - return types[type] + return accessControllers[type] } /** - * Checks whether the access controller exists. - * @param {string} type A valid access controller type. - * @return {boolean} True if the access controller exists, false otherwise. - */ -const isSupported = type => { - return Object.keys(types).includes(type) -} - -/** - * Adds an access controller module to the list of supported access controller - * types. + * Adds an access controller module to the list of supported access controller. * @param {AccessController} accessController A compatible access controller * module. * @throws Access controller `type` already added if the access controller is @@ -62,8 +52,8 @@ const isSupported = type => { * @throws Given AccessController class needs to implement: type if the access * controller module does not implement a type property. */ -const add = (accessController) => { - if (types[accessController.type]) { +const addAccessController = (accessController) => { + if (accessControllers[accessController.type]) { throw new Error(`Access controller '${accessController.type}' already added.`) } @@ -71,21 +61,20 @@ const add = (accessController) => { throw new Error('Given AccessController class needs to implement: type.') } - types[accessController.type] = accessController + accessControllers[accessController.type] = accessController } /** - * Removes an access controller from the types list. + * Removes an access controller from the list. * @param {string} type A valid access controller type. */ -const remove = type => { - delete types[type] +const removeAccessController = type => { + delete accessControllers[type] } export { - types, - get, - isSupported, - add, - remove + accessControllers, + getAccessController, + addAccessController, + removeAccessController } diff --git a/src/identities/identities.js b/src/identities/identities.js index 024711e..ea96511 100644 --- a/src/identities/identities.js +++ b/src/identities/identities.js @@ -4,9 +4,8 @@ * Identities provides a framework for generating and managing identity * details and providers. */ - import Identity, { isIdentity, isEqual, decodeIdentity } from './identity.js' -import { PublicKeyIdentityProvider } from './providers/index.js' +import { getProviderFor } from './providers/index.js' // import DIDIdentityProvider from './identity-providers/did.js' // import EthIdentityProvider from './identity-providers/ethereum.js' import KeyStore, { signMessage, verifyMessage } from '../key-store.js' @@ -14,13 +13,8 @@ import { LRUStorage, IPFSBlockStorage, MemoryStorage, ComposedStorage } from '.. import pathJoin from '../utils/path-join.js' const DefaultProviderType = 'publickey' -const DefaultIdentityKeysPath = pathJoin('./orbitdb', 'identities') -const supportedTypes = { - publickey: PublicKeyIdentityProvider - // [DIDIdentityProvider.type]: DIDIdentityProvider, - // [EthIdentityProvider.type]: EthIdentityProvider -} +const DefaultIdentityKeysPath = pathJoin('./orbitdb', 'identities') /** * Creates an instance of Identities. @@ -167,68 +161,6 @@ const Identities = async ({ keystore, path, storage, ipfs } = {}) => { } } -/** - * Checks whether an identity provider is supported. - * @param {string} type The identity provider type. - * @return {boolean} True if the identity provider is supported, false - * otherwise. - * @static - */ -const isProviderSupported = (type) => { - return Object.keys(supportedTypes).includes(type) -} - -/** - * Gets an identity provider. - * @param {string} type The identity provider type. - * @return {IdentityProvider} The IdentityProvider module corresponding to - * type. - * @throws IdentityProvider type is not supported if the identity provider is - * not supported. - * @static - */ -const getProviderFor = (type) => { - if (!isProviderSupported(type)) { - throw new Error(`IdentityProvider type '${type}' is not supported`) - } - - return supportedTypes[type] -} - -/** - * Adds an identity provider. - * @param {IdentityProvider} IdentityProvider The identity provider to add. - * @throws IdentityProvider must be given as an argument if no module is - * provided. - * @throws 'Given IdentityProvider doesn't have a field 'type' if the - * IdentityProvider does not include a type property. - * @static - */ -const addIdentityProvider = (IdentityProvider) => { - if (!IdentityProvider) { - throw new Error('IdentityProvider must be given as an argument') - } - - if (!IdentityProvider.type || - typeof IdentityProvider.type !== 'string') { - throw new Error('Given IdentityProvider doesn\'t have a field \'type\'') - } - - supportedTypes[IdentityProvider.type] = IdentityProvider -} - -/** - * Removes an identity provider. - * @param {string} type The identity provider type. - * @static - */ -const removeIdentityProvider = (type) => { - delete supportedTypes[type] -} - export { - Identities as default, - isProviderSupported, - addIdentityProvider, - removeIdentityProvider + Identities as default } diff --git a/src/identities/identity.js b/src/identities/identity.js index f57066d..32db82b 100644 --- a/src/identities/identity.js +++ b/src/identities/identity.js @@ -3,7 +3,6 @@ * @description * An identity. */ - import * as Block from 'multiformats/block' import * as dagCbor from '@ipld/dag-cbor' import { sha256 } from 'multiformats/hashes/sha2' diff --git a/src/identities/index.js b/src/identities/index.js index e2b6610..4ed6ab8 100644 --- a/src/identities/index.js +++ b/src/identities/index.js @@ -1,9 +1,4 @@ -export { - default as Identities, - addIdentityProvider, - removeIdentityProvider, - isProviderSupported -} from './identities.js' +export { default as Identities } from './identities.js' export { default as Identity, @@ -11,4 +6,8 @@ export { isEqual } from './identity.js' -export { PublicKeyIdentityProvider } from './providers/index.js' +export { + PublicKeyIdentityProvider, + addIdentityProvider, + identityProviders +} from './providers/index.js' diff --git a/src/identities/providers/index.js b/src/identities/providers/index.js index 35620ef..cb140d7 100644 --- a/src/identities/providers/index.js +++ b/src/identities/providers/index.js @@ -46,7 +46,68 @@ * * where my-custom-identity-provider is the custom module. */ +import * as PublicKeyIdentityProvider from './publickey.js' + +const identityProviders = { + publickey: PublicKeyIdentityProvider + // [DIDIdentityProvider.type]: DIDIdentityProvider, + // [EthIdentityProvider.type]: EthIdentityProvider +} + +/** + * Checks whether an identity provider is supported. + * @param {string} type The identity provider type. + * @return {boolean} True if the identity provider is supported, false + * otherwise. + * @static + */ +const isProviderSupported = (type) => { + return Object.keys(identityProviders).includes(type) +} + +/** + * Gets an identity provider. + * @param {string} type The identity provider type. + * @return {IdentityProvider} The IdentityProvider module corresponding to + * type. + * @throws IdentityProvider type is not supported if the identity provider is + * not supported. + * @static + */ +const getProviderFor = (type) => { + if (!isProviderSupported(type)) { + throw new Error(`IdentityProvider type '${type}' is not supported`) + } + + return identityProviders[type] +} + +/** + * Adds an identity provider. + * @param {IdentityProvider} IdentityProvider The identity provider to add. + * @throws IdentityProvider must be given as an argument if no module is + * provided. + * @throws 'Given IdentityProvider doesn't have a field 'type' if the + * IdentityProvider does not include a type property. + * @static + */ +const addIdentityProvider = (IdentityProvider) => { + if (!IdentityProvider) { + throw new Error('IdentityProvider must be given as an argument') + } + + if (!IdentityProvider.type || + typeof IdentityProvider.type !== 'string') { + throw new Error('Given IdentityProvider doesn\'t have a field \'type\'') + } + + if (identityProviders[IdentityProvider.type]) { + throw new Error(`Type already added: ${IdentityProvider.type}`) + } + + identityProviders[IdentityProvider.type] = IdentityProvider +} // export { default as DIDIdentityProvider } from './did.js' // export { default as EthIdentityProvider } from './ethereum.js' -export * as PublicKeyIdentityProvider from './publickey.js' +export { identityProviders, addIdentityProvider, getProviderFor, PublicKeyIdentityProvider } diff --git a/src/index.js b/src/index.js index 0c5f00f..8de08df 100644 --- a/src/index.js +++ b/src/index.js @@ -1,8 +1,39 @@ -export { default as OrbitDB } from './orbitdb.js' -export { databaseTypes, addDatabaseType } from './orbitdb.js' -export { default as OrbitDBAddress, isValidAddress, parseAddress } from './address.js' +export { + default as OrbitDB, + databaseTypes, + addDatabaseType +} from './orbitdb.js' + +export { + default as OrbitDBAddress, + isValidAddress, + parseAddress +} from './address.js' + export { Log, Entry, DefaultAccessController } from './oplog/index.js' + export { default as Database } from './database.js' + export { default as KeyStore } from './key-store.js' -export { Identities, isIdentity } from './identities/index.js' -export { IPFSBlockStorage, LevelStorage, LRUStorage, MemoryStorage, ComposedStorage } from './storage/index.js' + +export { + addAccessController, + removeAccessController, + getAccessController, + accessControllers +} from './access-controllers/index.js' + +export { + Identities, + isIdentity, + identityProviders, + addIdentityProvider +} from './identities/index.js' + +export { + IPFSBlockStorage, + LevelStorage, + LRUStorage, + MemoryStorage, + ComposedStorage +} from './storage/index.js' diff --git a/src/orbitdb.js b/src/orbitdb.js index 0bad08d..b7faf8c 100644 --- a/src/orbitdb.js +++ b/src/orbitdb.js @@ -35,7 +35,7 @@ import OrbitDBAddress, { isValidAddress } from './address.js' import Manifests from './manifest.js' import { createId } from './utils/index.js' import pathJoin from './utils/path-join.js' -import * as AccessControllers from './access-controllers/index.js' +import { getAccessController } from './access-controllers/index.js' import IPFSAccessController from './access-controllers/ipfs.js' /** @@ -168,7 +168,7 @@ const OrbitDB = async ({ ipfs, id, identity, keystore, directory } = {}) => { manifest = await manifests.get(addr.path) const acType = manifest.accessController.split('/', 2).pop() const acAddress = manifest.accessController.replaceAll(`/${acType}/`, '') - AccessController = AccessControllers.get(acType)() + AccessController = getAccessController(acType)() accessController = await AccessController({ orbitdb: { open, identity, ipfs }, identities, address: acAddress }) name = manifest.name type = type || manifest.type @@ -234,4 +234,4 @@ const OrbitDB = async ({ ipfs, id, identity, keystore, directory } = {}) => { } } -export { OrbitDB as default, OrbitDBAddress, addDatabaseType, databaseTypes, AccessControllers } +export { OrbitDB as default, OrbitDBAddress, addDatabaseType, databaseTypes } diff --git a/test/fixtures/providers.js b/test/fixtures/providers.js new file mode 100644 index 0000000..2ceca4b --- /dev/null +++ b/test/fixtures/providers.js @@ -0,0 +1,48 @@ +const customIdentityProvider = () => { + const verifyIdentity = async (data) => { return true } + + const CustomIdentityProvider = () => { + const getId = () => { return 'custom' } + + const signIdentity = (data) => { return `signature '${data}'` } + + return { + getId, + signIdentity, + type: 'custom' + } + } + + return { + default: CustomIdentityProvider, + type: 'custom', + verifyIdentity + } +} + +const fakeIdentityProvider = () => { + const verifyIdentity = async (data) => { return false } + + const FakeIdentityProvider = () => { + const getId = () => { return 'pubKey' } + + const signIdentity = (data) => { return `false signature '${data}'` } + + return { + getId, + signIdentity, + type: 'fake' + } + } + + return { + default: FakeIdentityProvider, + verifyIdentity, + type: 'fake' + } +} + +const CustomIdentityProvider = customIdentityProvider() +const FakeIdentityProvider = fakeIdentityProvider() + +export { CustomIdentityProvider, FakeIdentityProvider } \ No newline at end of file diff --git a/test/identities/identities.test.js b/test/identities/identities.test.js index 71f83a3..2b58584 100644 --- a/test/identities/identities.test.js +++ b/test/identities/identities.test.js @@ -3,8 +3,9 @@ import rmrf from 'rimraf' import { copy } from 'fs-extra' import { toString as uint8ArrayToString } from 'uint8arrays/to-string' import KeyStore, { signMessage, verifyMessage } from '../../src/key-store.js' -import { Identities, addIdentityProvider, Identity, PublicKeyIdentityProvider } from '../../src/identities/index.js' +import { Identities, identityProviders, addIdentityProvider, Identity, PublicKeyIdentityProvider } from '../../src/identities/index.js' import testKeysPath from '../fixtures/test-keys-path.js' +import { CustomIdentityProvider, FakeIdentityProvider } from '../fixtures/providers.js' const type = PublicKeyIdentityProvider.type const keysPath = './testkeys' @@ -14,6 +15,15 @@ describe('Identities', function () { await copy(testKeysPath, keysPath) }) + afterEach(async () => { + // reset the identityProviders. + for (const [key] of Object.entries(identityProviders)) { + if (key !== 'publickey') { + delete identityProviders[key] + } + } + }) + after(async () => { await rmrf(keysPath) }) @@ -213,30 +223,8 @@ describe('Identities', function () { }) it('false signature doesn\'t verify', async () => { - const IP = () => { - const verifyIdentity = async (data) => { return false } - - const FakeIdentityProvider = () => { - const getId = () => { return 'pubKey' } - - const signIdentity = (data) => { return `false signature '${data}'` } - - return { - getId, - signIdentity, - type: 'fake' - } - } - - return { - default: FakeIdentityProvider, - verifyIdentity, - type: 'fake' - } - } - - addIdentityProvider(IP()) - identity = await identities.createIdentity({ type: IP().type }) + addIdentityProvider(FakeIdentityProvider) + identity = await identities.createIdentity({ type: FakeIdentityProvider.type }) const verified = await identities.verifyIdentity(identity) assert.strictEqual(verified, false) }) @@ -345,4 +333,16 @@ describe('Identities', function () { assert.strictEqual(verified, false) }) }) + + describe('manage identity providers', () => { + it('has default identity providers', () => { + assert.deepStrictEqual(identityProviders, { publickey: PublicKeyIdentityProvider }) + }) + + it('can add an identity provider', () => { + addIdentityProvider(CustomIdentityProvider) + + assert.deepStrictEqual(identityProviders, { publickey: PublicKeyIdentityProvider, custom: CustomIdentityProvider }) + }) + }) }) diff --git a/test/orbitdb-access-controllers.test.js b/test/orbitdb-access-controllers.test.js index f6170a4..2b81131 100644 --- a/test/orbitdb-access-controllers.test.js +++ b/test/orbitdb-access-controllers.test.js @@ -1,7 +1,8 @@ import { strictEqual, deepStrictEqual, notStrictEqual } from 'assert' import rmrf from 'rimraf' import * as IPFS from 'ipfs-core' -import OrbitDB, { AccessControllers } from '../src/orbitdb.js' +import OrbitDB from '../src/orbitdb.js' +import { accessControllers, addAccessController, removeAccessController } from '../src/access-controllers/index.js' import config from './config.js' import pathJoin from '../src/utils/path-join.js' @@ -38,7 +39,7 @@ describe('Add a custom access controller', function () { } // Remove the added custom database type from OrbitDB import - AccessControllers.remove(type) + removeAccessController(type) await rmrf('./orbitdb') await rmrf('./ipfs1') @@ -51,7 +52,7 @@ describe('Add a custom access controller', function () { 'orbitdb' ] - deepStrictEqual(Object.keys(AccessControllers.types), expected) + deepStrictEqual(Object.keys(accessControllers), expected) }) it('throws and error if custom access controller hasn\'t been added', async () => { @@ -71,7 +72,7 @@ describe('Add a custom access controller', function () { describe('Custom access controller', function () { before(() => { - AccessControllers.add(CustomAccessController) + addAccessController(CustomAccessController) }) it('create a database with the custom access controller', async () => { @@ -83,7 +84,7 @@ describe('Add a custom access controller', function () { it('throws and error if custom access controller already exists', async () => { let err try { - AccessControllers.add(CustomAccessController) + addAccessController(CustomAccessController) } catch (e) { err = e.toString() } @@ -98,7 +99,7 @@ describe('Add a custom access controller', function () { type ] - deepStrictEqual(Object.keys(AccessControllers.types), expected) + deepStrictEqual(Object.keys(accessControllers), expected) }) it('can be removed from supported access controllers', async () => { @@ -107,9 +108,9 @@ describe('Add a custom access controller', function () { 'orbitdb' ] - AccessControllers.remove(type) + removeAccessController(type) - deepStrictEqual(Object.keys(AccessControllers.types), expected) + deepStrictEqual(Object.keys(accessControllers), expected) }) }) })