]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blob - server/tests/api/activitypub/security.ts
Merge branch 'release/v1.3.0' into develop
[github/Chocobozzz/PeerTube.git] / server / tests / api / activitypub / security.ts
1 /* tslint:disable:no-unused-expression */
2
3 import 'mocha'
4
5 import {
6 cleanupTests,
7 closeAllSequelize,
8 flushAndRunMultipleServers,
9 killallServers,
10 ServerInfo,
11 setActorField
12 } from '../../../../shared/extra-utils'
13 import { HTTP_SIGNATURE } from '../../../initializers/constants'
14 import { buildDigest, buildGlobalHeaders } from '../../../lib/job-queue/handlers/utils/activitypub-http-utils'
15 import * as chai from 'chai'
16 import { activityPubContextify, buildSignedActivity } from '../../../helpers/activitypub'
17 import { makeFollowRequest, makePOSTAPRequest } from '../../../../shared/extra-utils/requests/activitypub'
18
19 const expect = chai.expect
20
21 function setKeysOfServer (onServer: ServerInfo, ofServer: ServerInfo, publicKey: string, privateKey: string) {
22 return Promise.all([
23 setActorField(onServer.internalServerNumber, 'http://localhost:' + ofServer.port + '/accounts/peertube', 'publicKey', publicKey),
24 setActorField(onServer.internalServerNumber, 'http://localhost:' + ofServer.port + '/accounts/peertube', 'privateKey', privateKey)
25 ])
26 }
27
28 function getAnnounceWithoutContext (server2: ServerInfo) {
29 const json = require('./json/peertube/announce-without-context.json')
30 const result: typeof json = {}
31
32 for (const key of Object.keys(json)) {
33 if (Array.isArray(json[key])) {
34 result[key] = json[key].map(v => v.replace(':9002', `:${server2.port}`))
35 } else {
36 result[ key ] = json[ key ].replace(':9002', `:${server2.port}`)
37 }
38 }
39
40 return result
41 }
42
43 describe('Test ActivityPub security', function () {
44 let servers: ServerInfo[]
45 let url: string
46
47 const keys = require('./json/peertube/keys.json')
48 const invalidKeys = require('./json/peertube/invalid-keys.json')
49 const baseHttpSignature = () => ({
50 algorithm: HTTP_SIGNATURE.ALGORITHM,
51 authorizationHeaderName: HTTP_SIGNATURE.HEADER_NAME,
52 keyId: 'acct:peertube@localhost:' + servers[1].port,
53 key: keys.privateKey,
54 headers: HTTP_SIGNATURE.HEADERS_TO_SIGN
55 })
56
57 // ---------------------------------------------------------------
58
59 before(async function () {
60 this.timeout(60000)
61
62 servers = await flushAndRunMultipleServers(3)
63
64 url = servers[0].url + '/inbox'
65
66 await setKeysOfServer(servers[0], servers[1], keys.publicKey, keys.privateKey)
67
68 const to = { url: 'http://localhost:' + servers[0].port + '/accounts/peertube' }
69 const by = { url: 'http://localhost:' + servers[1].port + '/accounts/peertube', privateKey: keys.privateKey }
70 await makeFollowRequest(to, by)
71 })
72
73 describe('When checking HTTP signature', function () {
74
75 it('Should fail with an invalid digest', async function () {
76 const body = activityPubContextify(getAnnounceWithoutContext(servers[1]))
77 const headers = {
78 Digest: buildDigest({ hello: 'coucou' })
79 }
80
81 const { response } = await makePOSTAPRequest(url, body, baseHttpSignature(), headers)
82
83 expect(response.statusCode).to.equal(403)
84 })
85
86 it('Should fail with an invalid date', async function () {
87 const body = activityPubContextify(getAnnounceWithoutContext(servers[1]))
88 const headers = buildGlobalHeaders(body)
89 headers['date'] = 'Wed, 21 Oct 2015 07:28:00 GMT'
90
91 const { response } = await makePOSTAPRequest(url, body, baseHttpSignature(), headers)
92
93 expect(response.statusCode).to.equal(403)
94 })
95
96 it('Should fail with bad keys', async function () {
97 await setKeysOfServer(servers[0], servers[1], invalidKeys.publicKey, invalidKeys.privateKey)
98 await setKeysOfServer(servers[1], servers[1], invalidKeys.publicKey, invalidKeys.privateKey)
99
100 const body = activityPubContextify(getAnnounceWithoutContext(servers[1]))
101 const headers = buildGlobalHeaders(body)
102
103 const { response } = await makePOSTAPRequest(url, body, baseHttpSignature(), headers)
104
105 expect(response.statusCode).to.equal(403)
106 })
107
108 it('Should succeed with a valid HTTP signature', async function () {
109 await setKeysOfServer(servers[0], servers[1], keys.publicKey, keys.privateKey)
110 await setKeysOfServer(servers[1], servers[1], keys.publicKey, keys.privateKey)
111
112 const body = activityPubContextify(getAnnounceWithoutContext(servers[1]))
113 const headers = buildGlobalHeaders(body)
114
115 const { response } = await makePOSTAPRequest(url, body, baseHttpSignature(), headers)
116
117 expect(response.statusCode).to.equal(204)
118 })
119 })
120
121 describe('When checking Linked Data Signature', function () {
122 before(async () => {
123 await setKeysOfServer(servers[2], servers[2], keys.publicKey, keys.privateKey)
124
125 const to = { url: 'http://localhost:' + servers[0].port + '/accounts/peertube' }
126 const by = { url: 'http://localhost:' + servers[2].port + '/accounts/peertube', privateKey: keys.privateKey }
127 await makeFollowRequest(to, by)
128 })
129
130 it('Should fail with bad keys', async function () {
131 this.timeout(10000)
132
133 await setKeysOfServer(servers[0], servers[2], invalidKeys.publicKey, invalidKeys.privateKey)
134 await setKeysOfServer(servers[2], servers[2], invalidKeys.publicKey, invalidKeys.privateKey)
135
136 const body = getAnnounceWithoutContext(servers[1])
137 body.actor = 'http://localhost:' + servers[2].port + '/accounts/peertube'
138
139 const signer: any = { privateKey: invalidKeys.privateKey, url: 'http://localhost:' + servers[2].port + '/accounts/peertube' }
140 const signedBody = await buildSignedActivity(signer, body)
141
142 const headers = buildGlobalHeaders(signedBody)
143
144 const { response } = await makePOSTAPRequest(url, signedBody, baseHttpSignature(), headers)
145
146 expect(response.statusCode).to.equal(403)
147 })
148
149 it('Should fail with an altered body', async function () {
150 this.timeout(10000)
151
152 await setKeysOfServer(servers[0], servers[2], keys.publicKey, keys.privateKey)
153 await setKeysOfServer(servers[0], servers[2], keys.publicKey, keys.privateKey)
154
155 const body = getAnnounceWithoutContext(servers[1])
156 body.actor = 'http://localhost:' + servers[2].port + '/accounts/peertube'
157
158 const signer: any = { privateKey: keys.privateKey, url: 'http://localhost:' + servers[2].port + '/accounts/peertube' }
159 const signedBody = await buildSignedActivity(signer, body)
160
161 signedBody.actor = 'http://localhost:' + servers[2].port + '/account/peertube'
162
163 const headers = buildGlobalHeaders(signedBody)
164
165 const { response } = await makePOSTAPRequest(url, signedBody, baseHttpSignature(), headers)
166
167 expect(response.statusCode).to.equal(403)
168 })
169
170 it('Should succeed with a valid signature', async function () {
171 this.timeout(10000)
172
173 const body = getAnnounceWithoutContext(servers[1])
174 body.actor = 'http://localhost:' + servers[2].port + '/accounts/peertube'
175
176 const signer: any = { privateKey: keys.privateKey, url: 'http://localhost:' + servers[2].port + '/accounts/peertube' }
177 const signedBody = await buildSignedActivity(signer, body)
178
179 const headers = buildGlobalHeaders(signedBody)
180
181 const { response } = await makePOSTAPRequest(url, signedBody, baseHttpSignature(), headers)
182
183 expect(response.statusCode).to.equal(204)
184 })
185 })
186
187 after(async function () {
188 this.timeout(10000)
189
190 await cleanupTests(servers)
191
192 await closeAllSequelize(servers)
193 })
194 })