From fcc616ee2acbce7a8542d99eada31346513c84cf Mon Sep 17 00:00:00 2001 From: les Date: Fri, 13 Sep 2019 10:17:44 +0200 Subject: [PATCH] use sharedInbox to send events, fix #19 --- server/api/controller/user.js | 5 ++- server/api/models/user.js | 2 +- server/federation/follows.js | 2 +- server/federation/helpers.js | 71 +++++++++++++++++++++++------------ server/federation/users.js | 2 +- 5 files changed, 52 insertions(+), 30 deletions(-) diff --git a/server/api/controller/user.js b/server/api/controller/user.js index b80e0c94..292f4623 100644 --- a/server/api/controller/user.js +++ b/server/api/controller/user.js @@ -118,7 +118,8 @@ const userController = { // send response to client res.json(event) - if (req.user) { federation.sendEvent(event, req.user) } + const user = await User.findByPk(req.user.id, { include: { model: FedUsers, as: 'followers' }}) + if (user) { federation.sendEvent(event, user) } // res.sendStatus(200) @@ -209,7 +210,7 @@ const userController = { async current (req, res) { if (!req.user) return res.status(400).send('Not logged') - const user = await User.findByPk(req.user.id, { include: [ FedUsers ]}) + const user = await User.findByPk(req.user.id, { include: { model: FedUsers, as: 'followers' } }) res.json(user) }, diff --git a/server/api/models/user.js b/server/api/models/user.js index ffc608a5..af468fd4 100644 --- a/server/api/models/user.js +++ b/server/api/models/user.js @@ -39,7 +39,7 @@ module.exports = (sequelize, DataTypes) => { user.associate = function (models) { // associations can be defined here user.hasMany(models.event) - user.belongsToMany(models.fed_users, { through: 'user_followers' }) + user.belongsToMany(models.fed_users, { through: 'user_followers', as: 'followers' }) } user.prototype.comparePassword = async function (pwd) { diff --git a/server/federation/follows.js b/server/federation/follows.js index 5d6cf4b6..3ee7d7f4 100644 --- a/server/federation/follows.js +++ b/server/federation/follows.js @@ -10,7 +10,7 @@ module.exports = { const body = req.body if (typeof body.object !== 'string') { return } const username = body.object.replace(`${config.baseurl}/federation/u/`, '') - const user = await User.findOne({ where: { username }, include: [ FedUsers ] }) + const user = await User.findOne({ where: { username }, include: { model: FedUsers, as: 'followers' }}) if (!user) { return res.status(404).send('User not found') } // check for duplicate diff --git a/server/federation/helpers.js b/server/federation/helpers.js index abf58382..bb0270db 100644 --- a/server/federation/helpers.js +++ b/server/federation/helpers.js @@ -6,29 +6,27 @@ const httpSignature = require('http-signature') const debug = require('debug')('federation:helpers') const { user: User, fed_users: FedUsers } = require('../api/models') const url = require('url') - -const actorCache = [] +const settings = require('../api/controller/settings') const Helpers = { async signAndSend (message, user, to) { // get the URI of the actor object and append 'inbox' to it - const toInbox = to + '/inbox' - const toOrigin = url.parse(to) - const toPath = toOrigin.path + '/inbox' + const toUrl = url.parse(to) + // const toPath = toOrigin.path + '/inbox' // get the private key const privkey = user.rsa.privateKey const signer = crypto.createSign('sha256') const d = new Date() - const stringToSign = `(request-target): post ${toPath}\nhost: ${toOrigin.hostname}\ndate: ${d.toUTCString()}` + const stringToSign = `(request-target): post ${toUrl.path}\nhost: ${toUrl.hostname}\ndate: ${d.toUTCString()}` signer.update(stringToSign) signer.end() const signature = signer.sign(privkey) const signature_b64 = signature.toString('base64') const header = `keyId="${config.baseurl}/federation/u/${user.username}",headers="(request-target) host date",signature="${signature_b64}"` - const ret = await fetch(toInbox, { + const ret = await fetch(to, { headers: { - 'Host': toOrigin.hostname, + 'Host': toUrl.hostname, 'Date': d.toUTCString(), 'Signature': header, 'Content-Type': 'application/activity+json; charset=utf-8', @@ -40,55 +38,78 @@ const Helpers = { }, async sendEvent (event, user) { + if (!settings.settings.enable_federation) { + console.error(settings.settings) + console.error(settings.secretSettings) + debug('federation disabled') + return + } + + console.error('dentro sendEvent ', user) + // event is sent by user that published it and by the admin instance // collect followers from admin and user - const instanceAdmin = await User.findOne({ where: { email: config.admin, include: FedUsers } }) + const instanceAdmin = await User.findOne({ where: { email: config.admin }, include: { model: FedUsers, as: 'followers' } }) if (!instanceAdmin || !instanceAdmin.username) { debug('Instance admin not found (there is no user with email => %s)', config.admin) return } - console.error(instanceAdmin) - return - for (const follower of instanceAdmin.followers) { - debug('Notify %s with event %s (from admin user %s)', follower, event.title, instanceAdmin.username) + console.error(instanceAdmin.followers) + let recipients = {} + instanceAdmin.followers.forEach(follower => { + const sharedInbox = follower.user_followers.endpoints.sharedInbox + if (!recipients[sharedInbox]) recipients[sharedInbox] = [] + recipients[sharedInbox].push(follower.id) + }) + + for(const sharedInbox in recipients) { + debug('Notify %s with event %s (from admin user %s) cc => %d', sharedInbox, event.title, instanceAdmin.username, recipients[sharedInbox].length) const body = { id: `${config.baseurl}/federation/m/${event.id}#create`, type: 'Create', to: ['https://www.w3.org/ns/activitystreams#Public'], - cc: [`${config.baseurl}/federation/u/${instanceAdmin.username}/followers`, follower], + cc: [`${config.baseurl}/federation/u/${instanceAdmin.username}/followers`, ...recipients[sharedInbox]], actor: `${config.baseurl}/federation/u/${instanceAdmin.username}`, - object: event.toAP(instanceAdmin.username, [`${config.baseurl}/federation/u/${instanceAdmin.username}/followers`, follower]) + object: event.toAP(instanceAdmin.username, [`${config.baseurl}/federation/u/${instanceAdmin.username}/followers`, ...recipients[sharedInbox]]) } body['@context'] = 'https://www.w3.org/ns/activitystreams' - Helpers.signAndSend(body, instanceAdmin, follower) + Helpers.signAndSend(body, instanceAdmin, sharedInbox) } - // in case the event is published by the Admin itself do not republish + // in case the event is published by the Admin itself do not add user if (instanceAdmin.id === user.id) { debug('Event published by instance Admin') return - } - + } if (!user.settings.enable_federation || !user.username) { debug('Federation disabled for user %d (%s)', user.id, user.username) return } - for (const follower of user.followers) { - debug('Notify %s with event %s (from user %s)', follower, event.title, user.username) + + recipients = {} + user.followers.forEach(follower => { + const sharedInbox = follower.object.endpoints.sharedInbox + if (!recipients[sharedInbox]) recipients[sharedInbox] = [] + recipients[sharedInbox].push(follower.id) + }) + + debug(recipients) + for(const sharedInbox in recipients) { + debug('Notify %s with event %s (from admin user %s) cc => %d', sharedInbox, event.title, user.username, recipients[sharedInbox].length) const body = { id: `${config.baseurl}/federation/m/${event.id}#create`, type: 'Create', to: ['https://www.w3.org/ns/activitystreams#Public'], - cc: [`${config.baseurl}/federation/u/${user.username}/followers`, follower], - published: event.createdAt, + cc: [`${config.baseurl}/federation/u/${user.username}/followers`, ...recipients[sharedInbox]], actor: `${config.baseurl}/federation/u/${user.username}`, - object: event.toAP(user.username, [`${config.baseurl}/federation/u/${user.username}/followers`, follower]) + object: event.toAP(user.username, [`${config.baseurl}/federation/u/${user.username}/followers`, ...recipients[sharedInbox]]) } body['@context'] = 'https://www.w3.org/ns/activitystreams' - Helpers.signAndSend(body, user, follower) + Helpers.signAndSend(body, user, sharedInbox) } + }, // DO NOT USE THIS! (why is this needed btw?) diff --git a/server/federation/users.js b/server/federation/users.js index 79db6778..732385a5 100644 --- a/server/federation/users.js +++ b/server/federation/users.js @@ -45,7 +45,7 @@ module.exports = { const page = req.query.page debug('Retrieve %s followers', name) if (!name) return res.status(400).send('Bad request.') - const user = await User.findOne({where: { username: name }, include: [FedUsers]}) + const user = await User.findOne({where: { username: name }, include: { model: FedUsers, as: 'followers' }}) if (!user) return res.status(404).send(`No record found for ${name}`) res.type('application/activity+json; charset=utf-8')