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