feat: Expose identity provider managers. (#75)

* feat: Expose identity provider managers.

* refactor: Remove unnecessary provider tools.

* refactor: Match AC management to other management mechanisms (e.g. Identity Providers).
This commit is contained in:
Hayden Young 2023-06-02 02:53:40 +08:00 committed by GitHub
parent 794136c762
commit b9e573dc6d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 210 additions and 150 deletions

View File

@ -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
}

View File

@ -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
}

View File

@ -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'

View File

@ -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'

View File

@ -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 }

View File

@ -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'

View File

@ -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 }

48
test/fixtures/providers.js vendored Normal file
View File

@ -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 }

View File

@ -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 })
})
})
})

View File

@ -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)
})
})
})