Add compatibility with other Linked Signature algorithms
This commit is contained in:
parent
b83b8dd5ae
commit
df66d81583
|
@ -12,7 +12,6 @@ killall -q peertube || true
|
||||||
if [ "$1" = "misc" ]; then
|
if [ "$1" = "misc" ]; then
|
||||||
npm run build -- --light-fr
|
npm run build -- --light-fr
|
||||||
mocha --timeout 5000 --exit --require ts-node/register/type-check --bail server/tests/client.ts \
|
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/feeds/index.ts \
|
||||||
server/tests/misc-endpoints.ts \
|
server/tests/misc-endpoints.ts \
|
||||||
server/tests/helpers/index.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)
|
// 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 { 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()
|
const missed = checkMissedConfig()
|
||||||
if (missed.length !== 0) {
|
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 { UpdateVideosScheduler } from './server/lib/schedulers/update-videos-scheduler'
|
||||||
import { YoutubeDlUpdateScheduler } from './server/lib/schedulers/youtube-dl-update-scheduler'
|
import { YoutubeDlUpdateScheduler } from './server/lib/schedulers/youtube-dl-update-scheduler'
|
||||||
import { VideosRedundancyScheduler } from './server/lib/schedulers/videos-redundancy-scheduler'
|
import { VideosRedundancyScheduler } from './server/lib/schedulers/videos-redundancy-scheduler'
|
||||||
|
import { isHTTPSignatureDigestValid } from './server/helpers/peertube-crypto'
|
||||||
|
|
||||||
// ----------- Command line -----------
|
// ----------- Command line -----------
|
||||||
|
|
||||||
|
@ -131,7 +132,11 @@ app.use(morgan('combined', {
|
||||||
app.use(bodyParser.urlencoded({ extended: false }))
|
app.use(bodyParser.urlencoded({ extended: false }))
|
||||||
app.use(bodyParser.json({
|
app.use(bodyParser.json({
|
||||||
type: [ 'application/json', 'application/*+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
|
// Cookies
|
||||||
app.use(cookieParser())
|
app.use(cookieParser())
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import * as AsyncLRU from 'async-lru'
|
import * as AsyncLRU from 'async-lru'
|
||||||
import * as jsonld from 'jsonld/'
|
import * as jsonld from 'jsonld'
|
||||||
import * as jsig from 'jsonld-signatures'
|
import * as jsig from 'jsonld-signatures'
|
||||||
|
|
||||||
const nodeDocumentLoader = jsonld.documentLoaders.node()
|
const nodeDocumentLoader = jsonld.documentLoaders.node()
|
||||||
|
@ -17,4 +17,4 @@ jsonld.documentLoader = (url, cb) => {
|
||||||
|
|
||||||
jsig.use('jsonld', jsonld)
|
jsig.use('jsonld', jsonld)
|
||||||
|
|
||||||
export { jsig }
|
export { jsig, jsonld }
|
||||||
|
|
|
@ -1,9 +1,12 @@
|
||||||
import { Request } from 'express'
|
import { Request } from 'express'
|
||||||
import { BCRYPT_SALT_SIZE, HTTP_SIGNATURE, PRIVATE_RSA_KEY_SIZE } from '../initializers'
|
import { BCRYPT_SALT_SIZE, HTTP_SIGNATURE, PRIVATE_RSA_KEY_SIZE } from '../initializers'
|
||||||
import { ActorModel } from '../models/activitypub/actor'
|
import { ActorModel } from '../models/activitypub/actor'
|
||||||
import { bcryptComparePromise, bcryptGenSaltPromise, bcryptHashPromise, createPrivateKey, getPublicKey } from './core-utils'
|
import { bcryptComparePromise, bcryptGenSaltPromise, bcryptHashPromise, createPrivateKey, getPublicKey, sha256 } from './core-utils'
|
||||||
import { jsig } from './custom-jsonld-signature'
|
import { jsig, jsonld } from './custom-jsonld-signature'
|
||||||
import { logger } from './logger'
|
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')
|
const httpSignature = require('http-signature')
|
||||||
|
|
||||||
|
@ -30,21 +33,36 @@ async function cryptPassword (password: string) {
|
||||||
|
|
||||||
// HTTP Signature
|
// 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
|
return httpSignature.verifySignature(httpSignatureParsed, actor.publicKey) === true
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseHTTPSignature (req: Request) {
|
function parseHTTPSignature (req: Request, clockSkew?: number) {
|
||||||
return httpSignature.parse(req, { authorizationHeaderName: HTTP_SIGNATURE.HEADER_NAME })
|
return httpSignature.parse(req, { authorizationHeaderName: HTTP_SIGNATURE.HEADER_NAME, clockSkew })
|
||||||
}
|
}
|
||||||
|
|
||||||
// JSONLD
|
// 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 = {
|
const publicKeyObject = {
|
||||||
'@context': jsig.SECURITY_CONTEXT_URL,
|
'@context': jsig.SECURITY_CONTEXT_URL,
|
||||||
id: fromActor.url,
|
id: fromActor.url,
|
||||||
type: 'CryptographicKey',
|
type: 'CryptographicKey',
|
||||||
owner: fromActor.url,
|
owner: fromActor.url,
|
||||||
publicKeyPem: fromActor.publicKey
|
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) {
|
function signJsonLDObject (byActor: ActorModel, data: any) {
|
||||||
const options = {
|
const options = {
|
||||||
privateKeyPem: byActor.privateKey,
|
privateKeyPem: byActor.privateKey,
|
||||||
|
@ -82,6 +138,7 @@ function signJsonLDObject (byActor: ActorModel, data: any) {
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
export {
|
export {
|
||||||
|
isHTTPSignatureDigestValid,
|
||||||
parseHTTPSignature,
|
parseHTTPSignature,
|
||||||
isHTTPSignatureVerified,
|
isHTTPSignatureVerified,
|
||||||
isJsonLDSignatureVerified,
|
isJsonLDSignatureVerified,
|
||||||
|
|
|
@ -535,7 +535,7 @@ const ACTIVITY_PUB_ACTOR_TYPES: { [ id: string ]: ActivityPubActorType } = {
|
||||||
const HTTP_SIGNATURE = {
|
const HTTP_SIGNATURE = {
|
||||||
HEADER_NAME: 'signature',
|
HEADER_NAME: 'signature',
|
||||||
ALGORITHM: 'rsa-sha256',
|
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) {
|
function buildGlobalHeaders (body: any) {
|
||||||
const digest = 'SHA-256=' + sha256(JSON.stringify(body), 'base64')
|
|
||||||
|
|
||||||
return {
|
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 {
|
export {
|
||||||
|
buildDigest,
|
||||||
buildGlobalHeaders,
|
buildGlobalHeaders,
|
||||||
computeBody,
|
computeBody,
|
||||||
buildSignedRequestOptions
|
buildSignedRequestOptions
|
||||||
|
|
|
@ -53,7 +53,8 @@ function executeIfActivityPub (fun: RequestHandler | RequestHandler[]) {
|
||||||
|
|
||||||
export {
|
export {
|
||||||
checkSignature,
|
checkSignature,
|
||||||
executeIfActivityPub
|
executeIfActivityPub,
|
||||||
|
checkHttpSignature
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
@ -94,7 +95,7 @@ async function checkHttpSignature (req: Request, res: Response) {
|
||||||
async function checkJsonLDSignature (req: Request, res: Response) {
|
async function checkJsonLDSignature (req: Request, res: Response) {
|
||||||
const signatureObject: ActivityPubSignature = req.body.signature
|
const signatureObject: ActivityPubSignature = req.body.signature
|
||||||
|
|
||||||
if (!signatureObject.creator) {
|
if (!signatureObject || !signatureObject.creator) {
|
||||||
res.sendStatus(403)
|
res.sendStatus(403)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
import * as chai from 'chai'
|
import * as chai from 'chai'
|
||||||
import 'mocha'
|
import 'mocha'
|
||||||
import { flushTests, killallServers, makeActivityPubGetRequest, runServer, ServerInfo, setAccessTokensToServers } from './utils'
|
import { flushTests, killallServers, makeActivityPubGetRequest, runServer, ServerInfo, setAccessTokensToServers } from '../../utils'
|
||||||
|
|
||||||
const expect = chai.expect
|
const expect = chai.expect
|
||||||
|
|
|
@ -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
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
|
@ -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=="
|
||||||
|
}
|
||||||
|
}
|
|
@ -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-----"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -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-----"
|
||||||
|
}
|
|
@ -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 './redundancy'
|
||||||
|
import './activitypub'
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
// Order of the tests we want to execute
|
// Order of the tests we want to execute
|
||||||
import './client'
|
import './client'
|
||||||
import './activitypub'
|
|
||||||
import './feeds/'
|
import './feeds/'
|
||||||
import './cli/'
|
import './cli/'
|
||||||
import './api/'
|
import './api/'
|
||||||
|
|
|
@ -4,8 +4,10 @@ export * from './server/clients'
|
||||||
export * from './server/config'
|
export * from './server/config'
|
||||||
export * from './users/login'
|
export * from './users/login'
|
||||||
export * from './miscs/miscs'
|
export * from './miscs/miscs'
|
||||||
|
export * from './miscs/stubs'
|
||||||
export * from './server/follows'
|
export * from './server/follows'
|
||||||
export * from './requests/requests'
|
export * from './requests/requests'
|
||||||
|
export * from './requests/activitypub'
|
||||||
export * from './server/servers'
|
export * from './server/servers'
|
||||||
export * from './videos/services'
|
export * from './videos/services'
|
||||||
export * from './users/users'
|
export * from './users/users'
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
function buildRequestStub (): any {
|
||||||
|
return { }
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildResponseStub (): any {
|
||||||
|
return {
|
||||||
|
locals: {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
buildResponseStub,
|
||||||
|
buildRequestStub
|
||||||
|
}
|
|
@ -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:
|
jsonld@^0.5.12:
|
||||||
version "0.5.21"
|
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==
|
integrity sha512-1dQhaw1Eb3p7Cz5ECE2DNPwLvTmK+f6D45hACBdonJaFKP1bN9zlKLZWbPZQeZtduAc/LNv10J4ML0IiTBVahw==
|
||||||
dependencies:
|
dependencies:
|
||||||
rdf-canonize "^0.2.1"
|
rdf-canonize "^0.2.1"
|
||||||
|
|
Loading…
Reference in New Issue