dd03766c963208dd90ae4fea8f756a3778f1ac2e
[oweals/peertube.git] / server / tests / api / check-params / plugins.ts
1 /* tslint:disable:no-unused-expression */
2
3 import 'mocha'
4
5 import {
6   checkBadCountPagination,
7   checkBadSortPagination,
8   checkBadStartPagination,
9   cleanupTests,
10   createUser,
11   flushAndRunServer,
12   immutableAssign,
13   installPlugin,
14   makeGetRequest, makePostBodyRequest, makePutBodyRequest,
15   ServerInfo,
16   setAccessTokensToServers,
17   userLogin
18 } from '../../../../shared/extra-utils'
19 import { PluginType } from '../../../../shared/models/plugins/plugin.type'
20 import { PeerTubePlugin } from '../../../../shared/models/plugins/peertube-plugin.model'
21
22 describe('Test server plugins API validators', function () {
23   let server: ServerInfo
24   let userAccessToken = null
25
26   const npmPlugin = 'peertube-plugin-hello-world'
27   const pluginName = 'hello-world'
28   let npmVersion: string
29
30   const themePlugin = 'peertube-theme-background-red'
31   const themeName = 'background-red'
32   let themeVersion: string
33
34   // ---------------------------------------------------------------
35
36   before(async function () {
37     this.timeout(30000)
38
39     server = await flushAndRunServer(1)
40
41     await setAccessTokensToServers([ server ])
42
43     const user = {
44       username: 'user1',
45       password: 'password'
46     }
47
48     await createUser({ url: server.url, accessToken: server.accessToken, username: user.username, password: user.password })
49     userAccessToken = await userLogin(server, user)
50
51     {
52       const res = await installPlugin({ url: server.url, accessToken: server.accessToken, npmName: npmPlugin })
53       const plugin = res.body as PeerTubePlugin
54       npmVersion = plugin.version
55     }
56
57     {
58       const res = await installPlugin({ url: server.url, accessToken: server.accessToken, npmName: themePlugin })
59       const plugin = res.body as PeerTubePlugin
60       themeVersion = plugin.version
61     }
62   })
63
64   describe('With static plugin routes', function () {
65     it('Should fail with an unknown plugin name/plugin version', async function () {
66       const paths = [
67         '/plugins/' + pluginName + '/0.0.1/static/images/chocobo.png',
68         '/plugins/' + pluginName + '/0.0.1/client-scripts/client/common-client-plugin.js',
69         '/themes/' + themeName + '/0.0.1/static/images/chocobo.png',
70         '/themes/' + themeName + '/0.0.1/client-scripts/client/video-watch-client-plugin.js',
71         '/themes/' + themeName + '/0.0.1/css/assets/style1.css'
72       ]
73
74       for (const p of paths) {
75         await makeGetRequest({ url: server.url, path: p, statusCodeExpected: 404 })
76       }
77     })
78
79     it('Should fail when requesting a plugin in the theme path', async function () {
80       await makeGetRequest({
81         url: server.url,
82         path: '/themes/' + pluginName + '/' + npmVersion + '/static/images/chocobo.png',
83         statusCodeExpected: 404
84       })
85     })
86
87     it('Should fail with invalid versions', async function () {
88       const paths = [
89         '/plugins/' + pluginName + '/0.0.1.1/static/images/chocobo.png',
90         '/plugins/' + pluginName + '/0.1/client-scripts/client/common-client-plugin.js',
91         '/themes/' + themeName + '/1/static/images/chocobo.png',
92         '/themes/' + themeName + '/0.0.1000a/client-scripts/client/video-watch-client-plugin.js',
93         '/themes/' + themeName + '/0.a.1/css/assets/style1.css'
94       ]
95
96       for (const p of paths) {
97         await makeGetRequest({ url: server.url, path: p, statusCodeExpected: 400 })
98       }
99     })
100
101     it('Should fail with invalid paths', async function () {
102       const paths = [
103         '/plugins/' + pluginName + '/' + npmVersion + '/static/images/../chocobo.png',
104         '/plugins/' + pluginName + '/' + npmVersion + '/client-scripts/../client/common-client-plugin.js',
105         '/themes/' + themeName + '/' + themeVersion + '/static/../images/chocobo.png',
106         '/themes/' + themeName + '/' + themeVersion + '/client-scripts/client/video-watch-client-plugin.js/..',
107         '/themes/' + themeName + '/' + themeVersion + '/css/../assets/style1.css'
108       ]
109
110       for (const p of paths) {
111         await makeGetRequest({ url: server.url, path: p, statusCodeExpected: 400 })
112       }
113     })
114
115     it('Should fail with an unknown static file', async function () {
116       const paths = [
117         '/plugins/' + pluginName + '/' + npmVersion + '/static/fake/chocobo.png',
118         '/plugins/' + pluginName + '/' + npmVersion + '/client-scripts/client/fake.js',
119         '/themes/' + themeName + '/' + themeVersion + '/static/fake/chocobo.png',
120         '/themes/' + themeName + '/' + themeVersion + '/client-scripts/client/fake.js'
121       ]
122
123       for (const p of paths) {
124         await makeGetRequest({ url: server.url, path: p, statusCodeExpected: 404 })
125       }
126     })
127
128     it('Should fail with an unknown CSS file', async function () {
129       await makeGetRequest({
130         url: server.url,
131         path: '/themes/' + themeName + '/' + themeVersion + '/css/assets/fake.css',
132         statusCodeExpected: 404
133       })
134     })
135
136     it('Should succeed with the correct parameters', async function () {
137       const paths = [
138         '/plugins/' + pluginName + '/' + npmVersion + '/static/images/chocobo.png',
139         '/plugins/' + pluginName + '/' + npmVersion + '/client-scripts/client/common-client-plugin.js',
140         '/themes/' + themeName + '/' + themeVersion + '/static/images/chocobo.png',
141         '/themes/' + themeName + '/' + themeVersion + '/client-scripts/client/video-watch-client-plugin.js',
142         '/themes/' + themeName + '/' + themeVersion + '/css/assets/style1.css'
143       ]
144
145       for (const p of paths) {
146         await makeGetRequest({ url: server.url, path: p, statusCodeExpected: 200 })
147       }
148     })
149   })
150
151   describe('When listing available plugins/themes', function () {
152     const path = '/api/v1/plugins/available'
153     const baseQuery = {
154       search: 'super search',
155       pluginType: PluginType.PLUGIN
156     }
157
158     it('Should fail with an invalid token', async function () {
159       await makeGetRequest({
160         url: server.url,
161         path,
162         token: 'fake_token',
163         query: baseQuery,
164         statusCodeExpected: 401
165       })
166     })
167
168     it('Should fail if the user is not an administrator', async function () {
169       await makeGetRequest({
170         url: server.url,
171         path,
172         token: userAccessToken,
173         query: baseQuery,
174         statusCodeExpected: 403
175       })
176     })
177
178     it('Should fail with a bad start pagination', async function () {
179       await checkBadStartPagination(server.url, path, server.accessToken)
180     })
181
182     it('Should fail with a bad count pagination', async function () {
183       await checkBadCountPagination(server.url, path, server.accessToken)
184     })
185
186     it('Should fail with an incorrect sort', async function () {
187       await checkBadSortPagination(server.url, path, server.accessToken)
188     })
189
190     it('Should fail with an invalid plugin type', async function () {
191       const query = immutableAssign(baseQuery, { pluginType: 5 })
192
193       await makeGetRequest({
194         url: server.url,
195         path,
196         token: server.accessToken,
197         query
198       })
199     })
200
201     it('Should success with the correct parameters', async function () {
202       await makeGetRequest({
203         url: server.url,
204         path,
205         token: server.accessToken,
206         query: baseQuery,
207         statusCodeExpected: 200
208       })
209     })
210   })
211
212   describe('When listing local plugins/themes', function () {
213     const path = '/api/v1/plugins'
214     const baseQuery = {
215       pluginType: PluginType.THEME
216     }
217
218     it('Should fail with an invalid token', async function () {
219       await makeGetRequest({
220         url: server.url,
221         path,
222         token: 'fake_token',
223         query: baseQuery,
224         statusCodeExpected: 401
225       })
226     })
227
228     it('Should fail if the user is not an administrator', async function () {
229       await makeGetRequest({
230         url: server.url,
231         path,
232         token: userAccessToken,
233         query: baseQuery,
234         statusCodeExpected: 403
235       })
236     })
237
238     it('Should fail with a bad start pagination', async function () {
239       await checkBadStartPagination(server.url, path, server.accessToken)
240     })
241
242     it('Should fail with a bad count pagination', async function () {
243       await checkBadCountPagination(server.url, path, server.accessToken)
244     })
245
246     it('Should fail with an incorrect sort', async function () {
247       await checkBadSortPagination(server.url, path, server.accessToken)
248     })
249
250     it('Should fail with an invalid plugin type', async function () {
251       const query = immutableAssign(baseQuery, { pluginType: 5 })
252
253       await makeGetRequest({
254         url: server.url,
255         path,
256         token: server.accessToken,
257         query
258       })
259     })
260
261     it('Should success with the correct parameters', async function () {
262       await makeGetRequest({
263         url: server.url,
264         path,
265         token: server.accessToken,
266         query: baseQuery,
267         statusCodeExpected: 200
268       })
269     })
270   })
271
272   describe('When getting a plugin or the registered settings', function () {
273     const path = '/api/v1/plugins/'
274
275     it('Should fail with an invalid token', async function () {
276       for (const suffix of [ npmPlugin, `${npmPlugin}/registered-settings` ]) {
277         await makeGetRequest({
278           url: server.url,
279           path: path + suffix,
280           token: 'fake_token',
281           statusCodeExpected: 401
282         })
283       }
284     })
285
286     it('Should fail if the user is not an administrator', async function () {
287       for (const suffix of [ npmPlugin, `${npmPlugin}/registered-settings` ]) {
288         await makeGetRequest({
289           url: server.url,
290           path: path + suffix,
291           token: userAccessToken,
292           statusCodeExpected: 403
293         })
294       }
295     })
296
297     it('Should fail with an invalid npm name', async function () {
298       for (const suffix of [ 'toto', 'toto/registered-settings' ]) {
299         await makeGetRequest({
300           url: server.url,
301           path: path + suffix,
302           token: server.accessToken,
303           statusCodeExpected: 400
304         })
305       }
306
307       for (const suffix of [ 'peertube-plugin-TOTO', 'peertube-plugin-TOTO/registered-settings' ]) {
308         await makeGetRequest({
309           url: server.url,
310           path: path + suffix,
311           token: server.accessToken,
312           statusCodeExpected: 400
313         })
314       }
315     })
316
317     it('Should fail with an unknown plugin', async function () {
318       for (const suffix of [ 'peertube-plugin-toto', 'peertube-plugin-toto/registered-settings' ]) {
319         await makeGetRequest({
320           url: server.url,
321           path: path + suffix,
322           token: server.accessToken,
323           statusCodeExpected: 404
324         })
325       }
326     })
327
328     it('Should succeed with the correct parameters', async function () {
329       for (const suffix of [ npmPlugin, `${npmPlugin}/registered-settings` ]) {
330         await makeGetRequest({
331           url: server.url,
332           path: path + suffix,
333           token: server.accessToken,
334           statusCodeExpected: 200
335         })
336       }
337     })
338   })
339
340   describe('When updating plugin settings', function () {
341     const path = '/api/v1/plugins/'
342     const settings = { setting1: 'value1' }
343
344     it('Should fail with an invalid token', async function () {
345       await makePutBodyRequest({
346         url: server.url,
347         path: path + npmPlugin + '/settings',
348         fields: { settings },
349         token: 'fake_token',
350         statusCodeExpected: 401
351       })
352     })
353
354     it('Should fail if the user is not an administrator', async function () {
355       await makePutBodyRequest({
356         url: server.url,
357         path: path + npmPlugin + '/settings',
358         fields: { settings },
359         token: userAccessToken,
360         statusCodeExpected: 403
361       })
362     })
363
364     it('Should fail with an invalid npm name', async function () {
365       await makePutBodyRequest({
366         url: server.url,
367         path: path + 'toto/settings',
368         fields: { settings },
369         token: server.accessToken,
370         statusCodeExpected: 400
371       })
372
373       await makePutBodyRequest({
374         url: server.url,
375         path: path + 'peertube-plugin-TOTO/settings',
376         fields: { settings },
377         token: server.accessToken,
378         statusCodeExpected: 400
379       })
380     })
381
382     it('Should fail with an unknown plugin', async function () {
383       await makePutBodyRequest({
384         url: server.url,
385         path: path + 'peertube-plugin-toto/settings',
386         fields: { settings },
387         token: server.accessToken,
388         statusCodeExpected: 404
389       })
390     })
391
392     it('Should succeed with the correct parameters', async function () {
393       await makePutBodyRequest({
394         url: server.url,
395         path: path + npmPlugin + '/settings',
396         fields: { settings },
397         token: server.accessToken,
398         statusCodeExpected: 204
399       })
400     })
401   })
402
403   describe('When installing/updating/uninstalling a plugin', function () {
404     const path = '/api/v1/plugins/'
405
406     it('Should fail with an invalid token', async function () {
407       for (const suffix of [ 'install', 'update', 'uninstall' ]) {
408         await makePostBodyRequest({
409           url: server.url,
410           path: path + suffix,
411           fields: { npmName: npmPlugin },
412           token: 'fake_token',
413           statusCodeExpected: 401
414         })
415       }
416     })
417
418     it('Should fail if the user is not an administrator', async function () {
419       for (const suffix of [ 'install', 'update', 'uninstall' ]) {
420         await makePostBodyRequest({
421           url: server.url,
422           path: path + suffix,
423           fields: { npmName: npmPlugin },
424           token: userAccessToken,
425           statusCodeExpected: 403
426         })
427       }
428     })
429
430     it('Should fail with an invalid npm name', async function () {
431       for (const suffix of [ 'install', 'update', 'uninstall' ]) {
432         await makePostBodyRequest({
433           url: server.url,
434           path: path + suffix,
435           fields: { npmName: 'toto' },
436           token: server.accessToken,
437           statusCodeExpected: 400
438         })
439       }
440
441       for (const suffix of [ 'install', 'update', 'uninstall' ]) {
442         await makePostBodyRequest({
443           url: server.url,
444           path: path + suffix,
445           fields: { npmName: 'peertube-plugin-TOTO' },
446           token: server.accessToken,
447           statusCodeExpected: 400
448         })
449       }
450     })
451
452     it('Should succeed with the correct parameters', async function () {
453       const it = [
454         { suffix: 'install', status: 200 },
455         { suffix: 'update', status: 200 },
456         { suffix: 'uninstall', status: 204 }
457       ]
458
459       for (const obj of it) {
460         await makePostBodyRequest({
461           url: server.url,
462           path: path + obj.suffix,
463           fields: { npmName: npmPlugin },
464           token: server.accessToken,
465           statusCodeExpected: obj.status
466         })
467       }
468     })
469   })
470
471   after(async function () {
472     await cleanupTests([ server ])
473   })
474 })