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