Merge branch 'master' into develop
[oweals/peertube.git] / server / tests / api / check-params / plugins.ts
1 /* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
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/auth/fake-auth',
68         '/plugins/' + pluginName + '/0.0.1/static/images/chocobo.png',
69         '/plugins/' + pluginName + '/0.0.1/client-scripts/client/common-client-plugin.js',
70         '/themes/' + themeName + '/0.0.1/static/images/chocobo.png',
71         '/themes/' + themeName + '/0.0.1/client-scripts/client/video-watch-client-plugin.js',
72         '/themes/' + themeName + '/0.0.1/css/assets/style1.css'
73       ]
74
75       for (const p of paths) {
76         await makeGetRequest({ url: server.url, path: p, statusCodeExpected: 404 })
77       }
78     })
79
80     it('Should fail when requesting a plugin in the theme path', async function () {
81       await makeGetRequest({
82         url: server.url,
83         path: '/themes/' + pluginName + '/' + npmVersion + '/static/images/chocobo.png',
84         statusCodeExpected: 404
85       })
86     })
87
88     it('Should fail with invalid versions', async function () {
89       const paths = [
90         '/plugins/' + pluginName + '/0.0.1.1/auth/fake-auth',
91         '/plugins/' + pluginName + '/0.0.1.1/static/images/chocobo.png',
92         '/plugins/' + pluginName + '/0.1/client-scripts/client/common-client-plugin.js',
93         '/themes/' + themeName + '/1/static/images/chocobo.png',
94         '/themes/' + themeName + '/0.0.1000a/client-scripts/client/video-watch-client-plugin.js',
95         '/themes/' + themeName + '/0.a.1/css/assets/style1.css'
96       ]
97
98       for (const p of paths) {
99         await makeGetRequest({ url: server.url, path: p, statusCodeExpected: 400 })
100       }
101     })
102
103     it('Should fail with invalid paths', async function () {
104       const paths = [
105         '/plugins/' + pluginName + '/' + npmVersion + '/static/images/../chocobo.png',
106         '/plugins/' + pluginName + '/' + npmVersion + '/client-scripts/../client/common-client-plugin.js',
107         '/themes/' + themeName + '/' + themeVersion + '/static/../images/chocobo.png',
108         '/themes/' + themeName + '/' + themeVersion + '/client-scripts/client/video-watch-client-plugin.js/..',
109         '/themes/' + themeName + '/' + themeVersion + '/css/../assets/style1.css'
110       ]
111
112       for (const p of paths) {
113         await makeGetRequest({ url: server.url, path: p, statusCodeExpected: 400 })
114       }
115     })
116
117     it('Should fail with an unknown auth name', async function () {
118       const path = '/plugins/' + pluginName + '/' + npmVersion + '/auth/bad-auth'
119
120       await makeGetRequest({ url: server.url, path, statusCodeExpected: 404 })
121     })
122
123     it('Should fail with an unknown static file', async function () {
124       const paths = [
125         '/plugins/' + pluginName + '/' + npmVersion + '/static/fake/chocobo.png',
126         '/plugins/' + pluginName + '/' + npmVersion + '/client-scripts/client/fake.js',
127         '/themes/' + themeName + '/' + themeVersion + '/static/fake/chocobo.png',
128         '/themes/' + themeName + '/' + themeVersion + '/client-scripts/client/fake.js'
129       ]
130
131       for (const p of paths) {
132         await makeGetRequest({ url: server.url, path: p, statusCodeExpected: 404 })
133       }
134     })
135
136     it('Should fail with an unknown CSS file', async function () {
137       await makeGetRequest({
138         url: server.url,
139         path: '/themes/' + themeName + '/' + themeVersion + '/css/assets/fake.css',
140         statusCodeExpected: 404
141       })
142     })
143
144     it('Should succeed with the correct parameters', async function () {
145       const paths = [
146         '/plugins/' + pluginName + '/' + npmVersion + '/static/images/chocobo.png',
147         '/plugins/' + pluginName + '/' + npmVersion + '/client-scripts/client/common-client-plugin.js',
148         '/themes/' + themeName + '/' + themeVersion + '/static/images/chocobo.png',
149         '/themes/' + themeName + '/' + themeVersion + '/client-scripts/client/video-watch-client-plugin.js',
150         '/themes/' + themeName + '/' + themeVersion + '/css/assets/style1.css'
151       ]
152
153       for (const p of paths) {
154         await makeGetRequest({ url: server.url, path: p, statusCodeExpected: 200 })
155       }
156
157       const authPath = '/plugins/' + pluginName + '/' + npmVersion + '/auth/fake-auth'
158       await makeGetRequest({ url: server.url, path: authPath, statusCodeExpected: 302 })
159     })
160   })
161
162   describe('When listing available plugins/themes', function () {
163     const path = '/api/v1/plugins/available'
164     const baseQuery = {
165       search: 'super search',
166       pluginType: PluginType.PLUGIN,
167       currentPeerTubeEngine: '1.2.3'
168     }
169
170     it('Should fail with an invalid token', async function () {
171       await makeGetRequest({
172         url: server.url,
173         path,
174         token: 'fake_token',
175         query: baseQuery,
176         statusCodeExpected: 401
177       })
178     })
179
180     it('Should fail if the user is not an administrator', async function () {
181       await makeGetRequest({
182         url: server.url,
183         path,
184         token: userAccessToken,
185         query: baseQuery,
186         statusCodeExpected: 403
187       })
188     })
189
190     it('Should fail with a bad start pagination', async function () {
191       await checkBadStartPagination(server.url, path, server.accessToken)
192     })
193
194     it('Should fail with a bad count pagination', async function () {
195       await checkBadCountPagination(server.url, path, server.accessToken)
196     })
197
198     it('Should fail with an incorrect sort', async function () {
199       await checkBadSortPagination(server.url, path, server.accessToken)
200     })
201
202     it('Should fail with an invalid plugin type', async function () {
203       const query = immutableAssign(baseQuery, { pluginType: 5 })
204
205       await makeGetRequest({
206         url: server.url,
207         path,
208         token: server.accessToken,
209         query
210       })
211     })
212
213     it('Should fail with an invalid current peertube engine', async function () {
214       const query = immutableAssign(baseQuery, { currentPeerTubeEngine: '1.0' })
215
216       await makeGetRequest({
217         url: server.url,
218         path,
219         token: server.accessToken,
220         query
221       })
222     })
223
224     it('Should success with the correct parameters', async function () {
225       await makeGetRequest({
226         url: server.url,
227         path,
228         token: server.accessToken,
229         query: baseQuery,
230         statusCodeExpected: 200
231       })
232     })
233   })
234
235   describe('When listing local plugins/themes', function () {
236     const path = '/api/v1/plugins'
237     const baseQuery = {
238       pluginType: PluginType.THEME
239     }
240
241     it('Should fail with an invalid token', async function () {
242       await makeGetRequest({
243         url: server.url,
244         path,
245         token: 'fake_token',
246         query: baseQuery,
247         statusCodeExpected: 401
248       })
249     })
250
251     it('Should fail if the user is not an administrator', async function () {
252       await makeGetRequest({
253         url: server.url,
254         path,
255         token: userAccessToken,
256         query: baseQuery,
257         statusCodeExpected: 403
258       })
259     })
260
261     it('Should fail with a bad start pagination', async function () {
262       await checkBadStartPagination(server.url, path, server.accessToken)
263     })
264
265     it('Should fail with a bad count pagination', async function () {
266       await checkBadCountPagination(server.url, path, server.accessToken)
267     })
268
269     it('Should fail with an incorrect sort', async function () {
270       await checkBadSortPagination(server.url, path, server.accessToken)
271     })
272
273     it('Should fail with an invalid plugin type', async function () {
274       const query = immutableAssign(baseQuery, { pluginType: 5 })
275
276       await makeGetRequest({
277         url: server.url,
278         path,
279         token: server.accessToken,
280         query
281       })
282     })
283
284     it('Should success with the correct parameters', async function () {
285       await makeGetRequest({
286         url: server.url,
287         path,
288         token: server.accessToken,
289         query: baseQuery,
290         statusCodeExpected: 200
291       })
292     })
293   })
294
295   describe('When getting a plugin or the registered settings or public settings', function () {
296     const path = '/api/v1/plugins/'
297
298     it('Should fail with an invalid token', async function () {
299       for (const suffix of [ npmPlugin, `${npmPlugin}/registered-settings` ]) {
300         await makeGetRequest({
301           url: server.url,
302           path: path + suffix,
303           token: 'fake_token',
304           statusCodeExpected: 401
305         })
306       }
307     })
308
309     it('Should fail if the user is not an administrator', async function () {
310       for (const suffix of [ npmPlugin, `${npmPlugin}/registered-settings` ]) {
311         await makeGetRequest({
312           url: server.url,
313           path: path + suffix,
314           token: userAccessToken,
315           statusCodeExpected: 403
316         })
317       }
318     })
319
320     it('Should fail with an invalid npm name', async function () {
321       for (const suffix of [ 'toto', 'toto/registered-settings', 'toto/public-settings' ]) {
322         await makeGetRequest({
323           url: server.url,
324           path: path + suffix,
325           token: server.accessToken,
326           statusCodeExpected: 400
327         })
328       }
329
330       for (const suffix of [ 'peertube-plugin-TOTO', 'peertube-plugin-TOTO/registered-settings', 'peertube-plugin-TOTO/public-settings' ]) {
331         await makeGetRequest({
332           url: server.url,
333           path: path + suffix,
334           token: server.accessToken,
335           statusCodeExpected: 400
336         })
337       }
338     })
339
340     it('Should fail with an unknown plugin', async function () {
341       for (const suffix of [ 'peertube-plugin-toto', 'peertube-plugin-toto/registered-settings', 'peertube-plugin-toto/public-settings' ]) {
342         await makeGetRequest({
343           url: server.url,
344           path: path + suffix,
345           token: server.accessToken,
346           statusCodeExpected: 404
347         })
348       }
349     })
350
351     it('Should succeed with the correct parameters', async function () {
352       for (const suffix of [ npmPlugin, `${npmPlugin}/registered-settings`, `${npmPlugin}/public-settings` ]) {
353         await makeGetRequest({
354           url: server.url,
355           path: path + suffix,
356           token: server.accessToken,
357           statusCodeExpected: 200
358         })
359       }
360     })
361   })
362
363   describe('When updating plugin settings', function () {
364     const path = '/api/v1/plugins/'
365     const settings = { setting1: 'value1' }
366
367     it('Should fail with an invalid token', async function () {
368       await makePutBodyRequest({
369         url: server.url,
370         path: path + npmPlugin + '/settings',
371         fields: { settings },
372         token: 'fake_token',
373         statusCodeExpected: 401
374       })
375     })
376
377     it('Should fail if the user is not an administrator', async function () {
378       await makePutBodyRequest({
379         url: server.url,
380         path: path + npmPlugin + '/settings',
381         fields: { settings },
382         token: userAccessToken,
383         statusCodeExpected: 403
384       })
385     })
386
387     it('Should fail with an invalid npm name', async function () {
388       await makePutBodyRequest({
389         url: server.url,
390         path: path + 'toto/settings',
391         fields: { settings },
392         token: server.accessToken,
393         statusCodeExpected: 400
394       })
395
396       await makePutBodyRequest({
397         url: server.url,
398         path: path + 'peertube-plugin-TOTO/settings',
399         fields: { settings },
400         token: server.accessToken,
401         statusCodeExpected: 400
402       })
403     })
404
405     it('Should fail with an unknown plugin', async function () {
406       await makePutBodyRequest({
407         url: server.url,
408         path: path + 'peertube-plugin-toto/settings',
409         fields: { settings },
410         token: server.accessToken,
411         statusCodeExpected: 404
412       })
413     })
414
415     it('Should succeed with the correct parameters', async function () {
416       await makePutBodyRequest({
417         url: server.url,
418         path: path + npmPlugin + '/settings',
419         fields: { settings },
420         token: server.accessToken,
421         statusCodeExpected: 204
422       })
423     })
424   })
425
426   describe('When installing/updating/uninstalling a plugin', function () {
427     const path = '/api/v1/plugins/'
428
429     it('Should fail with an invalid token', async function () {
430       for (const suffix of [ 'install', 'update', 'uninstall' ]) {
431         await makePostBodyRequest({
432           url: server.url,
433           path: path + suffix,
434           fields: { npmName: npmPlugin },
435           token: 'fake_token',
436           statusCodeExpected: 401
437         })
438       }
439     })
440
441     it('Should fail if the user is not an administrator', async function () {
442       for (const suffix of [ 'install', 'update', 'uninstall' ]) {
443         await makePostBodyRequest({
444           url: server.url,
445           path: path + suffix,
446           fields: { npmName: npmPlugin },
447           token: userAccessToken,
448           statusCodeExpected: 403
449         })
450       }
451     })
452
453     it('Should fail with an invalid npm name', async function () {
454       for (const suffix of [ 'install', 'update', 'uninstall' ]) {
455         await makePostBodyRequest({
456           url: server.url,
457           path: path + suffix,
458           fields: { npmName: 'toto' },
459           token: server.accessToken,
460           statusCodeExpected: 400
461         })
462       }
463
464       for (const suffix of [ 'install', 'update', 'uninstall' ]) {
465         await makePostBodyRequest({
466           url: server.url,
467           path: path + suffix,
468           fields: { npmName: 'peertube-plugin-TOTO' },
469           token: server.accessToken,
470           statusCodeExpected: 400
471         })
472       }
473     })
474
475     it('Should succeed with the correct parameters', async function () {
476       this.timeout(10000)
477
478       const it = [
479         { suffix: 'install', status: 200 },
480         { suffix: 'update', status: 200 },
481         { suffix: 'uninstall', status: 204 }
482       ]
483
484       for (const obj of it) {
485         await makePostBodyRequest({
486           url: server.url,
487           path: path + obj.suffix,
488           fields: { npmName: npmPlugin },
489           token: server.accessToken,
490           statusCodeExpected: obj.status
491         })
492       }
493     })
494   })
495
496   after(async function () {
497     await cleanupTests([ server ])
498   })
499 })