Add compatibility with other Linked Signature algorithms
This commit is contained in:
parent
b83b8dd5ae
commit
df66d81583
28 changed files with 1012 additions and 22 deletions
|
@ -12,7 +12,6 @@ killall -q peertube || true
|
|||
if [ "$1" = "misc" ]; then
|
||||
npm run build -- --light-fr
|
||||
mocha --timeout 5000 --exit --require ts-node/register/type-check --bail server/tests/client.ts \
|
||||
server/tests/activitypub.ts \
|
||||
server/tests/feeds/index.ts \
|
||||
server/tests/misc-endpoints.ts \
|
||||
server/tests/helpers/index.ts
|
||||
|
|
|
@ -28,7 +28,7 @@ import { checkMissedConfig, checkFFmpeg } from './server/initializers/checker-be
|
|||
|
||||
// Do not use barrels because we don't want to load all modules here (we need to initialize database first)
|
||||
import { logger } from './server/helpers/logger'
|
||||
import { API_VERSION, CONFIG, CACHE } from './server/initializers/constants'
|
||||
import { API_VERSION, CONFIG, CACHE, HTTP_SIGNATURE } from './server/initializers/constants'
|
||||
|
||||
const missed = checkMissedConfig()
|
||||
if (missed.length !== 0) {
|
||||
|
@ -96,6 +96,7 @@ import { RemoveOldJobsScheduler } from './server/lib/schedulers/remove-old-jobs-
|
|||
import { UpdateVideosScheduler } from './server/lib/schedulers/update-videos-scheduler'
|
||||
import { YoutubeDlUpdateScheduler } from './server/lib/schedulers/youtube-dl-update-scheduler'
|
||||
import { VideosRedundancyScheduler } from './server/lib/schedulers/videos-redundancy-scheduler'
|
||||
import { isHTTPSignatureDigestValid } from './server/helpers/peertube-crypto'
|
||||
|
||||
// ----------- Command line -----------
|
||||
|
||||
|
@ -131,7 +132,11 @@ app.use(morgan('combined', {
|
|||
app.use(bodyParser.urlencoded({ extended: false }))
|
||||
app.use(bodyParser.json({
|
||||
type: [ 'application/json', 'application/*+json' ],
|
||||
limit: '500kb'
|
||||
limit: '500kb',
|
||||
verify: (req: express.Request, _, buf: Buffer, encoding: string) => {
|
||||
const valid = isHTTPSignatureDigestValid(buf, req)
|
||||
if (valid !== true) throw new Error('Invalid digest')
|
||||
}
|
||||
}))
|
||||
// Cookies
|
||||
app.use(cookieParser())
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import * as AsyncLRU from 'async-lru'
|
||||
import * as jsonld from 'jsonld/'
|
||||
import * as jsonld from 'jsonld'
|
||||
import * as jsig from 'jsonld-signatures'
|
||||
|
||||
const nodeDocumentLoader = jsonld.documentLoaders.node()
|
||||
|
@ -17,4 +17,4 @@ jsonld.documentLoader = (url, cb) => {
|
|||
|
||||
jsig.use('jsonld', jsonld)
|
||||
|
||||
export { jsig }
|
||||
export { jsig, jsonld }
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
import { Request } from 'express'
|
||||
import { BCRYPT_SALT_SIZE, HTTP_SIGNATURE, PRIVATE_RSA_KEY_SIZE } from '../initializers'
|
||||
import { ActorModel } from '../models/activitypub/actor'
|
||||
import { bcryptComparePromise, bcryptGenSaltPromise, bcryptHashPromise, createPrivateKey, getPublicKey } from './core-utils'
|
||||
import { jsig } from './custom-jsonld-signature'
|
||||
import { bcryptComparePromise, bcryptGenSaltPromise, bcryptHashPromise, createPrivateKey, getPublicKey, sha256 } from './core-utils'
|
||||
import { jsig, jsonld } from './custom-jsonld-signature'
|
||||
import { logger } from './logger'
|
||||
import { cloneDeep } from 'lodash'
|
||||
import { createVerify } from 'crypto'
|
||||
import { buildDigest } from '../lib/job-queue/handlers/utils/activitypub-http-utils'
|
||||
|
||||
const httpSignature = require('http-signature')
|
||||
|
||||
|
@ -30,21 +33,36 @@ async function cryptPassword (password: string) {
|
|||
|
||||
// HTTP Signature
|
||||
|
||||
function isHTTPSignatureVerified (httpSignatureParsed: any, actor: ActorModel) {
|
||||
function isHTTPSignatureDigestValid (rawBody: Buffer, req: Request): boolean {
|
||||
if (req.headers[HTTP_SIGNATURE.HEADER_NAME] && req.headers['digest']) {
|
||||
return buildDigest(rawBody.toString()) === req.headers['digest']
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
function isHTTPSignatureVerified (httpSignatureParsed: any, actor: ActorModel): boolean {
|
||||
return httpSignature.verifySignature(httpSignatureParsed, actor.publicKey) === true
|
||||
}
|
||||
|
||||
function parseHTTPSignature (req: Request) {
|
||||
return httpSignature.parse(req, { authorizationHeaderName: HTTP_SIGNATURE.HEADER_NAME })
|
||||
function parseHTTPSignature (req: Request, clockSkew?: number) {
|
||||
return httpSignature.parse(req, { authorizationHeaderName: HTTP_SIGNATURE.HEADER_NAME, clockSkew })
|
||||
}
|
||||
|
||||
// JSONLD
|
||||
|
||||
function isJsonLDSignatureVerified (fromActor: ActorModel, signedDocument: any) {
|
||||
async function isJsonLDSignatureVerified (fromActor: ActorModel, signedDocument: any): Promise<boolean> {
|
||||
if (signedDocument.signature.type === 'RsaSignature2017') {
|
||||
// Mastodon algorithm
|
||||
const res = await isJsonLDRSA2017Verified(fromActor, signedDocument)
|
||||
// Success? If no, try with our library
|
||||
if (res === true) return true
|
||||
}
|
||||
|
||||
const publicKeyObject = {
|
||||
'@context': jsig.SECURITY_CONTEXT_URL,
|
||||
id: fromActor.url,
|
||||
type: 'CryptographicKey',
|
||||
type: 'CryptographicKey',
|
||||
owner: fromActor.url,
|
||||
publicKeyPem: fromActor.publicKey
|
||||
}
|
||||
|
@ -69,6 +87,44 @@ function isJsonLDSignatureVerified (fromActor: ActorModel, signedDocument: any)
|
|||
})
|
||||
}
|
||||
|
||||
// Backward compatibility with "other" implementations
|
||||
async function isJsonLDRSA2017Verified (fromActor: ActorModel, signedDocument: any) {
|
||||
function hash (obj: any): Promise<any> {
|
||||
return jsonld.promises
|
||||
.normalize(obj, {
|
||||
algorithm: 'URDNA2015',
|
||||
format: 'application/n-quads'
|
||||
})
|
||||
.then(res => sha256(res))
|
||||
}
|
||||
|
||||
const signatureCopy = cloneDeep(signedDocument.signature)
|
||||
Object.assign(signatureCopy, {
|
||||
'@context': [
|
||||
'https://w3id.org/security/v1',
|
||||
{ RsaSignature2017: 'https://w3id.org/security#RsaSignature2017' }
|
||||
]
|
||||
})
|
||||
delete signatureCopy.type
|
||||
delete signatureCopy.id
|
||||
delete signatureCopy.signatureValue
|
||||
|
||||
const docWithoutSignature = cloneDeep(signedDocument)
|
||||
delete docWithoutSignature.signature
|
||||
|
||||
const [ documentHash, optionsHash ] = await Promise.all([
|
||||
hash(docWithoutSignature),
|
||||
hash(signatureCopy)
|
||||
])
|
||||
|
||||
const toVerify = optionsHash + documentHash
|
||||
|
||||
const verify = createVerify('RSA-SHA256')
|
||||
verify.update(toVerify, 'utf8')
|
||||
|
||||
return verify.verify(fromActor.publicKey, signedDocument.signature.signatureValue, 'base64')
|
||||
}
|
||||
|
||||
function signJsonLDObject (byActor: ActorModel, data: any) {
|
||||
const options = {
|
||||
privateKeyPem: byActor.privateKey,
|
||||
|
@ -82,6 +138,7 @@ function signJsonLDObject (byActor: ActorModel, data: any) {
|
|||
// ---------------------------------------------------------------------------
|
||||
|
||||
export {
|
||||
isHTTPSignatureDigestValid,
|
||||
parseHTTPSignature,
|
||||
isHTTPSignatureVerified,
|
||||
isJsonLDSignatureVerified,
|
||||
|
|
|
@ -535,7 +535,7 @@ const ACTIVITY_PUB_ACTOR_TYPES: { [ id: string ]: ActivityPubActorType } = {
|
|||
const HTTP_SIGNATURE = {
|
||||
HEADER_NAME: 'signature',
|
||||
ALGORITHM: 'rsa-sha256',
|
||||
HEADERS_TO_SIGN: [ 'date', 'host', 'digest', '(request-target)' ]
|
||||
HEADERS_TO_SIGN: [ '(request-target)', 'host', 'date', 'digest' ]
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
|
|
@ -38,15 +38,20 @@ async function buildSignedRequestOptions (payload: Payload) {
|
|||
}
|
||||
}
|
||||
|
||||
function buildGlobalHeaders (body: object) {
|
||||
const digest = 'SHA-256=' + sha256(JSON.stringify(body), 'base64')
|
||||
|
||||
function buildGlobalHeaders (body: any) {
|
||||
return {
|
||||
'Digest': digest
|
||||
'Digest': buildDigest(body)
|
||||
}
|
||||
}
|
||||
|
||||
function buildDigest (body: any) {
|
||||
const rawBody = typeof body === 'string' ? body : JSON.stringify(body)
|
||||
|
||||
return 'SHA-256=' + sha256(rawBody, 'base64')
|
||||
}
|
||||
|
||||
export {
|
||||
buildDigest,
|
||||
buildGlobalHeaders,
|
||||
computeBody,
|
||||
buildSignedRequestOptions
|
||||
|
|
|
@ -53,7 +53,8 @@ function executeIfActivityPub (fun: RequestHandler | RequestHandler[]) {
|
|||
|
||||
export {
|
||||
checkSignature,
|
||||
executeIfActivityPub
|
||||
executeIfActivityPub,
|
||||
checkHttpSignature
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
@ -94,7 +95,7 @@ async function checkHttpSignature (req: Request, res: Response) {
|
|||
async function checkJsonLDSignature (req: Request, res: Response) {
|
||||
const signatureObject: ActivityPubSignature = req.body.signature
|
||||
|
||||
if (!signatureObject.creator) {
|
||||
if (!signatureObject || !signatureObject.creator) {
|
||||
res.sendStatus(403)
|
||||
return false
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
import * as chai from 'chai'
|
||||
import 'mocha'
|
||||
import { flushTests, killallServers, makeActivityPubGetRequest, runServer, ServerInfo, setAccessTokensToServers } from './utils'
|
||||
import { flushTests, killallServers, makeActivityPubGetRequest, runServer, ServerInfo, setAccessTokensToServers } from '../../utils'
|
||||
|
||||
const expect = chai.expect
|
||||
|
182
server/tests/api/activitypub/helpers.ts
Normal file
182
server/tests/api/activitypub/helpers.ts
Normal file
|
@ -0,0 +1,182 @@
|
|||
/* tslint:disable:no-unused-expression */
|
||||
|
||||
import 'mocha'
|
||||
import { expect } from 'chai'
|
||||
import { buildRequestStub } from '../../utils'
|
||||
import { isHTTPSignatureVerified, isJsonLDSignatureVerified, parseHTTPSignature } from '../../../helpers/peertube-crypto'
|
||||
import { cloneDeep } from 'lodash'
|
||||
import { buildSignedActivity } from '../../../helpers/activitypub'
|
||||
|
||||
describe('Test activity pub helpers', function () {
|
||||
describe('When checking the Linked Signature', function () {
|
||||
|
||||
it('Should fail with an invalid Mastodon signature', async function () {
|
||||
const body = require('./json/mastodon/create-bad-signature.json')
|
||||
const publicKey = require('./json/mastodon/public-key.json').publicKey
|
||||
const fromActor = { publicKey, url: 'http://localhost:9002/accounts/peertube' }
|
||||
|
||||
const result = await isJsonLDSignatureVerified(fromActor as any, body)
|
||||
|
||||
expect(result).to.be.false
|
||||
})
|
||||
|
||||
it('Should fail with an invalid public key', async function () {
|
||||
const body = require('./json/mastodon/create.json')
|
||||
const publicKey = require('./json/mastodon/bad-public-key.json').publicKey
|
||||
const fromActor = { publicKey, url: 'http://localhost:9002/accounts/peertube' }
|
||||
|
||||
const result = await isJsonLDSignatureVerified(fromActor as any, body)
|
||||
|
||||
expect(result).to.be.false
|
||||
})
|
||||
|
||||
it('Should succeed with a valid Mastodon signature', async function () {
|
||||
const body = require('./json/mastodon/create.json')
|
||||
const publicKey = require('./json/mastodon/public-key.json').publicKey
|
||||
const fromActor = { publicKey, url: 'http://localhost:9002/accounts/peertube' }
|
||||
|
||||
const result = await isJsonLDSignatureVerified(fromActor as any, body)
|
||||
|
||||
expect(result).to.be.true
|
||||
})
|
||||
|
||||
it('Should fail with an invalid PeerTube signature', async function () {
|
||||
const keys = require('./json/peertube/invalid-keys.json')
|
||||
const body = require('./json/peertube/announce-without-context.json')
|
||||
|
||||
const actorSignature = { url: 'http://localhost:9002/accounts/peertube', privateKey: keys.privateKey }
|
||||
const signedBody = await buildSignedActivity(actorSignature as any, body)
|
||||
|
||||
const fromActor = { publicKey: keys.publicKey, url: 'http://localhost:9002/accounts/peertube' }
|
||||
const result = await isJsonLDSignatureVerified(fromActor as any, signedBody)
|
||||
|
||||
expect(result).to.be.false
|
||||
})
|
||||
|
||||
it('Should fail with an invalid PeerTube URL', async function () {
|
||||
const keys = require('./json/peertube/keys.json')
|
||||
const body = require('./json/peertube/announce-without-context.json')
|
||||
|
||||
const actorSignature = { url: 'http://localhost:9002/accounts/peertube', privateKey: keys.privateKey }
|
||||
const signedBody = await buildSignedActivity(actorSignature as any, body)
|
||||
|
||||
const fromActor = { publicKey: keys.publicKey, url: 'http://localhost:9003/accounts/peertube' }
|
||||
const result = await isJsonLDSignatureVerified(fromActor as any, signedBody)
|
||||
|
||||
expect(result).to.be.false
|
||||
})
|
||||
|
||||
it('Should succeed with a valid PeerTube signature', async function () {
|
||||
const keys = require('./json/peertube/keys.json')
|
||||
const body = require('./json/peertube/announce-without-context.json')
|
||||
|
||||
const actorSignature = { url: 'http://localhost:9002/accounts/peertube', privateKey: keys.privateKey }
|
||||
const signedBody = await buildSignedActivity(actorSignature as any, body)
|
||||
|
||||
const fromActor = { publicKey: keys.publicKey, url: 'http://localhost:9002/accounts/peertube' }
|
||||
const result = await isJsonLDSignatureVerified(fromActor as any, signedBody)
|
||||
|
||||
expect(result).to.be.true
|
||||
})
|
||||
})
|
||||
|
||||
describe('When checking HTTP signature', function () {
|
||||
it('Should fail with an invalid http signature', async function () {
|
||||
const req = buildRequestStub()
|
||||
req.method = 'POST'
|
||||
req.url = '/accounts/ronan/inbox'
|
||||
|
||||
const mastodonObject = cloneDeep(require('./json/mastodon/bad-http-signature.json'))
|
||||
req.body = mastodonObject.body
|
||||
req.headers = mastodonObject.headers
|
||||
req.headers.signature = 'Signature ' + req.headers.signature
|
||||
|
||||
const parsed = parseHTTPSignature(req, 3600 * 365 * 3)
|
||||
const publicKey = require('./json/mastodon/public-key.json').publicKey
|
||||
|
||||
const actor = { publicKey }
|
||||
const verified = isHTTPSignatureVerified(parsed, actor as any)
|
||||
|
||||
expect(verified).to.be.false
|
||||
})
|
||||
|
||||
it('Should fail with an invalid public key', async function () {
|
||||
const req = buildRequestStub()
|
||||
req.method = 'POST'
|
||||
req.url = '/accounts/ronan/inbox'
|
||||
|
||||
const mastodonObject = cloneDeep(require('./json/mastodon/http-signature.json'))
|
||||
req.body = mastodonObject.body
|
||||
req.headers = mastodonObject.headers
|
||||
req.headers.signature = 'Signature ' + req.headers.signature
|
||||
|
||||
const parsed = parseHTTPSignature(req, 3600 * 365 * 3)
|
||||
const publicKey = require('./json/mastodon/bad-public-key.json').publicKey
|
||||
|
||||
const actor = { publicKey }
|
||||
const verified = isHTTPSignatureVerified(parsed, actor as any)
|
||||
|
||||
expect(verified).to.be.false
|
||||
})
|
||||
|
||||
it('Should fail because of clock skew', async function () {
|
||||
const req = buildRequestStub()
|
||||
req.method = 'POST'
|
||||
req.url = '/accounts/ronan/inbox'
|
||||
|
||||
const mastodonObject = cloneDeep(require('./json/mastodon/http-signature.json'))
|
||||
req.body = mastodonObject.body
|
||||
req.headers = mastodonObject.headers
|
||||
req.headers.signature = 'Signature ' + req.headers.signature
|
||||
|
||||
let errored = false
|
||||
try {
|
||||
parseHTTPSignature(req)
|
||||
} catch {
|
||||
errored = true
|
||||
}
|
||||
|
||||
expect(errored).to.be.true
|
||||
})
|
||||
|
||||
it('Should fail without scheme', async function () {
|
||||
const req = buildRequestStub()
|
||||
req.method = 'POST'
|
||||
req.url = '/accounts/ronan/inbox'
|
||||
|
||||
const mastodonObject = cloneDeep(require('./json/mastodon/http-signature.json'))
|
||||
req.body = mastodonObject.body
|
||||
req.headers = mastodonObject.headers
|
||||
|
||||
let errored = false
|
||||
try {
|
||||
parseHTTPSignature(req, 3600 * 365 * 3)
|
||||
} catch {
|
||||
errored = true
|
||||
}
|
||||
|
||||
expect(errored).to.be.true
|
||||
})
|
||||
|
||||
it('Should succeed with a valid signature', async function () {
|
||||
const req = buildRequestStub()
|
||||
req.method = 'POST'
|
||||
req.url = '/accounts/ronan/inbox'
|
||||
|
||||
const mastodonObject = cloneDeep(require('./json/mastodon/http-signature.json'))
|
||||
req.body = mastodonObject.body
|
||||
req.headers = mastodonObject.headers
|
||||
req.headers.signature = 'Signature ' + req.headers.signature
|
||||
|
||||
const parsed = parseHTTPSignature(req, 3600 * 365 * 3)
|
||||
const publicKey = require('./json/mastodon/public-key.json').publicKey
|
||||
|
||||
const actor = { publicKey }
|
||||
const verified = isHTTPSignatureVerified(parsed, actor as any)
|
||||
|
||||
expect(verified).to.be.true
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
})
|
3
server/tests/api/activitypub/index.ts
Normal file
3
server/tests/api/activitypub/index.ts
Normal file
|
@ -0,0 +1,3 @@
|
|||
import './client'
|
||||
import './helpers'
|
||||
import './security'
|
|
@ -0,0 +1,93 @@
|
|||
{
|
||||
"headers": {
|
||||
"user-agent": "http.rb/3.3.0 (Mastodon/2.5.0; +http://localhost:3000/)",
|
||||
"host": "localhost",
|
||||
"date": "Mon, 22 Oct 2018 13:34:22 GMT",
|
||||
"accept-encoding": "gzip",
|
||||
"digest": "SHA-256=FEr5j2WSSfdEMcG3NTOXuGU0lUchfTJx4+BtUlWOwDk=",
|
||||
"content-type": "application/activity+json",
|
||||
"signature": "keyId=\"http://localhost:3000/users/ronan2#main-key\",algorithm=\"rsa-sha256\",headers=\"(request-target) host date digest content-type\",signature=\"oLKbgxdFXdXsHJ3x/UsG9Svu7oa8Dyqiy6Jif4wqNuhAqRVMRaG18f+dd2OcfFX3XRGF8p8flZkU6vvoEQBauTwGRGcgXAJuKC1zYIWGk+PeiW8lNUnE4qGapWcTiFnIo7FKauNdsgqg/tvgs1pQIdHkDDjZMI64twP7sTN/4vG1PCq+kyqi/DM+ORLi/W7vFuLVHt2Iz7ikfw/R3/mMtS4FwLops+tVYBQ2iQ9DVRhTwLKVbeL/LLVB/tdGzNZ4F4nImBAQQ9I7WpPM6J/k+cBmoEbrUKs8ptx9gbX3OSsl5wlvPVMNzU9F9yb2MrB/Y/J4qssKz+LbiaktKGj7OQ==\"",
|
||||
"content-length": "2815"
|
||||
},
|
||||
"body": {
|
||||
"@context": [
|
||||
"https://www.w3.org/ns/activitystreams",
|
||||
"https://w3id.org/security/v1",
|
||||
{
|
||||
"manuallyApprovesFollowers": "as:manuallyApprovesFollowers",
|
||||
"sensitive": "as:sensitive",
|
||||
"movedTo": {
|
||||
"@id": "as:movedTo",
|
||||
"@type": "@id"
|
||||
},
|
||||
"Hashtag": "as:Hashtag",
|
||||
"ostatus": "http://ostatus.org#",
|
||||
"atomUri": "ostatus:atomUri",
|
||||
"inReplyToAtomUri": "ostatus:inReplyToAtomUri",
|
||||
"conversation": "ostatus:conversation",
|
||||
"toot": "http://joinmastodon.org/ns#",
|
||||
"Emoji": "toot:Emoji",
|
||||
"focalPoint": {
|
||||
"@container": "@list",
|
||||
"@id": "toot:focalPoint"
|
||||
},
|
||||
"featured": {
|
||||
"@id": "toot:featured",
|
||||
"@type": "@id"
|
||||
},
|
||||
"schema": "http://schema.org#",
|
||||
"PropertyValue": "schema:PropertyValue",
|
||||
"value": "schema:value"
|
||||
}
|
||||
],
|
||||
"id": "http://localhost:3000/users/ronan2/statuses/100939547203370948/activity",
|
||||
"type": "Create",
|
||||
"actor": "http://localhost:3000/users/ronan2",
|
||||
"published": "2018-10-22T13:34:18Z",
|
||||
"to": [
|
||||
"https://www.w3.org/ns/activitystreams#Public"
|
||||
],
|
||||
"cc": [
|
||||
"http://localhost:3000/users/ronan2/followers",
|
||||
"http://localhost:9000/accounts/ronan"
|
||||
],
|
||||
"object": {
|
||||
"id": "http://localhost:3000/users/ronan2/statuses/100939547203370948",
|
||||
"type": "Note",
|
||||
"summary": null,
|
||||
"inReplyTo": "http://localhost:9000/videos/watch/90e6f8ed-b369-423c-b0c8-f44e5350c752",
|
||||
"published": "2018-10-22T13:34:18Z",
|
||||
"url": "http://localhost:3000/@ronan2/100939547203370948",
|
||||
"attributedTo": "http://localhost:3000/users/ronan2",
|
||||
"to": [
|
||||
"https://www.w3.org/ns/activitystreams#Public"
|
||||
],
|
||||
"cc": [
|
||||
"http://localhost:3000/users/ronan2/followers",
|
||||
"http://localhost:9000/accounts/ronan"
|
||||
],
|
||||
"sensitive": false,
|
||||
"atomUri": "http://localhost:3000/users/ronan2/statuses/100939547203370948",
|
||||
"inReplyToAtomUri": "http://localhost:9000/videos/watch/90e6f8ed-b369-423c-b0c8-f44e5350c752",
|
||||
"conversation": "tag:localhost:3000,2018-10-19:objectId=72:objectType=Conversation",
|
||||
"content": "<p><span class=\"h-card\"><a href=\"http://localhost:9000/accounts/ronan\" class=\"u-url mention\">@<span>ronan</span></a></span> zergzerg</p>",
|
||||
"contentMap": {
|
||||
"en": "<p><span class=\"h-card\"><a href=\"http://localhost:9000/accounts/ronan\" class=\"u-url mention\">@<span>ronan</span></a></span> zergzerg</p>"
|
||||
},
|
||||
"attachment": [],
|
||||
"tag": [
|
||||
{
|
||||
"type": "Mention",
|
||||
"href": "http://localhost:9000/accounts/ronan",
|
||||
"name": "@ronan@localhost:9000"
|
||||
}
|
||||
]
|
||||
},
|
||||
"signature": {
|
||||
"type": "RsaSignature2017",
|
||||
"creator": "http://localhost:3000/users/ronan2#main-key",
|
||||
"created": "2018-10-22T13:34:19Z",
|
||||
"signatureValue": "x+xL4l8ERziYVhwEafHJyBQOInvNZ0gV4ccYd9AtFYeGJagc8fY6jjjhbDRCD7yMhgTjBX69z20MXnDuwpmM6wej3dt1wLKdIyXVViO84nAlqFz7KmNxtk5lDnAVX/vttscT5YUFvw4dbPT2mQiEd1lKbaLftRiIPEomZpQ37+fUkQdcPrnhruPAISO/Sof1n1LFW4mYIffozteQSZBH6HaCVp+MRMIhdMi5e8w7PD48/cZz8D/EU8Vqi91FM76/3tMqg6nLqQ+8bq74Jvt2kzwZlIufe+I55QMpZOmF6hGIJEt+R0JXdjQbtgcELONmNj2dr8sAlzu7zKlAGuJ24Q=="
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,93 @@
|
|||
{
|
||||
"headers": {
|
||||
"user-agent": "http.rb/3.3.0 (Mastodon/2.5.0; +http://localhost:3000/)",
|
||||
"host": "localhost",
|
||||
"date": "Mon, 22 Oct 2018 13:34:22 GMT",
|
||||
"accept-encoding": "gzip",
|
||||
"digest": "SHA-256=FEr5j2WSSfdEMcG3NTOXuGU0lUchfTJx4+BtUlWOwDk=",
|
||||
"content-type": "application/activity+json",
|
||||
"signature": "keyId=\"http://localhost:3000/users/ronan2#main-key\",algorithm=\"rsa-sha256\",headers=\"(request-target) host date digest content-type\",signature=\"oLKbgxdFXdXsHJ3x/UsG9Svu7oa8Dyqiy6Jif4wqNuhAqRVMRaG18f+dd2OcfFX3XRGF8p8flZkU6vvoEQBauTwGRGcgXAJuKC1zYIWGk+PeiW8lNUnE4qGapWcTiFnIo7FKauNdsgqg/tvgs1pQIdHkDDjZMI64twP7sTN/4vG1PCq+kyqi/DM+ORLi/W7vFuLVHt2Iz7ikfw/R3/mMtS4FwLops+tVYBQ2iQ9DVRhTwLKVbeL/LLVB/tdGzNZ4F4nImBAQQ9I7WpPM6J/k+cBmoEbrUKs8ptx9gbX3OSsl4wlvPVMNzU9F9yb2MrB/Y/J4qssKz+LbiaktKGj7OQ==\"",
|
||||
"content-length": "2815"
|
||||
},
|
||||
"body": {
|
||||
"@context": [
|
||||
"https://www.w3.org/ns/activitystreams",
|
||||
"https://w3id.org/security/v1",
|
||||
{
|
||||
"manuallyApprovesFollowers": "as:manuallyApprovesFollowers",
|
||||
"sensitive": "as:sensitive",
|
||||
"movedTo": {
|
||||
"@id": "as:movedTo",
|
||||
"@type": "@id"
|
||||
},
|
||||
"Hashtag": "as:Hashtag",
|
||||
"ostatus": "http://ostatus.org#",
|
||||
"atomUri": "ostatus:atomUri",
|
||||
"inReplyToAtomUri": "ostatus:inReplyToAtomUri",
|
||||
"conversation": "ostatus:conversation",
|
||||
"toot": "http://joinmastodon.org/ns#",
|
||||
"Emoji": "toot:Emoji",
|
||||
"focalPoint": {
|
||||
"@container": "@list",
|
||||
"@id": "toot:focalPoint"
|
||||
},
|
||||
"featured": {
|
||||
"@id": "toot:featured",
|
||||
"@type": "@id"
|
||||
},
|
||||
"schema": "http://schema.org#",
|
||||
"PropertyValue": "schema:PropertyValue",
|
||||
"value": "schema:value"
|
||||
}
|
||||
],
|
||||
"id": "http://localhost:3000/users/ronan2/statuses/100939547203370948/activity",
|
||||
"type": "Create",
|
||||
"actor": "http://localhost:3000/users/ronan2",
|
||||
"published": "2018-10-22T13:34:18Z",
|
||||
"to": [
|
||||
"https://www.w3.org/ns/activitystreams#Public"
|
||||
],
|
||||
"cc": [
|
||||
"http://localhost:3000/users/ronan2/followers",
|
||||
"http://localhost:9000/accounts/ronan"
|
||||
],
|
||||
"object": {
|
||||
"id": "http://localhost:3000/users/ronan2/statuses/100939547203370948",
|
||||
"type": "Note",
|
||||
"summary": null,
|
||||
"inReplyTo": "http://localhost:9000/videos/watch/90e6f8ed-b369-423c-b0c8-f44e5350c752",
|
||||
"published": "2018-10-22T13:34:18Z",
|
||||
"url": "http://localhost:3000/@ronan2/100939547203370948",
|
||||
"attributedTo": "http://localhost:3000/users/ronan2",
|
||||
"to": [
|
||||
"https://www.w3.org/ns/activitystreams#Public"
|
||||
],
|
||||
"cc": [
|
||||
"http://localhost:3000/users/ronan2/followers",
|
||||
"http://localhost:9000/accounts/ronan"
|
||||
],
|
||||
"sensitive": false,
|
||||
"atomUri": "http://localhost:3000/users/ronan2/statuses/100939547203370948",
|
||||
"inReplyToAtomUri": "http://localhost:9000/videos/watch/90e6f8ed-b369-423c-b0c8-f44e5350c752",
|
||||
"conversation": "tag:localhost:3000,2018-10-19:objectId=72:objectType=Conversation",
|
||||
"content": "<p><span class=\"h-card\"><a href=\"http://localhost:9000/accounts/ronan\" class=\"u-url mention\">@<span>ronan</span></a></span> zergzerg</p>",
|
||||
"contentMap": {
|
||||
"en": "<p><span class=\"h-card\"><a href=\"http://localhost:9000/accounts/ronan\" class=\"u-url mention\">@<span>ronan</span></a></span> zergzerg</p>"
|
||||
},
|
||||
"attachment": [],
|
||||
"tag": [
|
||||
{
|
||||
"type": "Mention",
|
||||
"href": "http://localhost:9000/accounts/ronan",
|
||||
"name": "@ronan@localhost:9000"
|
||||
}
|
||||
]
|
||||
},
|
||||
"signature": {
|
||||
"type": "RsaSignature2017",
|
||||
"creator": "http://localhost:3000/users/ronan2#main-key",
|
||||
"created": "2018-10-22T13:34:19Z",
|
||||
"signatureValue": "x+xL4l8ERziYVhwEafHJyBQOInvNZ0gV4ccYd9AtFYeGJagc8fY6jjjhbDRCD7yMhgTjBX69z20MXnDuwpmM6wej3dt1wLKdIyXVViO84nAlqFz7KmNxtk5lDnAVX/vttscT5YUFvw4dbPT2mQiEd1lKbaLftRiIPEomZpQ37+fUkQdcPrnhruPAISO/Sof1n1LFW4mYIffozteQSZBH6HaCVp+MRMIhdMi5e8w7PD48/cZz8D/EU8Vqi91FM76/3tMqg6nLqQ+8bq74Jvt2kzwZlIufe+I55QMpZOmF6hGIJEt+R0JXdjQbtgcELONmNj2dr8sAlzu7zKlAGuJ24Q=="
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"publicKey": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0YyuthHtWWgDe0Fdgdp2\ndC5dTJsRqW6pFw5omIYYYjoES/WRewhVxEA54BhmxD3L1zChfx131N1TS8jVowhW\nm999jpUffKCCvLgYKIXETJDHiDeMONVx8wp7v9fS1HiFXo/E5und39gUMs14CMFZ\n6PE5jRV3r4XIKQJHQl7/X5n5FOb2934K+1TKUeBkbft/AushlKatYQakt3qHxpwx\nFvE+JjGo7QTnzdjaOx/e5QvojdGi2Kx4+jl77j2WVcSo5lOBz04OAVJtChtn82vS\nulPdDh3hZcDn+WK67yAhGP6AnzvOybZZS4zowlKiQ3kqjVVXKdl8gAsL4Y7MZ40R\nJQIDAQAB\n-----END PUBLIC KEY-----\n"
|
||||
}
|
|
@ -0,0 +1,81 @@
|
|||
{
|
||||
"@context": [
|
||||
"https://www.w3.org/ns/activitystreams",
|
||||
"https://w3id.org/security/v1",
|
||||
{
|
||||
"manuallyApprovesFollowers": "as:manuallyApprovesFollowers",
|
||||
"sensitive": "as:sensitive",
|
||||
"movedTo": {
|
||||
"@id": "as:movedTo",
|
||||
"@type": "@id"
|
||||
},
|
||||
"Hashtag": "as:Hashtag",
|
||||
"ostatus": "http://ostatus.org#",
|
||||
"atomUri": "ostatus:atomUri",
|
||||
"inReplyToAtomUri": "ostatus:inReplyToAtomUri",
|
||||
"conversation": "ostatus:conversation",
|
||||
"toot": "http://joinmastodon.org/ns#",
|
||||
"Emoji": "toot:Emoji",
|
||||
"focalPoint": {
|
||||
"@container": "@list",
|
||||
"@id": "toot:focalPoint"
|
||||
},
|
||||
"featured": {
|
||||
"@id": "toot:featured",
|
||||
"@type": "@id"
|
||||
},
|
||||
"schema": "http://schema.org#",
|
||||
"PropertyValue": "schema:PropertyValue",
|
||||
"value": "schema:value"
|
||||
}
|
||||
],
|
||||
"id": "http://localhost:3000/users/ronan2/statuses/100939345950887698/activity",
|
||||
"type": "Create",
|
||||
"actor": "http://localhost:3000/users/ronan2",
|
||||
"published": "2018-10-22T12:43:07Z",
|
||||
"to": [
|
||||
"https://www.w3.org/ns/activitystreams#Public"
|
||||
],
|
||||
"cc": [
|
||||
"http://localhost:3000/users/ronan2/followers",
|
||||
"http://localhost:9000/accounts/ronan"
|
||||
],
|
||||
"object": {
|
||||
"id": "http://localhost:3000/users/ronan2/statuses/100939345950887698",
|
||||
"type": "Note",
|
||||
"summary": null,
|
||||
"inReplyTo": "http://localhost:9000/videos/watch/90e6f8ed-b369-423c-b0c8-f44e5350c752",
|
||||
"published": "2018-10-22T12:43:07Z",
|
||||
"url": "http://localhost:3000/@ronan2/100939345950887698",
|
||||
"attributedTo": "http://localhost:3000/users/ronan2",
|
||||
"to": [
|
||||
"https://www.w3.org/ns/activitystreams#Public"
|
||||
],
|
||||
"cc": [
|
||||
"http://localhost:3000/users/ronan2/followers",
|
||||
"http://localhost:9000/accounts/ronan"
|
||||
],
|
||||
"sensitive": false,
|
||||
"atomUri": "http://localhost:3000/users/ronan2/statuses/100939345950887698",
|
||||
"inReplyToAtomUri": "http://localhost:9000/videos/watch/90e6f8ed-b369-423c-b0c8-f44e5350c752",
|
||||
"conversation": "tag:localhost:3000,2018-10-19:objectId=72:objectType=Conversation",
|
||||
"content": "<p><span class=\"h-card\"><a href=\"http://localhost:9000/accounts/ronan\" class=\"u-url mention\">@<span>ronan</span></a></span> zerg</p>",
|
||||
"contentMap": {
|
||||
"en": "<p><span class=\"h-card\"><a href=\"http://localhost:9000/accounts/ronan\" class=\"u-url mention\">@<span>ronan</span></a></span> zerg</p>"
|
||||
},
|
||||
"attachment": [],
|
||||
"tag": [
|
||||
{
|
||||
"type": "Mention",
|
||||
"href": "http://localhost:9000/accounts/ronan",
|
||||
"name": "@ronan@localhost:9000"
|
||||
}
|
||||
]
|
||||
},
|
||||
"signature": {
|
||||
"type": "RsaSignature2017",
|
||||
"creator": "http://localhost:3000/users/ronan2#main-key",
|
||||
"created": "2018-10-22T12:43:08Z",
|
||||
"signatureValue": "Vgr8nA0agPr9TcA4BlX+MWhmuE+rBcoIJLpnPbm3E5SnOCXbgjEfEaTLqfuzzkKNsR3PBbkvi3YWK4/DxJ0zmpzSB7yy4NRzluQMVQHqJiFKXAX3Sr3fIrK24xkWW9/F207c1NpFajSGbgnFKBdtFE0e5VqwSrSoOJkZukZW/2ATSnsyzblieuUmvTWpD0PqpUOsynPjw+RqZnqPn0cjw1z2Dm7ZRt3trnyMTXFYZw5U/YuqMY2kpadD6vq780md8kXlJIylxG6ZrlO2jz9fJdnfuVq43d4QFNsBm1K1r2WtNqX+i+wiqh+u3PjF4pzXtl/a3hJOH18IfZnK7I21mQ=="
|
||||
}
|
||||
}
|
81
server/tests/api/activitypub/json/mastodon/create.json
Normal file
81
server/tests/api/activitypub/json/mastodon/create.json
Normal file
|
@ -0,0 +1,81 @@
|
|||
{
|
||||
"@context": [
|
||||
"https://www.w3.org/ns/activitystreams",
|
||||
"https://w3id.org/security/v1",
|
||||
{
|
||||
"manuallyApprovesFollowers": "as:manuallyApprovesFollowers",
|
||||
"sensitive": "as:sensitive",
|
||||
"movedTo": {
|
||||
"@id": "as:movedTo",
|
||||
"@type": "@id"
|
||||
},
|
||||
"Hashtag": "as:Hashtag",
|
||||
"ostatus": "http://ostatus.org#",
|
||||
"atomUri": "ostatus:atomUri",
|
||||
"inReplyToAtomUri": "ostatus:inReplyToAtomUri",
|
||||
"conversation": "ostatus:conversation",
|
||||
"toot": "http://joinmastodon.org/ns#",
|
||||
"Emoji": "toot:Emoji",
|
||||
"focalPoint": {
|
||||
"@container": "@list",
|
||||
"@id": "toot:focalPoint"
|
||||
},
|
||||
"featured": {
|
||||
"@id": "toot:featured",
|
||||
"@type": "@id"
|
||||
},
|
||||
"schema": "http://schema.org#",
|
||||
"PropertyValue": "schema:PropertyValue",
|
||||
"value": "schema:value"
|
||||
}
|
||||
],
|
||||
"id": "http://localhost:3000/users/ronan2/statuses/100939345950887698/activity",
|
||||
"type": "Create",
|
||||
"actor": "http://localhost:3000/users/ronan2",
|
||||
"published": "2018-10-22T12:43:07Z",
|
||||
"to": [
|
||||
"https://www.w3.org/ns/activitystreams#Public"
|
||||
],
|
||||
"cc": [
|
||||
"http://localhost:3000/users/ronan2/followers",
|
||||
"http://localhost:9000/accounts/ronan"
|
||||
],
|
||||
"object": {
|
||||
"id": "http://localhost:3000/users/ronan2/statuses/100939345950887698",
|
||||
"type": "Note",
|
||||
"summary": null,
|
||||
"inReplyTo": "http://localhost:9000/videos/watch/90e6f8ed-b369-423c-b0c8-f44e5350c752",
|
||||
"published": "2018-10-22T12:43:07Z",
|
||||
"url": "http://localhost:3000/@ronan2/100939345950887698",
|
||||
"attributedTo": "http://localhost:3000/users/ronan2",
|
||||
"to": [
|
||||
"https://www.w3.org/ns/activitystreams#Public"
|
||||
],
|
||||
"cc": [
|
||||
"http://localhost:3000/users/ronan2/followers",
|
||||
"http://localhost:9000/accounts/ronan"
|
||||
],
|
||||
"sensitive": false,
|
||||
"atomUri": "http://localhost:3000/users/ronan2/statuses/100939345950887698",
|
||||
"inReplyToAtomUri": "http://localhost:9000/videos/watch/90e6f8ed-b369-423c-b0c8-f44e5350c752",
|
||||
"conversation": "tag:localhost:3000,2018-10-19:objectId=72:objectType=Conversation",
|
||||
"content": "<p><span class=\"h-card\"><a href=\"http://localhost:9000/accounts/ronan\" class=\"u-url mention\">@<span>ronan</span></a></span> zerg</p>",
|
||||
"contentMap": {
|
||||
"en": "<p><span class=\"h-card\"><a href=\"http://localhost:9000/accounts/ronan\" class=\"u-url mention\">@<span>ronan</span></a></span> zerg</p>"
|
||||
},
|
||||
"attachment": [],
|
||||
"tag": [
|
||||
{
|
||||
"type": "Mention",
|
||||
"href": "http://localhost:9000/accounts/ronan",
|
||||
"name": "@ronan@localhost:9000"
|
||||
}
|
||||
]
|
||||
},
|
||||
"signature": {
|
||||
"type": "RsaSignature2017",
|
||||
"creator": "http://localhost:3000/users/ronan2#main-key",
|
||||
"created": "2018-10-22T12:43:08Z",
|
||||
"signatureValue": "VgR8nA0agPr9TcA4BlX+MWhmuE+rBcoIJLpnPbm3E5SnOCXbgjEfEaTLqfuzzkKNsR3PBbkvi3YWK4/DxJ0zmpzSB7yy4NRzluQMVQHqJiFKXAX3Sr3fIrK24xkWW9/F207c1NpFajSGbgnFKBdtFE0e5VqwSrSoOJkZukZW/2ATSnsyzblieuUmvTWpD0PqpUOsynPjw+RqZnqPn0cjw1z2Dm7ZRt3trnyMTXFYZw5U/YuqMY2kpadD6vq780md8kXlJIylxG6ZrlO2jz9fJdnfuVq43d4QFNsBm1K1r2WtNqX+i+wiqh+u3PjF4pzXtl/a3hJOH18IfZnK7I21mQ=="
|
||||
}
|
||||
}
|
|
@ -0,0 +1,93 @@
|
|||
{
|
||||
"headers": {
|
||||
"user-agent": "http.rb/3.3.0 (Mastodon/2.5.0; +http://localhost:3000/)",
|
||||
"host": "localhost",
|
||||
"date": "Mon, 22 Oct 2018 13:34:22 GMT",
|
||||
"accept-encoding": "gzip",
|
||||
"digest": "SHA-256=FEr5j2WSSfdEMcG3NTOXuGU0lUchfTJx4+BtUlWOwDk=",
|
||||
"content-type": "application/activity+json",
|
||||
"signature": "keyId=\"http://localhost:3000/users/ronan2#main-key\",algorithm=\"rsa-sha256\",headers=\"(request-target) host date digest content-type\",signature=\"oLKbgxdFXdXsHJ3x/UsG9Svu7oa8Dyqiy6Jif4wqNuhAqRVMRaG18f+dd2OcfFX3XRGF8p8flZkU6vvoEQBauTwGRGcgXAJuKC1zYIWGk+PeiW8lNUnE4qGapWcTiFnIo7FKauNdsgqg/tvgs1pQIdHkDDjZMI64twP7sTN/4vG1PCq+kyqi/DM+ORLi/W7vFuLVHt2Iz7ikfw/R3/mMtS4FwLops+tVYBQ2iQ9DVRhTwLKVbeL/LLVB/tdGzNZ4F4nImBAQQ9I7WpPM6J/k+cBmoEbrUKs8ptx9gbX3OSsl5wlvPVMNzU9F9yb2MrB/Y/J4qssKz+LbiaktKGj7OQ==\"",
|
||||
"content-length": "2815"
|
||||
},
|
||||
"body": {
|
||||
"@context": [
|
||||
"https://www.w3.org/ns/activitystreams",
|
||||
"https://w3id.org/security/v1",
|
||||
{
|
||||
"manuallyApprovesFollowers": "as:manuallyApprovesFollowers",
|
||||
"sensitive": "as:sensitive",
|
||||
"movedTo": {
|
||||
"@id": "as:movedTo",
|
||||
"@type": "@id"
|
||||
},
|
||||
"Hashtag": "as:Hashtag",
|
||||
"ostatus": "http://ostatus.org#",
|
||||
"atomUri": "ostatus:atomUri",
|
||||
"inReplyToAtomUri": "ostatus:inReplyToAtomUri",
|
||||
"conversation": "ostatus:conversation",
|
||||
"toot": "http://joinmastodon.org/ns#",
|
||||
"Emoji": "toot:Emoji",
|
||||
"focalPoint": {
|
||||
"@container": "@list",
|
||||
"@id": "toot:focalPoint"
|
||||
},
|
||||
"featured": {
|
||||
"@id": "toot:featured",
|
||||
"@type": "@id"
|
||||
},
|
||||
"schema": "http://schema.org#",
|
||||
"PropertyValue": "schema:PropertyValue",
|
||||
"value": "schema:value"
|
||||
}
|
||||
],
|
||||
"id": "http://localhost:3000/users/ronan2/statuses/100939547203370948/activity",
|
||||
"type": "Create",
|
||||
"actor": "http://localhost:3000/users/ronan2",
|
||||
"published": "2018-10-22T13:34:18Z",
|
||||
"to": [
|
||||
"https://www.w3.org/ns/activitystreams#Public"
|
||||
],
|
||||
"cc": [
|
||||
"http://localhost:3000/users/ronan2/followers",
|
||||
"http://localhost:9000/accounts/ronan"
|
||||
],
|
||||
"object": {
|
||||
"id": "http://localhost:3000/users/ronan2/statuses/100939547203370948",
|
||||
"type": "Note",
|
||||
"summary": null,
|
||||
"inReplyTo": "http://localhost:9000/videos/watch/90e6f8ed-b369-423c-b0c8-f44e5350c752",
|
||||
"published": "2018-10-22T13:34:18Z",
|
||||
"url": "http://localhost:3000/@ronan2/100939547203370948",
|
||||
"attributedTo": "http://localhost:3000/users/ronan2",
|
||||
"to": [
|
||||
"https://www.w3.org/ns/activitystreams#Public"
|
||||
],
|
||||
"cc": [
|
||||
"http://localhost:3000/users/ronan2/followers",
|
||||
"http://localhost:9000/accounts/ronan"
|
||||
],
|
||||
"sensitive": false,
|
||||
"atomUri": "http://localhost:3000/users/ronan2/statuses/100939547203370948",
|
||||
"inReplyToAtomUri": "http://localhost:9000/videos/watch/90e6f8ed-b369-423c-b0c8-f44e5350c752",
|
||||
"conversation": "tag:localhost:3000,2018-10-19:objectId=72:objectType=Conversation",
|
||||
"content": "<p><span class=\"h-card\"><a href=\"http://localhost:9000/accounts/ronan\" class=\"u-url mention\">@<span>ronan</span></a></span> zergzerg</p>",
|
||||
"contentMap": {
|
||||
"en": "<p><span class=\"h-card\"><a href=\"http://localhost:9000/accounts/ronan\" class=\"u-url mention\">@<span>ronan</span></a></span> zergzerg</p>"
|
||||
},
|
||||
"attachment": [],
|
||||
"tag": [
|
||||
{
|
||||
"type": "Mention",
|
||||
"href": "http://localhost:9000/accounts/ronan",
|
||||
"name": "@ronan@localhost:9000"
|
||||
}
|
||||
]
|
||||
},
|
||||
"signature": {
|
||||
"type": "RsaSignature2017",
|
||||
"creator": "http://localhost:3000/users/ronan2#main-key",
|
||||
"created": "2018-10-22T13:34:19Z",
|
||||
"signatureValue": "x+xL4l8ERziYVhwEafHJyBQOInvNZ0gV4ccYd9AtFYeGJagc8fY6jjjhbDRCD7yMhgTjBX69z20MXnDuwpmM6wej3dt1wLKdIyXVViO84nAlqFz7KmNxtk5lDnAVX/vttscT5YUFvw4dbPT2mQiEd1lKbaLftRiIPEomZpQ37+fUkQdcPrnhruPAISO/Sof1n1LFW4mYIffozteQSZBH6HaCVp+MRMIhdMi5e8w7PD48/cZz8D/EU8Vqi91FM76/3tMqg6nLqQ+8bq74Jvt2kzwZlIufe+I55QMpZOmF6hGIJEt+R0JXdjQbtgcELONmNj2dr8sAlzu7zKlAGuJ24Q=="
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"publicKey": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0YyuthHtWWgDe0Fdgdp2\ndC5dTJsRqW6pFw5omIYYYjoES/WRewhVxEA54BhmxD3L1zChfx131N1TS8jVowhW\nm999jpUffKCCvLgYKIXETJDHiDeMONVx8wp7v9fS1HiFXo/E5und39gUMs14CMFZ\n6PE5jRV3r4XIKQJHQl7/X5n5FOb2934K+1TKUeBkbft/AushlKatYQakt3qHxpwx\nFvE+JjGo7QTnzdjaOx/e5QvojdGi2Kx4+jl87j2WVcSo5lOBz04OAVJtChtn82vS\nulPdDh3hZcDn+WK67yAhGP6AnzvOybZZS4zowlKiQ3kqjVVXKdl8gAsL4Y7MZ40R\nJQIDAQAB\n-----END PUBLIC KEY-----\n"
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"type": "Announce",
|
||||
"id": "http://localhost:9002/videos/watch/997111d4-e8d8-4f45-99d3-857905785d05/announces/1",
|
||||
"actor": "http://localhost:9002/accounts/peertube",
|
||||
"object": "http://localhost:9002/videos/watch/997111d4-e8d8-4f45-99d3-857905785d05",
|
||||
"to": [
|
||||
"https://www.w3.org/ns/activitystreams#Public",
|
||||
"http://localhost:9002/accounts/peertube/followers",
|
||||
"http://localhost:9002/video-channels/root_channel/followers",
|
||||
"http://localhost:9002/accounts/root/followers"
|
||||
],
|
||||
"cc": []
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"publicKey": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqjQGdH6D3naKmSbbr/Df\nEh1H42F3WlHYXuxKLkm5Bemjdde+GwHYdz5m3fcIWw3HTzfA+y9Of8epGdfSrtYO\nwAyc3Zoy7afPNa4bZXqhJ1Im41rMGieiCuUn4uTPPucIjC0gCkVwvuQr3Elbk55s\nIkczDkseJuadTvG+A1e4uNY2lnRmVhf4g5B90u6CLe2KdbPpifRoKlw9zaUBj4/F\npP5S75TS5l1DfJQIq2lp8RwrH6FvGKLnWlbGeNYX96DDvlA5Sxoxz6a+bTV9OopM\n7mS7eP8zF8lKXYUu8cjIscKm+XqGmyRoPyw2Pp53tew29idRUocVQHGBnlNbpKdd\naQIDAQAB\n-----END PUBLIC KEY-----\n",
|
||||
"privateKey": "-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQEAqjQGdH6D3naKmSbbr/DfEh1H42F3WlHYXuxKLkm5Bemjdde+\nGwHYdz5m3fcIWw3HTzfA+y9Of8epGdfSrtYOwAyc3Zoy7afPNa4bZXqhJ1Im41rM\nGieiCuUn4uTPPucIjC0gCkVwvuQr3Elbk55sIkczDkseJuadTvG+A1e4uNY2lnRm\nVhf4g5B90u6CLe2KdbPpifRoKlw9zaUBj4/FpP5S75TS5l1DfJQIq2lp8RwrH6Fv\nGKLnWlbGeNYX96DDvlA5Sxoxz6a+bTV9OopM7mS7eP8zF8lKXYUu8cjIscKm+XqG\nmyRoPyw3Pp53tew29idRUocVQHGBnlNbpKddaQIDAQABAoIBAQCnBZawCtbtH/ay\ng+dhqEW/SOyavbKZ92cU/1tsQPxISRYXNjdf2VfK7HmVqC2S7NqBanz+AVZPHmda\n7OfamkSvQbFN5VvEy8ATNV+9HbG3HG78/MT9hZcGigmyJkcZuy4wILgoXCxfpxlD\netla60PB/4yioiRcmEIWjjOgpByphDJ7RuuuptyEvgjUjpPtvHK47O/loaD2HFJk\nbIYbRirbjUjITRjQxGVIvanqiwPG9pB26YDLxDOoXEumcnzRcEFWNdvoleaLgquS\nn/zVsXWEq4+1i7t44DDstWUt/2Bw5ksIkSdayQ6oy3vzre3YFHwvbVZ7qtQQgpru\nx+NIolZhAoGBAN1RgNj8zy9Py3SJdsoXtnuCItfD7eo7LWXUa06cM/NS695Q+/to\naa5i3cJnRlv+b+b3VvnhkhIBLfFQW+hWwPnnxJEehcm09ddN9zbWrZ4Yv9yYu+8d\nTLGyWL8kPFF1dz+29DcrSv3tXEOwxByX/O4U/X/i3wl2WhkybxVFnCuvAoGBAMTf\n91BgLzvcYKOxH+vRPOJY7g2HKGFe35R91M4E+9Eq1rq4LUQHBb3fhRh4+scNu0yb\nNfN1Zdx2nbgCXdTKomF1Ahxp58/A2iU65vVzL6hYfWXEGSmoBqsGCIpIxQ9jgB9k\nCl7t/Ban8Z/ORHTjI9fpHlSZyCWJ3ajepiM2a1ZnAoGAPpDO6wi1DXvyWVSPF1yS\nwuGsNfD2rjPihpoBZ+yypwP3GBcu1QjUb28Vn+KQOmt4eQPNO8DwCVT6BvEfulPk\nJAHISPom+jnFEgPBcmhIFpyKiLNI1bUjvExd2FNHFgQuHP38ligQAC782Un8dtTk\ntO2MKH4bbVJe8CaYzpuqJZMCgYABZyMpBHZxs8FQiUuT75rCdiXEHOlxwC5RrY/d\no/VzaR28mOFhsbcdwkD9iqcm0fc6tYRt5rFCH+pBzGqEwKjljuLj9vE67sHfMAtD\nRn3Zcj/6gKo5PMRHZbSb36bf1DKuhpT4VjPMqYe0PtEIEDJKMJQRwELH2bKlqGiA\nqbucEwKBgQCkS85JnpHEV/tSylsEEn2W3CQCx58zl7iZNV7h/tWMR4AyrcI0HqP6\nllJ7V/Cfw66MgelPnosKgagwLVI6gsqDtjnzYo3XuMRVlYIySJ/jV3eiUNkV2Ky2\nfp/gA9sVgp38QSr+xB9E0LNStcbqDzoCCcDRws/SK7PbkQH9KV47tQ==\n-----END RSA PRIVATE KEY-----"
|
||||
}
|
||||
|
||||
|
4
server/tests/api/activitypub/json/peertube/keys.json
Normal file
4
server/tests/api/activitypub/json/peertube/keys.json
Normal file
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"publicKey": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqjQGdH6D3naKmSbbr/Df\nEh1H42F3WlHYXuxKLkm5Bemjdde+GwHYdz5m3fcIWw3HTzfA+y9Of8epGdfSrtYO\nwAyc3Zoy7afPNa4bZXqhJ1Im41rMGieiCuUn4uTPPucIjC0gCkVwvuQr3Elbk55s\nIkczDkseJuadTvG+A1e4uNY2lnRmVhf4g5B90u6CLe2KdbPpifRoKlw9zaUBj4/F\npP5S75TS5l1DfJQIq2lp8RwrH6FvGKLnWlbGeNYX96DDvlA5Sxoxz6a+bTV9OopM\n7mS7eP8zF8lKXYUu8cjIscKm+XqGmyRoPyw3Pp53tew29idRUocVQHGBnlNbpKdd\naQIDAQAB\n-----END PUBLIC KEY-----\n",
|
||||
"privateKey": "-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQEAqjQGdH6D3naKmSbbr/DfEh1H42F3WlHYXuxKLkm5Bemjdde+\nGwHYdz5m3fcIWw3HTzfA+y9Of8epGdfSrtYOwAyc3Zoy7afPNa4bZXqhJ1Im41rM\nGieiCuUn4uTPPucIjC0gCkVwvuQr3Elbk55sIkczDkseJuadTvG+A1e4uNY2lnRm\nVhf4g5B90u6CLe2KdbPpifRoKlw9zaUBj4/FpP5S75TS5l1DfJQIq2lp8RwrH6Fv\nGKLnWlbGeNYX96DDvlA5Sxoxz6a+bTV9OopM7mS7eP8zF8lKXYUu8cjIscKm+XqG\nmyRoPyw3Pp53tew29idRUocVQHGBnlNbpKddaQIDAQABAoIBAQCnBZawCtbtH/ay\ng+dhqEW/SOyavbKZ92cU/1tsQPxISRYXNjdf2VfK7HmVqC2S7NqBanz+AVZPHmda\n7OfamkSvQbFN5VvEy8ATNV+9HbG3HG78/MT9hZcGigmyJkcZuy4wILgoXCxfpxlD\netla60PB/4yioiRcmEIWjjOgpByphDJ7RuuuptyEvgjUjpPtvHK47O/loaD2HFJk\nbIYbRirbjUjITRjQxGVIvanqiwPG9pB26YDLxDOoXEumcnzRcEFWNdvoleaLgquS\nn/zVsXWEq4+1i7t44DDstWUt/2Bw5ksIkSdayQ6oy3vzre3YFHwvbVZ7qtQQgpru\nx+NIolZhAoGBAN1RgNj8zy9Py3SJdsoXtnuCItfD7eo7LWXUa06cM/NS695Q+/to\naa5i3cJnRlv+b+b3VvnhkhIBLfFQW+hWwPnnxJEehcm09ddN9zbWrZ4Yv9yYu+8d\nTLGyWL8kPFF1dz+29DcrSv3tXEOwxByX/O4U/X/i3wl2WhkybxVFnCuvAoGBAMTf\n91BgLzvcYKOxH+vRPOJY7g2HKGFe35R91M4E+9Eq1rq4LUQHBb3fhRh4+scNu0yb\nNfN1Zdx2nbgCXdTKomF1Ahxp58/A2iU65vVzL6hYfWXEGSmoBqsGCIpIxQ9jgB9k\nCl7t/Ban8Z/ORHTjI9fpHlSZyCWJ3ajepiM2a1ZnAoGAPpDO6wi1DXvyWVSPF1yS\nwuGsNfD2rjPihpoBZ+yypwP3GBcu1QjUb28Vn+KQOmt4eQPNO8DwCVT6BvEfulPk\nJAHISPom+jnFEgPBcmhIFpyKiLNI1bUjvExd2FNHFgQuHP38ligQAC782Un8dtTk\ntO2MKH4bbVJe8CaYzpuqJZMCgYABZyMpBHZxs8FQiUuT75rCdiXEHOlxwC5RrY/d\no/VzaR28mOFhsbcdwkD9iqcm0fc6tYRt5rFCH+pBzGqEwKjljuLj9vE67sHfMAtD\nRn3Zcj/6gKo5PMRHZbSb36bf1DKuhpT4VjPMqYe0PtEIEDJKMJQRwELH2bKlqGiA\nqbucEwKBgQCkS85JnpHEV/tSylsEEn2W3CQCx58zl7iZNV7h/tWMR4AyrcI0HqP6\nllJ7V/Cfw66MgelPnosKgagwLVI6gsqDtjnzYo3XuMRVlYIySJ/jV3eiUNkV2Ky2\nfp/gA9sVgp38QSr+xB9E0LNStcbqDzoCCcDRws/SK7PbkQH9KV47tQ==\n-----END RSA PRIVATE KEY-----"
|
||||
}
|
180
server/tests/api/activitypub/security.ts
Normal file
180
server/tests/api/activitypub/security.ts
Normal file
|
@ -0,0 +1,180 @@
|
|||
/* tslint:disable:no-unused-expression */
|
||||
|
||||
import 'mocha'
|
||||
|
||||
import { flushAndRunMultipleServers, flushTests, killallServers, makeAPRequest, makeFollowRequest, ServerInfo } from '../../utils'
|
||||
import { HTTP_SIGNATURE } from '../../../initializers'
|
||||
import { buildDigest, buildGlobalHeaders } from '../../../lib/job-queue/handlers/utils/activitypub-http-utils'
|
||||
import * as chai from 'chai'
|
||||
import { setActorField } from '../../utils/miscs/sql'
|
||||
import { activityPubContextify, buildSignedActivity } from '../../../helpers/activitypub'
|
||||
|
||||
const expect = chai.expect
|
||||
|
||||
function setKeysOfServer2 (serverNumber: number, publicKey: string, privateKey: string) {
|
||||
return Promise.all([
|
||||
setActorField(serverNumber, 'http://localhost:9002/accounts/peertube', 'publicKey', publicKey),
|
||||
setActorField(serverNumber, 'http://localhost:9002/accounts/peertube', 'privateKey', privateKey)
|
||||
])
|
||||
}
|
||||
|
||||
function setKeysOfServer3 (serverNumber: number, publicKey: string, privateKey: string) {
|
||||
return Promise.all([
|
||||
setActorField(serverNumber, 'http://localhost:9003/accounts/peertube', 'publicKey', publicKey),
|
||||
setActorField(serverNumber, 'http://localhost:9003/accounts/peertube', 'privateKey', privateKey)
|
||||
])
|
||||
}
|
||||
|
||||
describe('Test ActivityPub security', function () {
|
||||
let servers: ServerInfo[]
|
||||
let url: string
|
||||
|
||||
const keys = require('./json/peertube/keys.json')
|
||||
const invalidKeys = require('./json/peertube/invalid-keys.json')
|
||||
const baseHttpSignature = {
|
||||
algorithm: HTTP_SIGNATURE.ALGORITHM,
|
||||
authorizationHeaderName: HTTP_SIGNATURE.HEADER_NAME,
|
||||
keyId: 'acct:peertube@localhost:9002',
|
||||
key: keys.privateKey,
|
||||
headers: HTTP_SIGNATURE.HEADERS_TO_SIGN
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
before(async function () {
|
||||
this.timeout(60000)
|
||||
|
||||
servers = await flushAndRunMultipleServers(3)
|
||||
|
||||
url = servers[0].url + '/inbox'
|
||||
|
||||
await setKeysOfServer2(1, keys.publicKey, keys.privateKey)
|
||||
|
||||
const to = { url: 'http://localhost:9001/accounts/peertube' }
|
||||
const by = { url: 'http://localhost:9002/accounts/peertube', privateKey: keys.privateKey }
|
||||
await makeFollowRequest(to, by)
|
||||
})
|
||||
|
||||
describe('When checking HTTP signature', function () {
|
||||
|
||||
it('Should fail with an invalid digest', async function () {
|
||||
const body = activityPubContextify(require('./json/peertube/announce-without-context.json'))
|
||||
const headers = {
|
||||
Digest: buildDigest({ hello: 'coucou' })
|
||||
}
|
||||
|
||||
const { response } = await makeAPRequest(url, body, baseHttpSignature, headers)
|
||||
|
||||
expect(response.statusCode).to.equal(403)
|
||||
})
|
||||
|
||||
it('Should fail with an invalid date', async function () {
|
||||
const body = activityPubContextify(require('./json/peertube/announce-without-context.json'))
|
||||
const headers = buildGlobalHeaders(body)
|
||||
headers['date'] = 'Wed, 21 Oct 2015 07:28:00 GMT'
|
||||
|
||||
const { response } = await makeAPRequest(url, body, baseHttpSignature, headers)
|
||||
|
||||
expect(response.statusCode).to.equal(403)
|
||||
})
|
||||
|
||||
it('Should fail with bad keys', async function () {
|
||||
await setKeysOfServer2(1, invalidKeys.publicKey, invalidKeys.privateKey)
|
||||
await setKeysOfServer2(2, invalidKeys.publicKey, invalidKeys.privateKey)
|
||||
|
||||
const body = activityPubContextify(require('./json/peertube/announce-without-context.json'))
|
||||
const headers = buildGlobalHeaders(body)
|
||||
|
||||
const { response } = await makeAPRequest(url, body, baseHttpSignature, headers)
|
||||
|
||||
expect(response.statusCode).to.equal(403)
|
||||
})
|
||||
|
||||
it('Should succeed with a valid HTTP signature', async function () {
|
||||
await setKeysOfServer2(1, keys.publicKey, keys.privateKey)
|
||||
await setKeysOfServer2(2, keys.publicKey, keys.privateKey)
|
||||
|
||||
const body = activityPubContextify(require('./json/peertube/announce-without-context.json'))
|
||||
const headers = buildGlobalHeaders(body)
|
||||
|
||||
const { response } = await makeAPRequest(url, body, baseHttpSignature, headers)
|
||||
|
||||
expect(response.statusCode).to.equal(204)
|
||||
})
|
||||
})
|
||||
|
||||
describe('When checking Linked Data Signature', function () {
|
||||
before(async () => {
|
||||
await setKeysOfServer3(3, keys.publicKey, keys.privateKey)
|
||||
|
||||
const to = { url: 'http://localhost:9001/accounts/peertube' }
|
||||
const by = { url: 'http://localhost:9003/accounts/peertube', privateKey: keys.privateKey }
|
||||
await makeFollowRequest(to, by)
|
||||
})
|
||||
|
||||
it('Should fail with bad keys', async function () {
|
||||
this.timeout(10000)
|
||||
|
||||
await setKeysOfServer3(1, invalidKeys.publicKey, invalidKeys.privateKey)
|
||||
await setKeysOfServer3(3, invalidKeys.publicKey, invalidKeys.privateKey)
|
||||
|
||||
const body = require('./json/peertube/announce-without-context.json')
|
||||
body.actor = 'http://localhost:9003/accounts/peertube'
|
||||
|
||||
const signer: any = { privateKey: invalidKeys.privateKey, url: 'http://localhost:9003/accounts/peertube' }
|
||||
const signedBody = await buildSignedActivity(signer, body)
|
||||
|
||||
const headers = buildGlobalHeaders(signedBody)
|
||||
|
||||
const { response } = await makeAPRequest(url, signedBody, baseHttpSignature, headers)
|
||||
|
||||
expect(response.statusCode).to.equal(403)
|
||||
})
|
||||
|
||||
it('Should fail with an altered body', async function () {
|
||||
this.timeout(10000)
|
||||
|
||||
await setKeysOfServer3(1, keys.publicKey, keys.privateKey)
|
||||
await setKeysOfServer3(3, keys.publicKey, keys.privateKey)
|
||||
|
||||
const body = require('./json/peertube/announce-without-context.json')
|
||||
body.actor = 'http://localhost:9003/accounts/peertube'
|
||||
|
||||
const signer: any = { privateKey: keys.privateKey, url: 'http://localhost:9003/accounts/peertube' }
|
||||
const signedBody = await buildSignedActivity(signer, body)
|
||||
|
||||
signedBody.actor = 'http://localhost:9003/account/peertube'
|
||||
|
||||
const headers = buildGlobalHeaders(signedBody)
|
||||
|
||||
const { response } = await makeAPRequest(url, signedBody, baseHttpSignature, headers)
|
||||
|
||||
expect(response.statusCode).to.equal(403)
|
||||
})
|
||||
|
||||
it('Should succeed with a valid signature', async function () {
|
||||
this.timeout(10000)
|
||||
|
||||
const body = require('./json/peertube/announce-without-context.json')
|
||||
body.actor = 'http://localhost:9003/accounts/peertube'
|
||||
|
||||
const signer: any = { privateKey: keys.privateKey, url: 'http://localhost:9003/accounts/peertube' }
|
||||
const signedBody = await buildSignedActivity(signer, body)
|
||||
|
||||
const headers = buildGlobalHeaders(signedBody)
|
||||
|
||||
const { response } = await makeAPRequest(url, signedBody, baseHttpSignature, headers)
|
||||
|
||||
expect(response.statusCode).to.equal(204)
|
||||
})
|
||||
})
|
||||
|
||||
after(async function () {
|
||||
killallServers(servers)
|
||||
|
||||
// Keep the logs if the test failed
|
||||
if (this['ok']) {
|
||||
await flushTests()
|
||||
}
|
||||
})
|
||||
})
|
|
@ -1 +1,2 @@
|
|||
import './redundancy'
|
||||
import './activitypub'
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
// Order of the tests we want to execute
|
||||
import './client'
|
||||
import './activitypub'
|
||||
import './feeds/'
|
||||
import './cli/'
|
||||
import './api/'
|
||||
|
|
|
@ -4,8 +4,10 @@ export * from './server/clients'
|
|||
export * from './server/config'
|
||||
export * from './users/login'
|
||||
export * from './miscs/miscs'
|
||||
export * from './miscs/stubs'
|
||||
export * from './server/follows'
|
||||
export * from './requests/requests'
|
||||
export * from './requests/activitypub'
|
||||
export * from './server/servers'
|
||||
export * from './videos/services'
|
||||
export * from './users/users'
|
||||
|
|
29
server/tests/utils/miscs/sql.ts
Normal file
29
server/tests/utils/miscs/sql.ts
Normal file
|
@ -0,0 +1,29 @@
|
|||
import * as Sequelize from 'sequelize'
|
||||
|
||||
function getSequelize (serverNumber: number) {
|
||||
const dbname = 'peertube_test' + serverNumber
|
||||
const username = 'peertube'
|
||||
const password = 'peertube'
|
||||
const host = 'localhost'
|
||||
const port = 5432
|
||||
|
||||
return new Sequelize(dbname, username, password, {
|
||||
dialect: 'postgres',
|
||||
host,
|
||||
port,
|
||||
operatorsAliases: false,
|
||||
logging: false
|
||||
})
|
||||
}
|
||||
|
||||
function setActorField (serverNumber: number, to: string, field: string, value: string) {
|
||||
const seq = getSequelize(serverNumber)
|
||||
|
||||
const options = { type: Sequelize.QueryTypes.UPDATE }
|
||||
|
||||
return seq.query(`UPDATE actor SET "${field}" = '${value}' WHERE url = '${to}'`, options)
|
||||
}
|
||||
|
||||
export {
|
||||
setActorField
|
||||
}
|
14
server/tests/utils/miscs/stubs.ts
Normal file
14
server/tests/utils/miscs/stubs.ts
Normal file
|
@ -0,0 +1,14 @@
|
|||
function buildRequestStub (): any {
|
||||
return { }
|
||||
}
|
||||
|
||||
function buildResponseStub (): any {
|
||||
return {
|
||||
locals: {}
|
||||
}
|
||||
}
|
||||
|
||||
export {
|
||||
buildResponseStub,
|
||||
buildRequestStub
|
||||
}
|
43
server/tests/utils/requests/activitypub.ts
Normal file
43
server/tests/utils/requests/activitypub.ts
Normal file
|
@ -0,0 +1,43 @@
|
|||
import { doRequest } from '../../../helpers/requests'
|
||||
import { HTTP_SIGNATURE } from '../../../initializers'
|
||||
import { buildGlobalHeaders } from '../../../lib/job-queue/handlers/utils/activitypub-http-utils'
|
||||
import { activityPubContextify } from '../../../helpers/activitypub'
|
||||
|
||||
function makeAPRequest (url: string, body: any, httpSignature: any, headers: any) {
|
||||
const options = {
|
||||
method: 'POST',
|
||||
uri: url,
|
||||
json: body,
|
||||
httpSignature,
|
||||
headers
|
||||
}
|
||||
|
||||
return doRequest(options)
|
||||
}
|
||||
|
||||
async function makeFollowRequest (to: { url: string }, by: { url: string, privateKey }) {
|
||||
const follow = {
|
||||
type: 'Follow',
|
||||
id: by.url + '/toto',
|
||||
actor: by.url,
|
||||
object: to.url
|
||||
}
|
||||
|
||||
const body = activityPubContextify(follow)
|
||||
|
||||
const httpSignature = {
|
||||
algorithm: HTTP_SIGNATURE.ALGORITHM,
|
||||
authorizationHeaderName: HTTP_SIGNATURE.HEADER_NAME,
|
||||
keyId: by.url,
|
||||
key: by.privateKey,
|
||||
headers: HTTP_SIGNATURE.HEADERS_TO_SIGN
|
||||
}
|
||||
const headers = buildGlobalHeaders(body)
|
||||
|
||||
return makeAPRequest(to.url, body, httpSignature, headers)
|
||||
}
|
||||
|
||||
export {
|
||||
makeAPRequest,
|
||||
makeFollowRequest
|
||||
}
|
|
@ -5054,7 +5054,7 @@ jsonify@~0.0.0:
|
|||
|
||||
jsonld@^0.5.12:
|
||||
version "0.5.21"
|
||||
resolved "http://registry.npmjs.org/jsonld/-/jsonld-0.5.21.tgz#4d5b78d717eb92bcd1ac9d88e34efad95370c0bf"
|
||||
resolved "https://registry.yarnpkg.com/jsonld/-/jsonld-0.5.21.tgz#4d5b78d717eb92bcd1ac9d88e34efad95370c0bf"
|
||||
integrity sha512-1dQhaw1Eb3p7Cz5ECE2DNPwLvTmK+f6D45hACBdonJaFKP1bN9zlKLZWbPZQeZtduAc/LNv10J4ML0IiTBVahw==
|
||||
dependencies:
|
||||
rdf-canonize "^0.2.1"
|
||||
|
|
Loading…
Reference in a new issue