mirror of https://codeberg.org/pzp/pzp-promise.git
change secret-stack plugin structure
This commit is contained in:
parent
6b96852808
commit
41e1f9502f
481
lib/index.js
481
lib/index.js
|
@ -50,268 +50,265 @@ function assertValidConfig(config) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
/**
|
||||||
name: 'promise',
|
* @param {{ db: PPPPPDB | null; set: PPPPPSet | null }} peer
|
||||||
manifest: {
|
* @param {Config} config
|
||||||
// management
|
*/
|
||||||
create: 'async',
|
function initPromise(peer, config) {
|
||||||
revoke: 'async',
|
assertDBPlugin(peer)
|
||||||
// promises
|
assertValidConfig(config)
|
||||||
follow: 'async',
|
const devicePromisesFile = Path.join(config.global.path, 'promises.json')
|
||||||
accountAdd: 'async',
|
|
||||||
},
|
const promises = /** @type {Map<string, PPromise>} */ (new Map())
|
||||||
permissions: {
|
let loaded = false
|
||||||
anonymous: {
|
|
||||||
allow: ['follow', 'accountAdd'],
|
// Initial load
|
||||||
},
|
AtomicFileRW.readFile(
|
||||||
},
|
devicePromisesFile,
|
||||||
|
/** @type {CB<B4A | string>} */ function onLoad(err, buf) {
|
||||||
|
if (err) {
|
||||||
|
if (err.code === 'ENOENT') {
|
||||||
|
save((err, _) => {
|
||||||
|
if (err) return console.log('Problem creating promises file:', err)
|
||||||
|
else loaded = true
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
console.log('Problem loading promises file:', err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const json = typeof buf === 'string' ? buf : b4a.toString(buf, 'utf-8')
|
||||||
|
const arr = JSON.parse(json)
|
||||||
|
for (const [token, promise] of arr) {
|
||||||
|
promises.set(token, promise)
|
||||||
|
}
|
||||||
|
loaded = true
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {{ db: PPPPPDB | null; set: PPPPPSet | null }} peer
|
* @param {PPromise} promise
|
||||||
* @param {Config} config
|
* @return {Error | null}
|
||||||
*/
|
*/
|
||||||
init(peer, config) {
|
function validatePromise(promise) {
|
||||||
assertDBPlugin(peer)
|
if (typeof promise !== 'object' || typeof promise.type !== 'string') {
|
||||||
assertValidConfig(config)
|
return Error('Invalid promise created: ' + JSON.stringify(promise))
|
||||||
const devicePromisesFile = Path.join(config.global.path, 'promises.json')
|
}
|
||||||
|
switch (promise.type) {
|
||||||
const promises = /** @type {Map<string, PPromise>} */ (new Map())
|
case 'follow':
|
||||||
let loaded = false
|
case 'account-add':
|
||||||
|
if (typeof promise.account !== 'string') {
|
||||||
// Initial load
|
|
||||||
AtomicFileRW.readFile(
|
|
||||||
devicePromisesFile,
|
|
||||||
/** @type {CB<B4A | string>} */ function onLoad(err, buf) {
|
|
||||||
if (err) {
|
|
||||||
if (err.code === 'ENOENT') {
|
|
||||||
save((err, _) => {
|
|
||||||
// prettier-ignore
|
|
||||||
if (err) return console.log('Problem creating promises file:', err)
|
|
||||||
else loaded = true
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
console.log('Problem loading promises file:', err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
const json = typeof buf === 'string' ? buf : b4a.toString(buf, 'utf-8')
|
|
||||||
const arr = JSON.parse(json)
|
|
||||||
for (const [token, promise] of arr) {
|
|
||||||
promises.set(token, promise)
|
|
||||||
}
|
|
||||||
loaded = true
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {PPromise} promise
|
|
||||||
* @return {Error | null}
|
|
||||||
*/
|
|
||||||
function validatePromise(promise) {
|
|
||||||
if (typeof promise !== 'object' || typeof promise.type !== 'string') {
|
|
||||||
return Error('Invalid promise created: ' + JSON.stringify(promise))
|
|
||||||
}
|
|
||||||
switch (promise.type) {
|
|
||||||
case 'follow':
|
|
||||||
case 'account-add':
|
|
||||||
if (typeof promise.account !== 'string') {
|
|
||||||
// prettier-ignore
|
|
||||||
return Error('Invalid promise missing "account" field: ' + JSON.stringify(promise))
|
|
||||||
} else {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
return Error('Invalid promise type: ' + JSON.stringify(promise))
|
return Error('Invalid promise missing "account" field: ' + JSON.stringify(promise))
|
||||||
}
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {CB<any>} cb
|
|
||||||
*/
|
|
||||||
function save(cb) {
|
|
||||||
const json = JSON.stringify([...promises])
|
|
||||||
AtomicFileRW.writeFile(devicePromisesFile, json, cb)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {PPromise} promise
|
|
||||||
* @param {CB<string>} cb
|
|
||||||
*/
|
|
||||||
function create(promise, cb) {
|
|
||||||
if (!loaded) {
|
|
||||||
setTimeout(() => create(promise, cb), 100)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
let err
|
|
||||||
if ((err = validatePromise(promise))) return cb(err)
|
|
||||||
|
|
||||||
const token = bs58.encode(crypto.randomBytes(32))
|
|
||||||
promises.set(token, promise)
|
|
||||||
save((err, _) => {
|
|
||||||
// prettier-ignore
|
|
||||||
if (err) return cb(new Error('Failed to save promise file when creating new promise', { cause: err }))
|
|
||||||
cb(null, token)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {string} token
|
|
||||||
* @param {string} accountID
|
|
||||||
* @param {CB<boolean>} cb
|
|
||||||
*/
|
|
||||||
function follow(token, accountID, cb) {
|
|
||||||
if (!loaded) {
|
|
||||||
setTimeout(() => follow(token, accountID, cb), 100)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
assertSetPlugin(peer)
|
|
||||||
} catch (err) {
|
|
||||||
cb(/**@type {Error}*/ (err))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (!promises.has(token)) {
|
|
||||||
cb(new Error('Invalid token'))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
const promise = /** @type {PPromise} */ (promises.get(token))
|
|
||||||
if (promise.type !== 'follow') {
|
|
||||||
cb(new Error('Invalid token'))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
const myAccountID = promise.account
|
|
||||||
const theirAccountID = accountID
|
|
||||||
|
|
||||||
peer.set.load(myAccountID, (err) => {
|
|
||||||
// prettier-ignore
|
|
||||||
if (err) return cb(new Error(`Failed to load ppppp-set with account "${myAccountID}" when executing follow promise`, { cause: err }))
|
|
||||||
if (peer.set.has('follow', theirAccountID)) {
|
|
||||||
promises.delete(token)
|
|
||||||
cb(null, false)
|
|
||||||
return
|
|
||||||
} else {
|
} else {
|
||||||
peer.set.add('follow', theirAccountID, (err, _) => {
|
break
|
||||||
// prettier-ignore
|
|
||||||
if (err) return cb(new Error(`Failed to follow account "${theirAccountID}" in ppppp-set from account "${myAccountID}" when executing follow promise`, { cause: err }))
|
|
||||||
promises.delete(token)
|
|
||||||
save((err, _) => {
|
|
||||||
// prettier-ignore
|
|
||||||
if (err) return cb(new Error('Failed to save promise file when executing follow promise', { cause: err }))
|
|
||||||
cb(null, true)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
})
|
default:
|
||||||
|
return Error('Invalid promise type: ' + JSON.stringify(promise))
|
||||||
}
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} token
|
* @param {CB<any>} cb
|
||||||
* @param {AccountAdd} addition
|
*/
|
||||||
* @param {CB<boolean>} cb
|
function save(cb) {
|
||||||
*/
|
const json = JSON.stringify([...promises])
|
||||||
function accountAdd(token, addition, cb) {
|
AtomicFileRW.writeFile(devicePromisesFile, json, cb)
|
||||||
if (!loaded) {
|
}
|
||||||
setTimeout(() => accountAdd(token, addition, cb), 100)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
/**
|
||||||
assertDBPlugin(peer)
|
* @param {PPromise} promise
|
||||||
} catch (err) {
|
* @param {CB<string>} cb
|
||||||
cb(/**@type {Error}*/ (err))
|
*/
|
||||||
return
|
function create(promise, cb) {
|
||||||
}
|
if (!loaded) {
|
||||||
|
setTimeout(() => create(promise, cb), 100)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let err
|
||||||
|
if ((err = validatePromise(promise))) return cb(err)
|
||||||
|
|
||||||
if (!addition?.consent) {
|
const token = bs58.encode(crypto.randomBytes(32))
|
||||||
// prettier-ignore
|
promises.set(token, promise)
|
||||||
cb(new Error('Invalid key to be added, missing "consent": ' + JSON.stringify(addition)))
|
save((err, _) => {
|
||||||
return
|
// prettier-ignore
|
||||||
}
|
if (err) return cb(new Error('Failed to save promise file when creating new promise', { cause: err }))
|
||||||
|
cb(null, token)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
if (
|
/**
|
||||||
!addition?.key?.purpose ||
|
* @param {string} token
|
||||||
!addition?.key?.algorithm ||
|
* @param {string} accountID
|
||||||
!addition?.key?.bytes
|
* @param {CB<boolean>} cb
|
||||||
) {
|
*/
|
||||||
// prettier-ignore
|
function follow(token, accountID, cb) {
|
||||||
cb(new Error('Invalid key to be added, missing purpose/algorithm/bytes: ' + JSON.stringify(addition)))
|
if (!loaded) {
|
||||||
return
|
setTimeout(() => follow(token, accountID, cb), 100)
|
||||||
}
|
return
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
assertSetPlugin(peer)
|
||||||
|
} catch (err) {
|
||||||
|
cb(/**@type {Error}*/ (err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (!promises.has(token)) {
|
||||||
|
cb(new Error('Invalid token'))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const promise = /** @type {PPromise} */ (promises.get(token))
|
||||||
|
if (promise.type !== 'follow') {
|
||||||
|
cb(new Error('Invalid token'))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const myAccountID = promise.account
|
||||||
|
const theirAccountID = accountID
|
||||||
|
|
||||||
const { algorithm, purpose } = addition.key
|
peer.set.load(myAccountID, (err) => {
|
||||||
switch (purpose) {
|
// prettier-ignore
|
||||||
case 'sig':
|
if (err) return cb(new Error(`Failed to load ppppp-set with account "${myAccountID}" when executing follow promise`, { cause: err }))
|
||||||
case 'shs-and-sig':
|
if (peer.set.has('follow', theirAccountID)) {
|
||||||
if (algorithm !== 'ed25519') {
|
promises.delete(token)
|
||||||
// prettier-ignore
|
|
||||||
cb(new Error(`Invalid key to be added, expected algorithm "ed25519" for "${purpose}": ${JSON.stringify(addition)}`))
|
|
||||||
return
|
|
||||||
} else {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
case 'external-encryption':
|
|
||||||
if (algorithm !== 'x25519-xsalsa20-poly1305') {
|
|
||||||
// prettier-ignore
|
|
||||||
cb(new Error(`Invalid key to be added, expected algorithm "x25519-xsalsa20-poly1305" for "${purpose}": ${JSON.stringify(addition)}`))
|
|
||||||
return
|
|
||||||
} else {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
// prettier-ignore
|
|
||||||
cb(new Error(`Invalid key to be added, expected purpose "sig", "shs-and-sig", or "external-encryption": ${JSON.stringify(addition)}`))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!promises.has(token)) {
|
|
||||||
cb(new Error('Invalid token'))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const promise = /** @type {AccountAddPromise} */ (promises.get(token))
|
|
||||||
const { type, account } = promise
|
|
||||||
if (type !== 'account-add') {
|
|
||||||
cb(new Error('Invalid token'))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const keypair = {
|
|
||||||
curve: /**@type {const}*/ ('ed25519'),
|
|
||||||
public: addition.key.bytes,
|
|
||||||
}
|
|
||||||
if (peer.db.account.has({ account, keypair })) {
|
|
||||||
cb(null, false)
|
cb(null, false)
|
||||||
return
|
return
|
||||||
}
|
} else {
|
||||||
|
peer.set.add('follow', theirAccountID, (err, _) => {
|
||||||
peer.db.account.add(
|
// prettier-ignore
|
||||||
{ account, keypair, consent: addition.consent },
|
if (err) return cb(new Error(`Failed to follow account "${theirAccountID}" in ppppp-set from account "${myAccountID}" when executing follow promise`, { cause: err }))
|
||||||
(err, rec) => {
|
|
||||||
if (err) return cb(err)
|
|
||||||
promises.delete(token)
|
promises.delete(token)
|
||||||
save(() => {
|
save((err, _) => {
|
||||||
|
// prettier-ignore
|
||||||
|
if (err) return cb(new Error('Failed to save promise file when executing follow promise', { cause: err }))
|
||||||
cb(null, true)
|
cb(null, true)
|
||||||
})
|
})
|
||||||
}
|
})
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {string} token
|
|
||||||
* @param {CB<any>} cb
|
|
||||||
*/
|
|
||||||
function revoke(token, cb) {
|
|
||||||
if (!loaded) {
|
|
||||||
setTimeout(() => revoke(token, cb), 100)
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
promises.delete(token)
|
/**
|
||||||
save(cb)
|
* @param {string} token
|
||||||
|
* @param {AccountAdd} addition
|
||||||
|
* @param {CB<boolean>} cb
|
||||||
|
*/
|
||||||
|
function accountAdd(token, addition, cb) {
|
||||||
|
if (!loaded) {
|
||||||
|
setTimeout(() => accountAdd(token, addition, cb), 100)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
return { create, revoke, follow, accountAdd }
|
try {
|
||||||
|
assertDBPlugin(peer)
|
||||||
|
} catch (err) {
|
||||||
|
cb(/**@type {Error}*/ (err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!addition?.consent) {
|
||||||
|
// prettier-ignore
|
||||||
|
cb(new Error('Invalid key to be added, missing "consent": ' + JSON.stringify(addition)))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
!addition?.key?.purpose ||
|
||||||
|
!addition?.key?.algorithm ||
|
||||||
|
!addition?.key?.bytes
|
||||||
|
) {
|
||||||
|
// prettier-ignore
|
||||||
|
cb(new Error('Invalid key to be added, missing purpose/algorithm/bytes: ' + JSON.stringify(addition)))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const { algorithm, purpose } = addition.key
|
||||||
|
switch (purpose) {
|
||||||
|
case 'sig':
|
||||||
|
case 'shs-and-sig':
|
||||||
|
if (algorithm !== 'ed25519') {
|
||||||
|
// prettier-ignore
|
||||||
|
cb(new Error(`Invalid key to be added, expected algorithm "ed25519" for "${purpose}": ${JSON.stringify(addition)}`))
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case 'external-encryption':
|
||||||
|
if (algorithm !== 'x25519-xsalsa20-poly1305') {
|
||||||
|
// prettier-ignore
|
||||||
|
cb(new Error(`Invalid key to be added, expected algorithm "x25519-xsalsa20-poly1305" for "${purpose}": ${JSON.stringify(addition)}`))
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
// prettier-ignore
|
||||||
|
cb(new Error(`Invalid key to be added, expected purpose "sig", "shs-and-sig", or "external-encryption": ${JSON.stringify(addition)}`))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!promises.has(token)) {
|
||||||
|
cb(new Error('Invalid token'))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const promise = /** @type {AccountAddPromise} */ (promises.get(token))
|
||||||
|
const { type, account } = promise
|
||||||
|
if (type !== 'account-add') {
|
||||||
|
cb(new Error('Invalid token'))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const keypair = {
|
||||||
|
curve: /**@type {const}*/ ('ed25519'),
|
||||||
|
public: addition.key.bytes,
|
||||||
|
}
|
||||||
|
if (peer.db.account.has({ account, keypair })) {
|
||||||
|
cb(null, false)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
peer.db.account.add(
|
||||||
|
{ account, keypair, consent: addition.consent },
|
||||||
|
(err, rec) => {
|
||||||
|
if (err) return cb(err)
|
||||||
|
promises.delete(token)
|
||||||
|
save(() => {
|
||||||
|
cb(null, true)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} token
|
||||||
|
* @param {CB<any>} cb
|
||||||
|
*/
|
||||||
|
function revoke(token, cb) {
|
||||||
|
if (!loaded) {
|
||||||
|
setTimeout(() => revoke(token, cb), 100)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
promises.delete(token)
|
||||||
|
save(cb)
|
||||||
|
}
|
||||||
|
|
||||||
|
return { create, revoke, follow, accountAdd }
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.name = 'promise'
|
||||||
|
exports.manifest = {
|
||||||
|
// management
|
||||||
|
create: 'async',
|
||||||
|
revoke: 'async',
|
||||||
|
// promises
|
||||||
|
follow: 'async',
|
||||||
|
accountAdd: 'async',
|
||||||
|
}
|
||||||
|
exports.init = initPromise
|
||||||
|
exports.permissions = {
|
||||||
|
anonymous: {
|
||||||
|
allow: ['follow', 'accountAdd'],
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue