Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Don't use member functions from keyring #75

Merged
merged 46 commits into from
Sep 18, 2023
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
925479c
Rewrite listAuthors to not use keyring
Powersource Jul 27, 2023
258cacd
Ignore pnpm lock
Powersource Jul 27, 2023
8d5c6bf
Don't use keyring listAuthors to setup replication
Powersource Jul 27, 2023
45bf22b
Remove tribes.registerAuthors
Powersource Aug 10, 2023
cb0cfd0
Stop depending on keyring.processAddMembers
Powersource Aug 16, 2023
d4db5ff
Lint
Powersource Aug 16, 2023
12bf6be
Try to log more on invite test err
Powersource Aug 17, 2023
5fc9434
Apply suggestions from code review
Powersource Aug 17, 2023
7a440b4
Refactor away promise in replication setup
Powersource Aug 17, 2023
2af905b
Remove commented code
Powersource Aug 17, 2023
7300f77
try things to debug test
mixmix Aug 18, 2023
1220936
reveal the ACTUAL culprit
mixmix Aug 18, 2023
764200f
Replace use of keystore.author.groupKeys
Powersource Aug 29, 2023
3563122
Filter for dupe msgs in getgrouptangle
Powersource Aug 30, 2023
ade51b3
Don't rebuild if we're the adder
Powersource Aug 30, 2023
ef11281
Use db1 in rebuild-manager tests
Powersource Aug 30, 2023
2d7a2a5
Skip broken rebuild test
Powersource Aug 30, 2023
c3c2da0
Fix replicate test
Powersource Sep 1, 2023
21d6334
Fix replicate better
Powersource Sep 1, 2023
6dab626
Fix unbox test
Powersource Sep 1, 2023
94627ea
Fix standard complaint
Powersource Sep 1, 2023
1725ebc
Fix rebuild test
Powersource Sep 1, 2023
3badae0
update CI to Node >=12
mixmix Sep 4, 2023
ca89f70
revert ?. optional-chaining, we must support Node 12 still
mixmix Sep 4, 2023
5aa6532
seperate unbox test-vectors into different test
mixmix Sep 4, 2023
851f45b
remove deprecated tests for "application"
mixmix Sep 4, 2023
9315d07
upgrade [email protected]
mixmix Sep 4, 2023
9549e77
try to upgrade npm before npm ci
mixmix Sep 4, 2023
c14eebb
npm i -g npm@9
mixmix Sep 4, 2023
9b79ff4
revert lockfile to v2
mixmix Sep 4, 2023
0f76509
Add comment about decrypt keys to try
Powersource Sep 4, 2023
658aebd
Use sets for processedNewAuthors
Powersource Sep 4, 2023
561047a
Use scuttle.group instead of ssb.tribes
Powersource Sep 4, 2023
5fc9298
Fix invite test reattempts
Powersource Sep 4, 2023
c924fcd
Elaborate test persistence comment
Powersource Sep 4, 2023
7bc0e62
Remove persistence comment moved to issue
Powersource Sep 4, 2023
a3331f8
Refactor processAuthors internal helper
Powersource Sep 5, 2023
02a9383
Adapt closing test to windows
Powersource Sep 5, 2023
4663c93
Remove a registerAuthors call
Powersource Sep 5, 2023
7f7ff0d
Remove another registerAuthors call
Powersource Sep 5, 2023
f98159f
Keep unbox test async
Powersource Sep 5, 2023
ce18c44
CI: modify node versions run
mixmix Sep 12, 2023
f078721
disable unbox vector tests in Windows
mixmix Sep 13, 2023
e68b6de
dont use any test-pretty printing to see what happens in CI
mixmix Sep 13, 2023
5a785af
re-instate tap-arc
mixmix Sep 18, 2023
05ecf5c
get passing on windows
mixmix Sep 18, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ build/Release
node_modules/
jspm_packages/

pnpm-lock.yaml

# TypeScript v1 declaration files
typings/

Expand Down
16 changes: 1 addition & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -174,9 +174,8 @@ _alias of `ssb.tribes.get`_
## API (Extras)

