prototype automatic follow acceptance with signed request
This commit is contained in:
parent
06d6f85960
commit
4d7e99461d
2 changed files with 71 additions and 7 deletions
|
@ -1,10 +1,72 @@
|
||||||
const express = require('express')
|
const express = require('express')
|
||||||
const router = express.Router()
|
const router = express.Router()
|
||||||
const utils = require('../utils')
|
const utils = require('../utils')
|
||||||
|
const request = require('request-promise-native')
|
||||||
|
const httpSignature = require('http-signature')
|
||||||
|
const {ObjectId} = require('mongodb')
|
||||||
|
|
||||||
router.post('/', utils.validators.activity, function (req, res) {
|
router.post('/', utils.validators.activity, function (req, res) {
|
||||||
const db = req.app.get('db');
|
const db = req.app.get('db');
|
||||||
req.body._target = req.user
|
let outgoingResponse
|
||||||
|
req.body._meta = {_target: utils.usernameToIRI(req.user)}
|
||||||
|
// side effects
|
||||||
|
switch(req.body.type) {
|
||||||
|
case 'Accept':
|
||||||
|
// workaround for node-http-signature#87
|
||||||
|
const tempUrl = req.url
|
||||||
|
req.url = req.originalUrl
|
||||||
|
sigHead = httpSignature.parse(req, {clockSkew: 3000})
|
||||||
|
req.url = tempUrl
|
||||||
|
// TODO - real key lookup with remote fetch
|
||||||
|
utils.getOrCreateActor(sigHead.keyId.replace(/.*\//, ''), db)
|
||||||
|
.then(user => {
|
||||||
|
const valid = httpSignature.verifySignature(sigHead, user.publicKey.publicKeyPem)
|
||||||
|
console.log('key validation', valid)
|
||||||
|
return res.status(valid ? 200 : 401).send()
|
||||||
|
})
|
||||||
|
break
|
||||||
|
case 'Follow':
|
||||||
|
req.body._meta._target = req.body.object.id
|
||||||
|
// send acceptance reply
|
||||||
|
Promise.all([
|
||||||
|
utils.getOrCreateActor(req.user, db, true),
|
||||||
|
request({
|
||||||
|
url: utils.actorFromActivity(req.body),
|
||||||
|
headers: {Accept: 'application/activity+json'},
|
||||||
|
json: true,
|
||||||
|
})
|
||||||
|
])
|
||||||
|
.then(([user, actor]) => {
|
||||||
|
if (!actor || !actor.inbox) {
|
||||||
|
throw new Error('unable to send follow request acceptance: actor inbox not retrievable')
|
||||||
|
}
|
||||||
|
const newID = new ObjectId()
|
||||||
|
const responseOpts = {
|
||||||
|
method: 'POST',
|
||||||
|
url: actor.inbox,
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/activity+json',
|
||||||
|
},
|
||||||
|
httpSignature: {
|
||||||
|
key: user._meta.privateKey,
|
||||||
|
keyId: user.id,
|
||||||
|
headers: ['(request-target)', 'host', 'date'],
|
||||||
|
},
|
||||||
|
json: true,
|
||||||
|
body: utils.toJSONLD({
|
||||||
|
_id: newID,
|
||||||
|
type: 'Accept',
|
||||||
|
id: `https://${req.app.get('domain')}/o/${newID.toHexString()}`,
|
||||||
|
actor: user.id,
|
||||||
|
object: req.body,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
return request(responseOpts)
|
||||||
|
})
|
||||||
|
.then(result => console.log('success', result))
|
||||||
|
.catch(e => console.log(e))
|
||||||
|
break
|
||||||
|
}
|
||||||
Promise.all([
|
Promise.all([
|
||||||
db.collection('objects').insertOne(req.body.object),
|
db.collection('objects').insertOne(req.body.object),
|
||||||
db.collection('streams').insertOne(req.body)
|
db.collection('streams').insertOne(req.body)
|
||||||
|
|
|
@ -51,8 +51,6 @@ function createLocalActor (name, type) {
|
||||||
privateKeyEncoding: {
|
privateKeyEncoding: {
|
||||||
type: 'pkcs8',
|
type: 'pkcs8',
|
||||||
format: 'pem',
|
format: 'pem',
|
||||||
cipher: 'aes-256-cbc',
|
|
||||||
passphrase: config.KEYPASS
|
|
||||||
}
|
}
|
||||||
}).then(pair => {
|
}).then(pair => {
|
||||||
const actorBase = usernameToIRI(name);
|
const actorBase = usernameToIRI(name);
|
||||||
|
@ -80,13 +78,15 @@ function createLocalActor (name, type) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
module.exports.createLocalActor = createLocalActor
|
module.exports.createLocalActor = createLocalActor
|
||||||
|
const actorProj = {_id: 0, _meta: 0}
|
||||||
async function getOrCreateActor(preferredUsername, db) {
|
const metaActorProj = {_id: 0}
|
||||||
|
async function getOrCreateActor(preferredUsername, db, includeMeta) {
|
||||||
const id = usernameToIRI(preferredUsername)
|
const id = usernameToIRI(preferredUsername)
|
||||||
let user = await db.collection('objects')
|
let user = await db.collection('objects')
|
||||||
.find({id: id})
|
.find({id: id})
|
||||||
.limit(1)
|
.limit(1)
|
||||||
.project({_id: 0, _meta: 0})
|
// strict comparison as we don't want to return private keys on accident
|
||||||
|
.project(includeMeta === true ? metaActorProj : actorProj)
|
||||||
.next()
|
.next()
|
||||||
if (user) {
|
if (user) {
|
||||||
return user
|
return user
|
||||||
|
@ -95,8 +95,10 @@ async function getOrCreateActor(preferredUsername, db) {
|
||||||
user = await createLocalActor(preferredUsername, 'Group')
|
user = await createLocalActor(preferredUsername, 'Group')
|
||||||
await db.collection('objects').insertOne(user)
|
await db.collection('objects').insertOne(user)
|
||||||
// only executed on success
|
// only executed on success
|
||||||
delete user._meta
|
|
||||||
delete user._id
|
delete user._id
|
||||||
|
if (includeMeta !== true) {
|
||||||
|
delete user._meta
|
||||||
|
}
|
||||||
return user
|
return user
|
||||||
}
|
}
|
||||||
module.exports.getOrCreateActor = getOrCreateActor
|
module.exports.getOrCreateActor = getOrCreateActor
|
||||||
|
|
Loading…
Reference in a new issue