Skip to content
This repository has been archived by the owner on Aug 9, 2021. It is now read-only.

(WIP) Feat/thread access controllers #435

Closed
wants to merge 10 commits into from
4 changes: 4 additions & 0 deletions example/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,10 @@ <h5 style="padding: 20px 0px 10px 0px" id="spacePriv"> </h5>
<h3> Threads: </h3>
<input type="text" id="threadName" placeholder="Join thread by name">
<button id="joinThread" type="button" class="btn btn btn-primary" >Join thread</button>

<input type="text" id="threadMod" placeholder="Add a thread Mod">
<button id="addThreadMod" type="button" class="btn btn btn-primary" >Add thread Mod</button>

<div id="posts" style="display: none;">
<h4> Posts: </h4>
<p>
Expand Down
19 changes: 17 additions & 2 deletions example/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -97,17 +97,32 @@ bopen.addEventListener('click', event => {
box.spaces[window.currentSpace].joinThread(name).then(thread => {
window.currentThread = thread
thread.onNewPost(post => {
threadData.innerHTML += post.author + ': <br />' + post.message + '<br /><br />'
updateThreadData()
})
updateThreadData()
})
})

addThreadMod.addEventListener('click', () => {
const name = threadMod.value
posts.style.display = 'block'
window.currentThread.addMod(name).then(res => {
console.log(res)
})
})

window.deletePost = (el) => {
window.currentThread.deletePost(el.id).then(res => {
updateThreadData()
})
}

const updateThreadData = () => {
threadData.innerHTML = ''
window.currentThread.getPosts().then(posts => {
posts.map(post => {
threadData.innerHTML += post.author + ': <br />' + post.message + '<br /><br />'
threadData.innerHTML += post.author + ': <br />' + post.message + '<br /><br />'
threadData.innerHTML += `<button id="` + post.postId + `"onClick="window.deletePost(` + post.postId + `)" type="button" class="btn btn btn-primary" >Delete</button>` + '<br /><br />'
})
})
}
Expand Down
12 changes: 9 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,19 +43,25 @@
},
"homepage": "https://github.com/3box/3box-js#readme",
"dependencies": {
"@babel/runtime": "^7.1.2",
"@babel/runtime": "^7.4.4",
"did-jwt": "^0.1.1",
"elliptic": "^6.4.1",
"ethers": "^4.0.20",
"graphql-request": "^1.8.2",
"https-did-resolver": "^0.1.0",
"ipfs": "^0.33.1",
"idb-readable-stream": "0.0.4",
"ipfs": "^0.34.4",
"ipfs-mini": "^1.1.5",
"ipfs-postmsg-proxy": "^3.1.1",
"js-sha256": "^0.9.0",
"muport-did-resolver": "^0.3.0-alpha.2",
"node-fetch": "^2.3.0",
"orbit-db": "git://github.com/orbitdb/orbit-db.git#dddb271",
"orbit-db": "git://github.com/3box/orbit-db.git#feat/legacy-create",
"orbit-db-access-controllers": "git://github.com/3box/orbit-db-access-controllers.git#feat/legacy-ac-support",
"orbit-db-cache-postmsg-proxy": "^0.1.1",
"orbit-db-identity-provider": "^0.1.0",
"orbit-db-io": "git://github.com/3box/orbit-db-io.git#feat/backwards-compatibility",
"orbit-db-pubsub": "^0.5.5",
"store": "^2.0.12",
"tweetnacl": "^1.0.1",
"tweetnacl-util": "^0.15.0"
Expand Down
26 changes: 19 additions & 7 deletions src/3box.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,13 @@ const idUtils = require('./utils/id')
const config = require('./config.js')
const API = require('./api')

let AccessControllers = require('orbit-db-access-controllers')
const ThreadAccessController = require('./access/thread-open-mod-access')
const ModeratorAccessController = require('./access/moderator-access')
AccessControllers.addAccessController({ AccessController: ThreadAccessController })
AccessControllers.addAccessController({ AccessController: ModeratorAccessController })


const ADDRESS_SERVER_URL = config.address_server_url
const PINNING_NODE = config.pinning_node
const PINNING_ROOM = config.pinning_room
Expand Down Expand Up @@ -78,18 +85,23 @@ class Box {
this.pinningNode = opts.pinningNode || PINNING_NODE
this._ipfs.swarm.connect(this.pinningNode, () => {})

const keyring = this._3id.getKeyringBySpaceName(rootStoreName)
const identity = await keyring.getIdentity()
const key = keyring.getDBKey()
// const cache = (opts.iframeStore && !!cacheProxy) ? cacheProxy : null
this._orbitdb = new OrbitDB(this._ipfs, opts.orbitPath) // , { cache })
// this._orbitdb = new OrbitDB(this._ipfs, identity, opts.orbitPath) // , { cache })
this._orbitdb = new OrbitDB(this._ipfs, identity, {
AccessControllers: AccessControllers
zachferland marked this conversation as resolved.
Show resolved Hide resolved
})
globalOrbitDB = this._orbitdb

const dbKey = this._3id.getKeyringBySpaceName(rootStoreName).getDBKey()
const key = await this._orbitdb.keystore.importPrivateKey(dbKey)
this._rootStore = await this._orbitdb.feed(rootStoreName, {
key,
write: [key.getPublic('hex')]
identity,
accessController: {
write: [key.getPublic('hex')],
legacy: true
}
})
const rootStoreAddress = this._rootStore.address.toString()

this._pubsub = new Pubsub(this._ipfs, (await this._ipfs.id()).id)

const onNewPeer = async (topic, peer) => {
Expand Down
2 changes: 1 addition & 1 deletion src/3id/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ class ThreeId {
async _initMuport (muportIpfs) {
let keys = this._mainKeyring.getPublicKeys()
const doc = createMuportDocument(keys.signingKey, this.managementAddress, keys.asymEncryptionKey)
let docHash = (await this._ipfs.files.add(Buffer.from(JSON.stringify(doc))))[0].hash
let docHash = (await this._ipfs.add(Buffer.from(JSON.stringify(doc))))[0].hash
this._muportDID = 'did:muport:' + docHash
this.muportFingerprint = utils.sha256Multihash(this._muportDID)
const publishToInfura = async () => {
Expand Down
13 changes: 12 additions & 1 deletion src/3id/keyring.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,12 @@ const nacl = require('tweetnacl')
nacl.util = require('tweetnacl-util')
const SimpleSigner = require('did-jwt').SimpleSigner
const { sha256 } = require('../utils/index')
const EC = require('elliptic').ec
const ec = new EC('secp256k1')
const IdentityProvider = require('./orbitProvider')
const Identities = require('orbit-db-identity-provider')

Identities.addIdentityProvider(IdentityProvider)

const BASE_PATH = "m/7696500'/0'/0'"
const MM_PATH = "m/44'/60'/0'/0"
Expand Down Expand Up @@ -58,7 +64,12 @@ class Keyring {
}

getDBKey () {
return this.signingKey.privateKey.slice(2)
return ec.keyFromPrivate(this.signingKey.privateKey.slice(2))
}

async getIdentity () {
zachferland marked this conversation as resolved.
Show resolved Hide resolved
const key = this.getDBKey()
return await Identities.createIdentity({ type: `3ID`, pubKey: key.getPublic('hex')})
}

getDBSalt () {
Expand Down
42 changes: 42 additions & 0 deletions src/3id/orbitProvider.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// const Identities = require('orbit-db-identity-provider')
const IdentityProvider = require('orbit-db-identity-provider/src/identity-provider-interface.js')


class OrbitIdentityProvider {
constructor (options={}) {
// super(options)
console.log('create new identity')
console.log(options)
this.pubKey = options.pubKey
console.log('pubkey')
console.log(this.pubKey)
}

static get type () { return '3ID' } // return type
// return identifier of external id (eg. a public key)
async getId () {
zachferland marked this conversation as resolved.
Show resolved Hide resolved
return this.pubKey
}
//return a signature of data (signature of the OrbtiDB public key)
async signIdentity (data) {
return 'signedstring'
}

//return true if identity.sigantures are valid
static async verifyIdentity (identity) {
console.log(identity)
return true
}
}


module.exports = OrbitIdentityProvider

// Identities.addIdentityProvider(MyIdentityProvider)

// to create an identity of type `MyIdentityType`
// const identity = await Identities.createIdentity({ type: `MyIdentityType`})

// module.exports = (pubKey) => {
// return new OrbitProvider(pubkey )
// }
65 changes: 65 additions & 0 deletions src/access/moderator-access.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// 'use strict'
const AccessController = require('orbit-db-access-controllers/src/access-controller-interface')
const type = 'moderator-access'

const moderator = 'moderator'
const member = 'member'

class ModeratorAccessController {
constructor (options) {
this._capabilityType = {}
this._capabilityType[moderator] = moderator
this._write = [] // Allowed to add other mods or members
this._rootMod = options.rootMod || "*"
this._write.push(this._rootMod)
this._members = options.members
if (this._members) this._capabilityType[member] = member
}

static get type () { return type }

isMod(id) {
this._write.includes(id)
}

isValidCapability (capability) {
Object.entries(this._capabilityType).map(e => e[1]).includes(capability)
}

async canAppend (entry, identityProvider) {
const entryID = entry.identity.id
const capability = entry.payload.value.capability
const isMod = this.isMod(entryID)
const noMods = this._write.includes('*')
const validCapability = isValidCapability(capability)
const validSig = () => identityProvider.verifyIdentity(entry.identity)

if ((noMods || isMod) && validCapability && validSig()) {
if (capability === this._capabilityType.moderator) this._write.push(modAddId)
return true
}

return false
}

async load (address) {
const addList = address.split('/')
const suffix = addList.pop()
this._members = suffix === 'members'
const mod = suffix.includes('mod') ? suffix : addList.pop()
this._rootMod = mod.split('_')[1]
}

async save () {
// TODO if entire obj saved in manfest, can just pass our own fields
let address = `${type}/mod_${this._rootMod}`
address += this._members ? '/members' : ''
return { address }
}

static async create (orbitdb, options = {}) {
return new ModeratorAccessController(options)
}
}

module.exports = ModeratorAccessController
Loading