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