members db and tokens db

This commit is contained in:
Andre Staltz 2023-06-21 16:26:00 +03:00
parent a03950ac30
commit 80f2a3afb0
No known key found for this signature in database
GPG Key ID: 9EDE23EA7E8A4890
7 changed files with 197 additions and 19 deletions

2
.gitignore vendored
View File

@ -2,4 +2,4 @@ node_modules
pnpm-lock.yaml
yarn.lock
TODO
keypair
data

71
lib/members.cjs Normal file
View File

@ -0,0 +1,71 @@
const Crypto = require('node:crypto')
const Path = require('node:path')
const AtomicFileRW = require('atomic-file-rw')
const Base58 = require('bs58')
class Tokens {
static #filePath
/**
* @type {Set<string>}
*/
static #set
static #loaded = false
static #save(cb) {
const json = JSON.stringify([...this.#set])
AtomicFileRW.writeFile(this.#filePath, json, cb)
}
static load(parentPath) {
if (this.#loaded) return
this.#filePath = Path.join(parentPath, 'members.json')
this.#set = new Set()
AtomicFileRW.readFile(this.#filePath, (err, buf) => {
if (err) {
if (err.code === 'ENOENT') {
this.#loaded = true
} else {
console.warn('Problem loading members file:', err)
}
return
}
const json = typeof buf === 'string' ? buf : buf.toString('utf-8')
const arr = JSON.parse(json)
for (const token of arr) {
this.#set.add(token)
}
this.#loaded = true
})
}
/**
* @param {string} token
* @returns {boolean}
*/
static has(token) {
if (!this.#loaded) {
throw new Error('Members not loaded yet, cannot call has()')
}
return this.#set.has(token)
}
static create() {
if (!this.#loaded) {
throw new Error('Members not loaded yet, cannot call create()')
}
let token
do {
token = Base58.encode(Crypto.randomBytes(32))
} while (this.#set.has(token))
this.#set.add(token)
this.#save((err, _) => {
if (err) console.warn('Problem saving members file:', err)
})
return token
}
}
module.exports = Tokens

View File

@ -1,8 +1,12 @@
const Path = require('node:path')
const Keypair = require('ppppp-keypair')
const caps = require('ppppp-caps')
const SSAPI = require('secret-stack/lib/api')
module.exports = function startPeer() {
const keypair = Keypair.loadOrCreateSync('./keypair')
const path = Path.join(__dirname, '..', 'data')
const keypairPath = Path.join(path, 'keypair')
const keypair = Keypair.loadOrCreateSync(keypairPath)
SSAPI([], {})
.use(require('secret-stack/lib/core'))
@ -11,7 +15,8 @@ module.exports = function startPeer() {
.use(require('ssb-conn'))
.use(require('./plugin-hub.cjs'))
.call(null, {
caps: { shse: 'p2pLq5VZKvNWaaafMUEcxH9BKm2WjNBCxsc8TRQV5gS' },
path,
caps,
keypair,
conn: {
autostart: false,

View File

@ -2,6 +2,8 @@ const cat = require('pull-cat')
const Notify = require('pull-notify')
const pull = require('pull-stream')
const debug = require('debug')('ppppp:hub')
const Tokens = require('./tokens.cjs')
const Members = require('./members.cjs')
function ErrorDuplex(message) {
const err = new Error(message)
@ -22,32 +24,36 @@ module.exports = {
createTunnel: 'duplex',
ping: 'sync',
attendants: 'source',
createToken: 'async',
createToken: 'sync',
},
permissions: {
anonymous: {
allow: ['createTunnel', 'ping', 'attendants', 'createToken'],
},
},
init(sstack) {
init(sstack, config) {
if (!sstack.conn || !sstack.conn.connect) {
throw new Error('tunnel plugin is missing the required ssb-conn plugin')
}
debug('running multiserver at %s', sstack.getAddress('public'))
// Ensure that incoming connections are only from members
sstack.auth.hook(function (fn, args) {
const [incomingId, cb] = args
cb(null, true)
// fn.apply(this, args)
Tokens.load(config.path)
Members.load(config.path)
// FIXME:
// if (members.has(incomingId)) {
// fn.apply(this, args);
// } else {
// debug('prevented stranger %s from connecting to us', incomingId);
// cb(new Error('client is a stranger'));
// }
// Ensure that client connections are only from members or to-be members
sstack.auth.hook(function (fn, args) {
const [clientMetadata, cb] = args
const {pubkey, extra} = clientMetadata
if (Members.has(pubkey)) {
cb(null, true)
} else if (extra && Tokens.has(extra)) {
Tokens.delete(extra)
Members.add(pubkey)
cb(null, true)
} else {
debug('prevented stranger %s from connecting to us', pubkey)
cb(new Error('client is a stranger'))
}
})
const attendants = new Map()
@ -101,8 +107,8 @@ module.exports = {
return Date.now()
},
createToken(cb) {
cb(new Error('not implemented'))
createToken() {
return Tokens.create()
},
}
},

81
lib/tokens.cjs Normal file
View File

@ -0,0 +1,81 @@
const Crypto = require('node:crypto')
const Path = require('node:path')
const AtomicFileRW = require('atomic-file-rw')
const Base58 = require('bs58')
class Tokens {
static #filePath
/**
* @type {Set<string>}
*/
static #set
static #loaded = false
static #save(cb) {
const json = JSON.stringify([...this.#set])
AtomicFileRW.writeFile(this.#filePath, json, cb)
}
static load(parentPath) {
if (this.#loaded) return
this.#filePath = Path.join(parentPath, 'tokens.json')
this.#set = new Set()
AtomicFileRW.readFile(this.#filePath, (err, buf) => {
if (err) {
if (err.code === 'ENOENT') {
this.#loaded = true
} else {
console.log('Problem loading tokens file:', err)
}
return
}
const json = typeof buf === 'string' ? buf : Buffer.toString(buf, 'utf-8')
const arr = JSON.parse(json)
for (const token of arr) {
this.#set.add(token)
}
this.#loaded = true
})
}
/**
* @param {string} token
* @returns {boolean}
*/
static has(token) {
if (!this.#loaded) {
throw new Error('Tokens not loaded yet, cannot call has()')
}
return this.#set.has(token)
}
static create() {
if (!this.#loaded) {
throw new Error('Tokens not loaded yet, cannot call create()')
}
let token
do {
token = Base58.encode(Crypto.randomBytes(32))
} while (this.#set.has(token))
this.#set.add(token)
this.#save((err, _) => {
if (err) console.log('Problem saving tokens file:', err)
})
return token
}
static delete(token) {
if (!this.#loaded) {
throw new Error('Tokens not loaded yet, cannot call delete()')
}
this.#set.delete(token)
this.#save((err, _) => {
if (err) console.log('Problem saving tokens file:', err)
})
}
}
module.exports = Tokens

12
package-lock.json generated
View File

@ -11,10 +11,13 @@
"dependencies": {
"@fastify/static": "6.10.2",
"@fastify/view": "7.4.1",
"atomic-file-rw": "~0.3.0",
"bs58": "5.0.0",
"debug": "4.3.4",
"ejs": "3.1.9",
"fastify": "4.17.0",
"pino": "8.14.1",
"ppppp-caps": "github:staltz/ppppp-caps",
"ppppp-keypair": "github:staltz/ppppp-keypair",
"pull-cat": "1.1.11",
"pull-notify": "0.1.2",
@ -2347,6 +2350,11 @@
"resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-6.2.1.tgz",
"integrity": "sha512-wHuWB+CvSVb2XqXM0W/WOYUkVSPbiJb9S5fNB7TBhd8s892Xq910bRxwHtC4l71hgztObTjXL6ZheZXFjhDrDQ=="
},
"node_modules/ppppp-caps": {
"version": "0.0.1",
"resolved": "git+ssh://git@github.com/staltz/ppppp-caps.git#a2111355a1d2bddfc4d5f82267257fe99c14f608",
"license": "CC0-1.0"
},
"node_modules/ppppp-keypair": {
"version": "0.0.1",
"resolved": "git+ssh://git@github.com/staltz/ppppp-keypair.git#2d0cd86dae6df2fa33eb14c836ab706244807f43",
@ -5533,6 +5541,10 @@
"resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-6.2.1.tgz",
"integrity": "sha512-wHuWB+CvSVb2XqXM0W/WOYUkVSPbiJb9S5fNB7TBhd8s892Xq910bRxwHtC4l71hgztObTjXL6ZheZXFjhDrDQ=="
},
"ppppp-caps": {
"version": "git+ssh://git@github.com/staltz/ppppp-caps.git#a2111355a1d2bddfc4d5f82267257fe99c14f608",
"from": "ppppp-caps@github:staltz/ppppp-caps"
},
"ppppp-keypair": {
"version": "git+ssh://git@github.com/staltz/ppppp-keypair.git#2d0cd86dae6df2fa33eb14c836ab706244807f43",
"from": "ppppp-keypair@github:staltz/ppppp-keypair",

View File

@ -26,9 +26,12 @@
"dependencies": {
"@fastify/static": "6.10.2",
"@fastify/view": "7.4.1",
"atomic-file-rw": "~0.3.0",
"bs58": "5.0.0",
"debug": "4.3.4",
"ejs": "3.1.9",
"fastify": "4.17.0",
"ppppp-caps": "github:staltz/ppppp-caps",
"ppppp-keypair": "github:staltz/ppppp-keypair",
"pino": "8.14.1",
"pull-cat": "1.1.11",