These endpoints give you access to additional features, such as:
- **registering groups or group-authors**:
- **registering groups**:
- `ssb.tribes.register(groupId, info, cb)`
- `ssb.tribes.registerAuthors(groupId, [authorId], cb)`
- **linking your feed to a group**
- `ssb.tribes.link.create({ group, name }, cb)`
- **linking subGroups to a group**
Expand Down Expand Up @@ -207,19 +206,6 @@ where:
- `scheme` *String* (optional) - a description of the key management scheme this key is part of
- `cb`[ *Function* - a callback with signature `cb(err: Error, success: Boolean)`


### `ssb.tribes.registerAuthors(groupId, [authorId], cb)`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we are reverting the membership persisting, then revert this
If we're not leave it deleted

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think even when we add persistence back, i think it should be an internal thing not exposed to the user


Makes an off-log note that some author(s) are part of a group.
This is used to know which group keys to consider when you receive a private message from a particular author.

_NOTE: mainly used internally_

where:
- `groupId` *String* - is a the id for the group you want to not these users are part of
- `[authorId]` *Array* - is a connection of feedIds
- `cb` *Function* - has signature `cb(err, success)`

### `ssb.tribes.poBox.create(opts, cb)`

Creates a P.O. Box key-pair, which is like a one-way group messaging setup with a public and private curve25519 keypair.
Expand Down
98 changes: 66 additions & 32 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -91,29 +91,58 @@ function init (ssb, config) {

/* start listeners */
const rebuildManager = new RebuildManager(ssb)
listen.addMember(ssb, m => {
const { root, groupKey } = m.value.content
ssb.get({ id: root, meta: true }, (err, groupInitMsg) => {
if (err) throw err
const processedNewAuthors = {}
pull(
listen.addMember(ssb),
pull.asyncMap((m, cb) => {
const { root, groupKey } = m.value.content
ssb.get({ id: root, meta: true }, (err, groupInitMsg) => {
if (err) throw err

const groupId = buildGroupId({ groupInitMsg, groupKey })
const authors = unique([
groupInitMsg.value.author,
m.value.author,
...m.value.content.recps.filter(isFeed)
])
const groupId = buildGroupId({ groupInitMsg, groupKey })
const authors = unique([
groupInitMsg.value.author,
m.value.author,
...m.value.content.recps.filter(isFeed)
])

const record = keystore.group.get(groupId)
// if we haven't been in the group since before, register the group
if (record == null) {
return keystore.group.register(groupId, { key: groupKey, root }, (err) => {
if (err) return cb(err)
processAuthors(cb)
})
} else {
processAuthors(cb)
}

keystore.processAddMember({ groupId, groupKey, root, authors }, (err, newAuthors) => {
Powersource marked this conversation as resolved.
Show resolved Hide resolved
if (err) throw err
if (newAuthors.length) {
state.newAuthorListeners.forEach(fn => fn({ groupId, newAuthors }))
function processAuthors (cb) {
if (processedNewAuthors[groupId] === undefined) processedNewAuthors[groupId] = []
Powersource marked this conversation as resolved.
Show resolved Hide resolved

const newAuthors = authors.filter(author => !processedNewAuthors[groupId].includes(author))

// TODO persist membership between restarts so we don't have to process again even if we restart
// TODO write comment when persisting about why we persist
// we basically want to know which members we've already re-indexed for
Powersource marked this conversation as resolved.
Show resolved Hide resolved
processedNewAuthors[groupId] = [...processedNewAuthors[groupId], ...newAuthors]

if (newAuthors.length) {
state.newAuthorListeners.forEach(fn => fn({ groupId, newAuthors }))

const reason = ['add-member', ...newAuthors].join()
rebuildManager.rebuild(reason)
const reason = ['add-member', ...newAuthors].join()
// TODO shouldn't rebuild if we're the person who added them
Powersource marked this conversation as resolved.
Show resolved Hide resolved
rebuildManager.rebuild(reason)
}
return cb()
}
})
}),
pull.drain(() => {}, (err) => {
if (err) console.error('Listening for new addMembers errored:', err)
})
})
)

listen.poBox(ssb, m => {
const { poBoxId, key: poBoxKey } = m.value.content.keys.set
keystore.processPOBox({ poBoxId, poBoxKey }, (err, isNew) => {
Expand All @@ -133,15 +162,25 @@ function init (ssb, config) {
.forEach(id => ssb.replicate.request({ id, replicate: true }))
})

state.loading.keystore.once((s) => {
const peers = new Set()
keystore.group.list()
.map(groupId => keystore.group.listAuthors(groupId))
.forEach(authors => authors.forEach(author => peers.add(author)))

peers.delete(ssb.id)
Array.from(peers)
.forEach(id => ssb.replicate.request({ id, replicate: true }))
state.loading.keystore.once(() => {
pull(
pull.values(keystore.group.list()),
paraMap(
(groupId, cb) => ssb.tribes.listAuthors(groupId, (err, feedIds) => {
Powersource marked this conversation as resolved.
Show resolved Hide resolved
if (err) return cb('error listing authors to replicate on start')
cb(null, feedIds)
}),
5
),
pull.flatten(),
pull.unique(),
pull.drain(feedId => {
if (feedId === ssb.id) return
ssb.replicate.request({ id: feedId, replicate: true })
}, (err) => {
if (err) console.error('error on initializing replication of group members')
})
mixmix marked this conversation as resolved.
Show resolved Hide resolved
)
})
}
})
Expand Down Expand Up @@ -198,8 +237,6 @@ function init (ssb, config) {
const readKey = unboxer.key(initValue.content, initValue)
if (!readKey) return cb(new Error('tribes.group.init failed, please try again while not publishing other messages'))

state.newAuthorListeners.forEach(fn => fn({ groupId: data.groupId, newAuthors: [ssb.id] }))

// addMember the admin
scuttle.group.addMember(data.groupId, [ssb.id], {}, (err) => {
if (err) return cb(err)
Expand Down Expand Up @@ -259,9 +296,6 @@ function init (ssb, config) {
register (groupId, info, cb) {
keystore.group.register(groupId, info, cb)
},
registerAuthors (groupId, authors, cb) {
keystore.group.registerAuthors(groupId, authors, (err) => err ? cb(err) : cb(null, true))
},
create: tribeCreate,
list: tribeList,
get: tribeGet,
Expand All @@ -276,7 +310,7 @@ function init (ssb, config) {
})
},
listAuthors (groupId, cb) {
onKeystoreReady(() => cb(null, keystore.group.listAuthors(groupId)))
scuttle.group.listAuthors(groupId, cb)
},

link: {
Expand Down
7 changes: 3 additions & 4 deletions listen.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,16 @@ const mockSSB = { backlinks: true, query: true }
const { isUpdate: isPOBox } = new CRUT(mockSSB, poBoxSpec).spec

module.exports = {
addMember (ssb, emit) {
pull(
addMember (ssb) {
return pull(
ssb.messagesByType({ type: 'group/add-member', private: true, live: true }),
// NOTE this will run through all messages on each startup, which will help guarentee
// all messages have been emitted AND processed
// (same not true if we used a dummy flume-view)
pull.filter(m => m.sync !== true),
pull.filter(isAddMember),
pull.filter(isAddMember)
// NOTE we DO NOT filter our own messages out
// this is important for rebuilding indexes and keystore state if we have to restore our feed
pull.drain(emit)
)
},
poBox (ssb, emit) {
Expand Down
28 changes: 28 additions & 0 deletions method/group.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ const { keySchemes } = require('private-group-spec')
const { SecretKey } = require('ssb-private-group-keys')
const bfe = require('ssb-bfe')
const Crut = require('ssb-crut')
const pull = require('pull-stream')

const { groupId: buildGroupId, poBoxKeys } = require('../lib')
const initSpec = require('../spec/group/init')
Expand Down Expand Up @@ -93,6 +94,33 @@ module.exports = function GroupMethods (ssb, keystore, state) {

ssb.publish(content, cb)
},
listAuthors (groupId, cb) {
const query = [{
$filter: {
value: {
content: {
type: 'group/add-member'
}
}
}
}]

pull(
ssb.query.read({ query }),
pull.filter(addMemberSpec.isValid),
pull.filter(msg =>
groupId === msg.value.content.recps[0]
),
mixmix marked this conversation as resolved.
Show resolved Hide resolved
pull.map(msg => msg.value.content.recps.slice(1)),
pull.flatten(),
Powersource marked this conversation as resolved.
Show resolved Hide resolved
pull.unique(),
pull.collect((err, members) => {
if (err) return cb(err)

return cb(null, members)
})
)
},
addPOBox (groupId, cb) {
const info = keystore.group.get(groupId)
if (!info) return cb(new Error('unknown groupId: ' + groupId))
Expand Down
1 change: 1 addition & 0 deletions method/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ module.exports = function Method (ssb, keystore, state) {
group: {
init: patient(group.init),
addMember: patient(group.addMember),
listAuthors: group.listAuthors,
addPOBox: patient(group.addPOBox),
getPOBox: group.getPOBox
},
Expand Down
2 changes: 1 addition & 1 deletion rebuild-manager.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ module.exports = class RebuildManager {
this.isRebuilding = true

rebuild(err => {
log('finished')
log('finished', ssb.id)
// rebuild done
this.requests.callback(err)

Expand Down
23 changes: 16 additions & 7 deletions test/api/add-new-author-listener.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ const { promisify: p } = require('util')
const { Server, replicate } = require('../helpers')

test('addNewAuthorListener', async t => {
t.plan(4)
t.plan(6)

const admin = Server() // me
const newPerson = Server() // some friend
const admin = Server({ name: 'admin', debug: false }) // me
const newPerson = Server({ name: 'newPerson' }) // some friend

const name = id => {
if (id === admin.id) return 'admin'
Expand All @@ -15,9 +15,15 @@ test('addNewAuthorListener', async t => {

let groupId // eslint-disable-line

let numAdded = 0
admin.tribes.addNewAuthorListener(({ newAuthors, groupId: _groupId }) => {
// should hear adding self to newly created group
t.deepEqual(newAuthors.map(name), ['admin'], 'admin = returns expected newAuthors')
if (numAdded < 1) {
t.deepEqual(newAuthors.map(name), ['admin'], 'admin = returns expected newAuthors')
} else if (numAdded === 1) {
t.deepEqual(newAuthors.map(name), ['newPerson'], 'admin sees newMember being added')
}
numAdded++

const testGroupId = () => {
if (!groupId) return setTimeout(testGroupId, 50)
Expand All @@ -31,8 +37,11 @@ test('addNewAuthorListener', async t => {
t.deepEqual(newAuthors.map(name), ['admin', 'newPerson'], 'newPerson returns expected newAuthors')
t.equal(_groupId, groupId, 'newPerson, returns expected groupId')

admin.close()
newPerson.close()
setTimeout(() => {
admin.close()
newPerson.close()
t.end()
}, 1000)
})

try {
Expand All @@ -43,7 +52,7 @@ test('addNewAuthorListener', async t => {
groupId = groupData.groupId
await p(admin.tribes.invite)(groupId, [newPerson.id], { text: 'ahoy' })

await p(admin.tribes.invite)(groupId, [newPerson.id], { text: 'ahoy' })
await p(admin.tribes.invite)(groupId, [newPerson.id], { text: 'ahoy again' })
// we want to test that duplicate adds dont fire the addNewAuthorListener multiple times,
// unfortunately there are race conditions around calculating the new authors, so
// we have added a small delay here
Expand Down
2 changes: 1 addition & 1 deletion test/api/application/list.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const emptyFields = {
tombstone: null
}

test('tribes.application.list (v2.1 application)', async t => {
test.skip('tribes.application.list (v2.1 application)', async t => {
Powersource marked this conversation as resolved.
Show resolved Hide resolved
const strangerOpts = {
application: true,
name: 'stranger-test-' + Date.now(),
Expand Down
9 changes: 6 additions & 3 deletions test/api/invite.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,12 @@ test('tribes.invite', async t => {
text: 'Welcome new person!',
recps: [groupId]
}
const { key: greetingKey } = await p(kaitiaki.publish)(greetingContent)
await p(replicate)({ from: kaitiaki, to: newPerson, live: false, name })
const greetingMsg = await p(Getter(newPerson))(greetingKey)
const { key: greetingKey } = await p(kaitiaki.publish)(greetingContent).catch(t.fail)
await p(replicate)({ from: kaitiaki, to: newPerson, live: false, name }).catch(t.fail)
const greetingMsg = await p(Getter(newPerson))(greetingKey).catch(err => {
console.error('error on getting greeting msg:', err)
mixmix marked this conversation as resolved.
Show resolved Hide resolved
t.fail(err)
})
t.deepEqual(greetingMsg.value.content, greetingContent, 'new person can read group content')

/* new person posts to group, kaitiaki can read */
Expand Down