Commit a7c4592a authored by les's avatar les
Browse files

cleaning federation

parent 84ac3876
......@@ -87,12 +87,8 @@ export default {
description: `I movimenti hanno bisogno di organizzarsi e autofinanziarsi. <br/>Questo è un dono per voi, usatelo solo per eventi non commerciali e ovviamente antifascisti, antisessisti, antirazzisti.
<br/>Prima di poter pubblicare <strong>dobbiamo approvare l'account</strong>, considera che <strong>dietro questo sito ci sono delle persone</strong> di
carne e sangue, scrivici quindi due righe per farci capire che eventi vorresti pubblicare.`,
error: 'Errore: ',
admin_complete: 'Sei il primo utente e quindi sei amministratore!',
complete: 'Confermeremo la registrazione quanto prima.',
request: 'Richiesta di registrazione',
registration_email: `Ciao,
ci è arrivata una richiesta di registrazione su gancio, la confermeremo quanto prima.`
error: 'Error: ',
complete: 'Registration has to be confirmed.'
},
event: {
......@@ -103,79 +99,79 @@ export default {
Puoi invece fare il <a href='/login'>login</a> o <a href='/registrarti'>registrarti</a>,
altrimenti vai avanti e riceverai una risposta il prima possibile. `,
same_day: 'Same day',
what_description: 'Event\' name',
what_description: 'Event\'s name',
description_description: 'Description',
tag_description: 'Tag...',
media_description: 'Puoi aggiungere un volantino',
added: 'Evento aggiunto',
added_anon: 'Evento aggiunto, verrà confermato quanto prima.',
where_description: `Dov'è il gancio? Se il posto non è presente, scrivilo e <b>premi invio</b>. `,
confirmed: 'Evento confermato',
not_found: 'Evento non trovato',
remove_confirmation: `Sicura di voler eliminare questo evento?`,
recurrent: `Ricorrente`,
recurrent_description: 'Scegli la frequenza e seleziona i giorni',
multidate_description: 'Un festival o una tre giorni? Scegli quando comincia e quando finisce.',
multidate: 'Più giorni',
normal: 'Normale',
normal_description: 'Scegli il giorno.',
recurrent_1w_days: 'Ogni {days}',
recurrent_2w_days: 'Un {days} ogni due',
recurrent_1m_days: '|Il giorno {days} di ogni mese|I giorni {days} di ogni mese',
recurrent_2m_days: '|Il giorno {days} ogni due mesi|I giorni {days} ogni due mesi',
recurrent_1m_ordinal: 'Il {n} {days} di ogni mese',
recurrent_2m_ordinal: '|Il {n} {days} un mese sì e uno no|Il {n} {days} un mese sì e uno no',
due: 'alle',
from: 'Dalle',
image_too_big: 'Immagine troppo grande! Massimo 4M'
tag_description: 'Tag',
media_description: 'You could add an event\'s flyer (optional)',
added: 'Event added',
added_anon: 'Event added but has to be confirmed.',
where_description: `Where's the event? If not present, write it and <b>press enter</b>. `,
confirmed: 'Event confirmed',
not_found: 'Event not found',
remove_confirmation: `Are you sure to remove this event?`,
recurrent: `Recurrent`,
recurrent_description: 'Choose the frequency and select the days',
multidate_description: 'It\'s a festival? Choose when it starts and when it ends?',
multidate: 'More days',
normal: 'Normal',
normal_description: 'Choose the day.',
recurrent_1w_days: 'Each {days}',
recurrent_2w_days: 'A {days} each two',
recurrent_1m_days: '|The {days} of each month|{days} of each month',
recurrent_2m_days: '|The {days} a month each two|The {days} a month each two',
recurrent_1m_ordinal: 'The {n} {days} of each month',
recurrent_2m_ordinal: '|The {n} {days} a month each two|The {n} {days} a month each two',
due: 'due',
from: 'From',
image_too_big: 'Image too big! Max 4M'
},
admin: {
mastodon_instance: 'Istanza',
mastodon_description: 'Puoi associare un account mastodon a questa istanza di gancio, ogni evento verrà pubblicato lì.',
place_description: `Nel caso in cui un luogo sia errato o cambi indirizzo, puoi modificarlo. <br/>Considera che tutti gli eventi associati a questo luogo cambieranno indirizzo (anche quelli passati!)`,
event_confirm_description: 'Puoi confermare qui gli eventi inseriti da utenti anonimi',
delete_user: 'Elimina',
remove_admin: 'Rimuovi admin',
delete_user_confirm: 'Sicura di rimuovere questo utente?',
user_remove_ok: 'Utente eliminato',
user_create_ok: 'Utente creato',
allow_registration_description : 'Vuoi abilitare la registrazione?',
allow_anon_event: 'Si possono inserire eventi anonimi (previa conferma)?',
allow_comments: 'Abilita commenti',
allow_recurrent_event: 'Abilita eventi fissi',
recurrent_event_visible: 'Appuntamenti fissi visibili di default',
federation: 'Federazione / ActivityPub'
delete_user: 'Remove',
remove_admin: 'Remove admin',
delete_user_confirm: 'Are you sure to remove this user?',
user_remove_ok: 'User removed',
user_create_ok: 'User created',
allow_registration_description : 'Allow open registrations?',
allow_anon_event: 'Allow anon events (has to be confirmed)?',
allow_recurrent_event: 'Enable recurrent events',
recurrent_event_visible: 'Show recurrent events by default',
federation: 'Federation / ActivityPub',
enable_federation: 'Enable federation'
},
auth: {
not_confirmed: 'Non abbiamo ancora confermato questa mail...',
fail: 'Autenticazione fallita. Sicura la password è giusta? E la mail?'
not_confirmed: 'Not confirmed yet',
fail: 'Auth failed!. Are you sure password is correct?'
},
settings: {
change_password: 'Cambia password',
password_updated: 'Password modificata',
danger_section: 'Sezione pericolosa',
change_password: 'Modify your password',
password_updated: 'Password updated',
danger_section: 'Dangerous section',
remove_account: 'Premendo il seguente tasto il tuo utente verrà eliminato. Gli eventi da te pubblicati invece no.',
remove_account_confirm: 'Stai per eliminare definitivamente il tuo account',
},
err: {
register_error: 'Errore nella registrazione'
register_error: 'Error during registration'
},
ordinal: {
1: 'primo',
2: 'secondo',
3: 'terzo',
4: 'quarto',
5: 'quinto',
[-1]: 'ultimo',
1: 'first',
2: 'second',
3: 'third',
4: 'fourth',
5: 'fifth',
[-1]: 'last',
},
about: `
<p>
<a href='https://gancio.org'>Gancio</a> is a shared agenda for local communities.
Gancio e' un progetto dell'<a href='https://autistici.org/underscore'>underscore hacklab</a> e uno dei
servizi di <a href='https://cisti.org'>cisti.org</a>.</p>
......
......@@ -90,11 +90,7 @@ export default {
<br/>Prima di poter pubblicare <strong>dobbiamo approvare l'account</strong>, considera che <strong>dietro questo sito ci sono delle persone</strong> di
carne e sangue, scrivici quindi due righe per farci capire che eventi vorresti pubblicare.`,
error: 'Errore: ',
admin_complete: 'Sei il primo utente e quindi sei amministratore!',
complete: 'Confermeremo la registrazione quanto prima.',
request: 'Richiesta di registrazione',
registration_email: `Ciao,
ci è arrivata una richiesta di registrazione su gancio, la confermeremo quanto prima.`
complete: 'Confermeremo la registrazione quanto prima.'
},
event: {
......@@ -150,7 +146,8 @@ export default {
allow_comments: 'Abilita commenti',
allow_recurrent_event: 'Abilita eventi fissi',
recurrent_event_visible: 'Appuntamenti fissi visibili di default',
federation: 'Federazione / ActivityPub'
federation: 'Federazione / ActivityPub',
enable_federation: 'Abilita la federazione!'
},
auth: {
......
......@@ -127,6 +127,11 @@ export default {
validate ({store}) {
return (store.state.auth.loggedIn || store.state.settings.allow_anon_event)
},
head () {
return {
title: `${this.settings.title} - ${this.$t('common.add_event')}`
}
},
data() {
const month = moment().month()+1
const year = moment().year()
......@@ -197,7 +202,7 @@ export default {
data.event.type = 'normal'
data.date = new Date(event.start_datetime)
}
data.time.start = moment(event.start_datetime).format('HH:mm')
data.time.end = moment(event.end_datetime).format('HH:mm')
data.event.title = event.title
......@@ -217,6 +222,7 @@ export default {
places: state => state.places,
user: state => state.user,
events: state => state.events,
settings: state => state.settings
}),
whenPatterns () {
const dates = this.date
......
const config = require('config')
const Helpers = require('./helpers')
const { user: User } = require('../api/models')
module.exports = {
// follow request from fediverse
async follow (req, res, body, targetOrigin, domain) {
if (typeof b.object !== 'string') return
const username = body.object.replace(`${config.baseurl}/federation/u/`, '')
console.error('someone wants to follow ' + username)
const user = await User.findOne({ where: { username }})
if (!user) {
console.error('No user found!')
return
}
Helpers.sendAcceptMessage(body, user, domain, req, res, targetOrigin)
console.error('FOLLOWERS ', user.followers)
if (user.followers.indexOf(body.actor) === -1) {
console.error('ok this is a new follower: ', body.actor)
await user.update({ followers: [...user.followers, body.actor] })
}
},
// unfollow request from fediverse
unfollow () {
console.error('inside unfollow')
}
}
\ No newline at end of file
const fetch = require('fetch')
const request = require('request')
const crypto = require('crypto')
const Helpers = {
async signAndSend(message, user, domain, req, res, targetOrigin) {
// get the URI of the actor object and append 'inbox' to it
let inbox = message.object.actor+'/inbox'
let inboxFragment = inbox.replace(targetOrigin,'')
const targetDomain = new URL(targetOrigin).host
// get the private key
const privkey = user.rsa.privateKey
const signer = crypto.createSign('sha256')
let d = new Date()
let stringToSign = `(request-target): post ${inboxFragment}\nhost: ${targetDomain}\ndate: ${d.toUTCString()}`
signer.update(stringToSign)
signer.end()
const signature = signer.sign(privkey)
const signature_b64 = signature.toString('base64')
let header = `keyId="${config.baseurl}/federation/u/${user.username}",headers="(request-target) host date",signature="${signature_b64}"`
request({
url: inbox,
headers: {
'Host': targetDomain,
'Date': d.toUTCString(),
'Signature': header
},
method: 'POST',
json: true,
body: message
}, function (error, response){
if (error) {
console.log('Error:', error, response.body)
}
else {
console.log('Response:', response.body)
}
})
return res.status(200)
},
async sendAcceptMessage (body, user, domain, req, res, targetOrigin) {
const guid = crypto.randomBytes(16).toString('hex')
let message = {
'@context': 'https://www.w3.org/ns/activitystreams',
'id': `${config.baseurl}/federation/${guid}`,
'type': 'Accept',
'actor': `${config.baseurl}/federation/u/${user.username}`,
'object': body,
}
Helpers.signAndSend(message, user, domain, req, res, targetOrigin)
}
}
module.exports = Helpers
\ No newline at end of file
const express = require('express')
const router = express.Router()
const { user: User } = require('../api/models')
const config = require('config')
const get = require('lodash/get')
const crypto = require('crypto')
const request = require('request')
function signAndSend(message, user, domain, req, res, targetOrigin) {
// get the URI of the actor object and append 'inbox' to it
let inbox = message.object.actor+'/inbox'
let inboxFragment = inbox.replace(targetOrigin,'')
const targetDomain = new URL(targetOrigin).host
// get the private key
const privkey = user.rsa.privateKey
const signer = crypto.createSign('sha256')
let d = new Date()
let stringToSign = `(request-target): post ${inboxFragment}\nhost: ${targetDomain}\ndate: ${d.toUTCString()}`
signer.update(stringToSign)
signer.end()
const signature = signer.sign(privkey)
const signature_b64 = signature.toString('base64')
let header = `keyId="${config.baseurl}/federation/u/${user.username}",headers="(request-target) host date",signature="${signature_b64}"`
request({
url: inbox,
headers: {
'Host': targetDomain,
'Date': d.toUTCString(),
'Signature': header
},
method: 'POST',
json: true,
body: message
}, function (error, response){
if (error) {
console.log('Error:', error, response.body)
}
else {
console.log('Response:', response.body)
}
})
return res.status(200);
}
function sendAcceptMessage (body, user, domain, req, res, targetOrigin) {
const guid = crypto.randomBytes(16).toString('hex')
let message = {
'@context': 'https://www.w3.org/ns/activitystreams',
'id': `${config.baseurl}/federation/${guid}`,
'type': 'Accept',
'actor': `${config.baseurl}/federation/u/${user.username}`,
'object': body,
}
signAndSend(message, user, domain, req, res, targetOrigin)
}
router.post('/inbox', async (req, res) => {
const bodyParser = require('body-parser')
const cors = require('cors')
const Follows = require('./follows')
const Users = require('./users')
/**
* Federation is calling!
* ref: https://www.w3.org/TR/activitypub/#Overview
*/
router.use(cors())
router.use(bodyParser.json({type: 'application/activity+json'}))
// get any message coming from federation
// Federation is calling!
router.post('/u/:name/inbox', async (req, res) => {
const b = req.body
console.error('> INBOX ', b)
console.error('> INBOX ', b.type, b)
const targetOrigin = new URL(b.actor).origin
const domain = new URL(config.baseurl).host
switch(b.type) {
case 'Follow':
if (typeof b.object !== 'string') return
const username = b.object.replace(`${config.baseurl}/federation/u/`, '')
console.error('someone wants to follow ' + username)
const user = await User.findOne({ where: { username }})
if (!user) {
console.error('No user found!')
return
}
sendAcceptMessage(b, user, domain, req, res, targetOrigin)
console.error('FOLLOWERS ', user.followers)
if (user.followers.indexOf(b.actor) === -1) {
console.error('ok this is a new follower: ', b.actor)
await user.update({ followers: [...user.followers, b.actor] })
}
Follows.follow(req, res, b, targetOrigin, domain)
break
case 'Undo':
Follows.unfollow(req, res, b, targetOrigin, domain)
break
case 'Announce':
console.error('This is a boost ?')
break
case 'Note':
console.error('this is a note ! I should not receive this')
break
case 'Create':
console.error('Create what? This is probably a reply', b.object.type)
break
}
})
router.get('/u/:name', async (req, res) => {
const name = req.params.name
if (!name) return res.status(400).send('Bad request.')
const user = await User.findOne({where: { username: name }})
if (!user) return res.status(404).send(`No record found for ${name}`)
const ret = {
'@context': [
'https://www.w3.org/ns/activitystreams',
'https://w3id.org/security/v1'
],
id: `${config.baseurl}/federation/u/${name}`,
type: 'Person',
preferredUsername: name,
nodeInfo2Url: `${config.baseurl}/.well-known/x-nodeinfo2`,
inbox: `${config.baseurl}/federation/inbox`,
followers: `${config.baseurl}/federation/u/${name}/followers`,
publicKey: {
id: `${config.baseurl}/federation/u/${name}#main-key`,
owner: `${config.baseurl}/federation/u/${name}`,
publicKeyPem: get(user, 'rsa.publicKey', '')
}
}
res.json(ret)
})
router.get('/u/:name/followers', async (req, res) => {
const name = req.params.name
if (!name) return res.status(400).send('Bad request.')
const user = await User.findOne({where: { username: name }})
if (!user) return res.status(404).send(`No record found for ${name}`)
const ret = {
'@context': [ 'https://www.w3.org/ns/activitystreams' ],
id: `${config.baseurl}/federation/u/${name}/followers`,
type: 'OrderedCollection',
totalItems: user.followers.length,
first: {
id: `${config.baseurl}/federation/u/${name}/followers?page=1`,
type: 'OrderedCollectionPage',
totalItems: user.followers.length,
partOf: `${config.baseurl}/federation/u/${name}/followers`,
orderedItems: user.followers,
}
}
res.json(ret)
})
router.get('/u/:name/outbox', Users.outbox)
router.get('/u/:name/followers', Users.followers)
router.get('/u/:name', Users.get)
module.exports = router
const { user: User } = require('../api/models')
const config = require('config')
module.exports = {
async get (req, res) {
const name = req.params.name
if (!name) return res.status(400).send('Bad request.')
const user = await User.findOne({where: { username: name }})
if (!user) return res.status(404).send(`No record found for ${name}`)
const ret = {
'@context': [
'https://www.w3.org/ns/activitystreams',
'https://w3id.org/security/v1'
],
id: `${config.baseurl}/federation/u/${name}`,
type: 'Person',
preferredUsername: name,
nodeInfo2Url: `${config.baseurl}/.well-known/x-nodeinfo2`,
inbox: `${config.baseurl}/federation/u/${name}/inbox`,
outbox: `${config.baseurl}/federation/u/${name}/outbox`,
followers: `${config.baseurl}/federation/u/${name}/followers`,
publicKey: {
id: `${config.baseurl}/federation/u/${name}#main-key`,
owner: `${config.baseurl}/federation/u/${name}`,
publicKeyPem: get(user, 'rsa.publicKey', '')
}
}
res.json(ret)
},
async followers (req, res) {
const name = req.params.name
if (!name) return res.status(400).send('Bad request.')
const user = await User.findOne({where: { username: name }})
if (!user) return res.status(404).send(`No record found for ${name}`)
const ret = {
'@context': [ 'https://www.w3.org/ns/activitystreams' ],
id: `${config.baseurl}/federation/u/${name}/followers`,
type: 'OrderedCollection',
totalItems: user.followers.length,
first: {
id: `${config.baseurl}/federation/u/${name}/followers?page=1`,
type: 'OrderedCollectionPage',
totalItems: user.followers.length,
partOf: `${config.baseurl}/federation/u/${name}/followers`,
orderedItems: user.followers,
}
}
res.json(ret)
},
outbox (req, res) {
console.error('Inside outbox, should return all events from this user')
}
}
\ No newline at end of file
......@@ -3,7 +3,6 @@ const path = require('path')
const express = require('express')
const consola = require('consola')
const morgan = require('morgan')
const bodyParser = require('body-parser')
const cors = require('cors')
const { Nuxt, Builder } = require('nuxt')
......@@ -37,7 +36,7 @@ async function start() {
// federation api / activitypub / webfinger / nodeinfo
app.use('/.well-known/webfinger', cors(), require('./federation/webfinger'))
app.use('/.well-known/x-nodeinfo2', cors(), require('./federation/nodeinfo'))
app.use('/federation', cors(), bodyParser.join({type: 'applicatoin/activity+json'}), require('./federation'))
app.use('/federation', require('./federation'))
// Give nuxt middleware to express
app.use(nuxt.render)
......
......@@ -1953,6 +1953,13 @@ binary-extensions@^2.0.0:
resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.0.0.tgz#23c0df14f6a88077f5f986c0d167ec03c3d5537c"
integrity sha512-Phlt0plgpIIBOGTT/ehfFnbNlfsDEiqmzE2KRXoX1bLIlir4X/MR+zSyBEkL05ffWgnRSf/DXv+WrUAVr93/ow==
biskviit@1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/biskviit/-/biskviit-1.0.1.tgz#037a0cd4b71b9e331fd90a1122de17dc49e420a7"
integrity sha1-A3oM1LcbnjMf2QoRIt4X3EnkIKc=
dependencies:
psl "^1.1.7"
bl@^1.0.0:
version "1.2.2"
resolved "https://registry.yarnpkg.com/bl/-/bl-1.2.2.tgz#a160911717103c07410cef63ef51b397c025af9c"
......@@ -3681,6 +3688,13 @@ encodeurl@~1.0.2:
resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59"
integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=
encoding@0.1.12:
version "0.1.12"
resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.12.tgz#538b66f3ee62cd1ab51ec323829d1f9480c74beb"
integrity sha1-U4tm8+5izRq1HsMjgp0flIDHS+s=
dependencies:
iconv-lite "~0.4.13"
end-of-stream@^1.0.0, end-of-stream@^1.1.0:
version "1.4.1"
resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.1.tgz#ed29634d19baba463b6ce6b80a37213eab71ec43"
......@@ -4422,6 +4436,14 @@ fast-levenshtein@~2.0.4:
resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917"
integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=
fetch@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/fetch/-/fetch-1.1.0.tgz#0a8279f06be37f9f0ebb567560a30a480da59a2e"
integrity sha1-CoJ58Gvjf58Ou1Z1YKMKSA2lmi4=
dependencies:
biskviit "1.0.1"
encoding "0.1.12"
figgy-pudding@^3.5.1:
version "3.5.1"
resolved "https://registry.yarnpkg.com/figgy-pudding/-/figgy-pudding-3.5.1.tgz#862470112901c727a0e495a80744bd5baa1d6790"
......@@ -5315,7 +5337,7 @@ i18n@^0.8.3:
mustache "*"
sprintf-js ">=1.0.3"
iconv-lite@0.4.24, iconv-lite@^0.4.24, iconv-lite@^0.4.4:
iconv-lite@0.4.24, iconv-lite@^0.4.24, iconv-lite@^0.4.4, iconv-lite@~0.4.13:
version "0.4.24"
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b"
integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==
......@@ -8620,7 +8642,7 @@ pseudomap@^1.0.2:
resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3"
integrity sha1-8FKijacOYYkX7wqKw0wa5aaChrM=
psl@^1.1.24:
psl@^1.1.24, psl@^1.1.7:
version "1.2.0"
resolved "https://registry.yarnpkg.com/psl/-/psl-1.2.0.tgz#df12b5b1b3a30f51c329eacbdef98f3a6e136dc6"
integrity sha512-GEn74ZffufCmkDDLNcl3uuyF/aSD6exEyh1v/ZSdAomB82t6G9hzJVRx0jBmLDW+VfZqks3aScmMw9DszwUalA==
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment