]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blob - server/tests/plugins/external-auth.ts
Translated using Weblate (Croatian)
[github/Chocobozzz/PeerTube.git] / server / tests / plugins / external-auth.ts
1 /* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2
3 import 'mocha'
4 import { expect } from 'chai'
5 import { wait } from '@shared/core-utils'
6 import { HttpStatusCode, UserRole } from '@shared/models'
7 import {
8 cleanupTests,
9 createSingleServer,
10 decodeQueryString,
11 PeerTubeServer,
12 PluginsCommand,
13 setAccessTokensToServers
14 } from '@shared/server-commands'
15
16 async function loginExternal (options: {
17 server: PeerTubeServer
18 npmName: string
19 authName: string
20 username: string
21 query?: any
22 expectedStatus?: HttpStatusCode
23 expectedStatusStep2?: HttpStatusCode
24 }) {
25 const res = await options.server.plugins.getExternalAuth({
26 npmName: options.npmName,
27 npmVersion: '0.0.1',
28 authName: options.authName,
29 query: options.query,
30 expectedStatus: options.expectedStatus || HttpStatusCode.FOUND_302
31 })
32
33 if (res.status !== HttpStatusCode.FOUND_302) return
34
35 const location = res.header.location
36 const { externalAuthToken } = decodeQueryString(location)
37
38 const resLogin = await options.server.login.loginUsingExternalToken({
39 username: options.username,
40 externalAuthToken: externalAuthToken as string,
41 expectedStatus: options.expectedStatusStep2
42 })
43
44 return resLogin.body
45 }
46
47 describe('Test external auth plugins', function () {
48 let server: PeerTubeServer
49
50 let cyanAccessToken: string
51 let cyanRefreshToken: string
52
53 let kefkaAccessToken: string
54 let kefkaRefreshToken: string
55
56 let externalAuthToken: string
57
58 before(async function () {
59 this.timeout(30000)
60
61 server = await createSingleServer(1, {
62 rates_limit: {
63 login: {
64 max: 30
65 }
66 }
67 })
68
69 await setAccessTokensToServers([ server ])
70
71 for (const suffix of [ 'one', 'two', 'three' ]) {
72 await server.plugins.install({ path: PluginsCommand.getPluginTestPath('-external-auth-' + suffix) })
73 }
74 })
75
76 it('Should display the correct configuration', async function () {
77 const config = await server.config.getConfig()
78
79 const auths = config.plugin.registeredExternalAuths
80 expect(auths).to.have.lengthOf(9)
81
82 const auth2 = auths.find((a) => a.authName === 'external-auth-2')
83 expect(auth2).to.exist
84 expect(auth2.authDisplayName).to.equal('External Auth 2')
85 expect(auth2.npmName).to.equal('peertube-plugin-test-external-auth-one')
86 })
87
88 it('Should redirect for a Cyan login', async function () {
89 const res = await server.plugins.getExternalAuth({
90 npmName: 'test-external-auth-one',
91 npmVersion: '0.0.1',
92 authName: 'external-auth-1',
93 query: {
94 username: 'cyan'
95 },
96 expectedStatus: HttpStatusCode.FOUND_302
97 })
98
99 const location = res.header.location
100 expect(location.startsWith('/login?')).to.be.true
101
102 const searchParams = decodeQueryString(location)
103
104 expect(searchParams.externalAuthToken).to.exist
105 expect(searchParams.username).to.equal('cyan')
106
107 externalAuthToken = searchParams.externalAuthToken as string
108 })
109
110 it('Should reject auto external login with a missing or invalid token', async function () {
111 const command = server.login
112
113 await command.loginUsingExternalToken({ username: 'cyan', externalAuthToken: '', expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
114 await command.loginUsingExternalToken({ username: 'cyan', externalAuthToken: 'blabla', expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
115 })
116
117 it('Should reject auto external login with a missing or invalid username', async function () {
118 const command = server.login
119
120 await command.loginUsingExternalToken({ username: '', externalAuthToken, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
121 await command.loginUsingExternalToken({ username: '', externalAuthToken, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
122 })
123
124 it('Should reject auto external login with an expired token', async function () {
125 this.timeout(15000)
126
127 await wait(5000)
128
129 await server.login.loginUsingExternalToken({
130 username: 'cyan',
131 externalAuthToken,
132 expectedStatus: HttpStatusCode.BAD_REQUEST_400
133 })
134
135 await server.servers.waitUntilLog('expired external auth token', 4)
136 })
137
138 it('Should auto login Cyan, create the user and use the token', async function () {
139 {
140 const res = await loginExternal({
141 server,
142 npmName: 'test-external-auth-one',
143 authName: 'external-auth-1',
144 query: {
145 username: 'cyan'
146 },
147 username: 'cyan'
148 })
149
150 cyanAccessToken = res.access_token
151 cyanRefreshToken = res.refresh_token
152 }
153
154 {
155 const body = await server.users.getMyInfo({ token: cyanAccessToken })
156 expect(body.username).to.equal('cyan')
157 expect(body.account.displayName).to.equal('cyan')
158 expect(body.email).to.equal('cyan@example.com')
159 expect(body.role).to.equal(UserRole.USER)
160 }
161 })
162
163 it('Should auto login Kefka, create the user and use the token', async function () {
164 {
165 const res = await loginExternal({
166 server,
167 npmName: 'test-external-auth-one',
168 authName: 'external-auth-2',
169 username: 'kefka'
170 })
171
172 kefkaAccessToken = res.access_token
173 kefkaRefreshToken = res.refresh_token
174 }
175
176 {
177 const body = await server.users.getMyInfo({ token: kefkaAccessToken })
178 expect(body.username).to.equal('kefka')
179 expect(body.account.displayName).to.equal('Kefka Palazzo')
180 expect(body.email).to.equal('kefka@example.com')
181 expect(body.role).to.equal(UserRole.ADMINISTRATOR)
182 }
183 })
184
185 it('Should refresh Cyan token, but not Kefka token', async function () {
186 {
187 const resRefresh = await server.login.refreshToken({ refreshToken: cyanRefreshToken })
188 cyanAccessToken = resRefresh.body.access_token
189 cyanRefreshToken = resRefresh.body.refresh_token
190
191 const body = await server.users.getMyInfo({ token: cyanAccessToken })
192 expect(body.username).to.equal('cyan')
193 }
194
195 {
196 await server.login.refreshToken({ refreshToken: kefkaRefreshToken, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
197 }
198 })
199
200 it('Should update Cyan profile', async function () {
201 await server.users.updateMe({
202 token: cyanAccessToken,
203 displayName: 'Cyan Garamonde',
204 description: 'Retainer to the king of Doma'
205 })
206
207 const body = await server.users.getMyInfo({ token: cyanAccessToken })
208 expect(body.account.displayName).to.equal('Cyan Garamonde')
209 expect(body.account.description).to.equal('Retainer to the king of Doma')
210 })
211
212 it('Should logout Cyan', async function () {
213 await server.login.logout({ token: cyanAccessToken })
214 })
215
216 it('Should have logged out Cyan', async function () {
217 await server.servers.waitUntilLog('On logout cyan')
218
219 await server.users.getMyInfo({ token: cyanAccessToken, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
220 })
221
222 it('Should login Cyan and keep the old existing profile', async function () {
223 {
224 const res = await loginExternal({
225 server,
226 npmName: 'test-external-auth-one',
227 authName: 'external-auth-1',
228 query: {
229 username: 'cyan'
230 },
231 username: 'cyan'
232 })
233
234 cyanAccessToken = res.access_token
235 }
236
237 const body = await server.users.getMyInfo({ token: cyanAccessToken })
238 expect(body.username).to.equal('cyan')
239 expect(body.account.displayName).to.equal('Cyan Garamonde')
240 expect(body.account.description).to.equal('Retainer to the king of Doma')
241 expect(body.role).to.equal(UserRole.USER)
242 })
243
244 it('Should not update an external auth email', async function () {
245 await server.users.updateMe({
246 token: cyanAccessToken,
247 email: 'toto@example.com',
248 currentPassword: 'toto',
249 expectedStatus: HttpStatusCode.BAD_REQUEST_400
250 })
251 })
252
253 it('Should reject token of Kefka by the plugin hook', async function () {
254 this.timeout(10000)
255
256 await wait(5000)
257
258 await server.users.getMyInfo({ token: kefkaAccessToken, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
259 })
260
261 it('Should unregister external-auth-2 and do not login existing Kefka', async function () {
262 await server.plugins.updateSettings({
263 npmName: 'peertube-plugin-test-external-auth-one',
264 settings: { disableKefka: true }
265 })
266
267 await server.login.login({ user: { username: 'kefka', password: 'fake' }, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
268
269 await loginExternal({
270 server,
271 npmName: 'test-external-auth-one',
272 authName: 'external-auth-2',
273 query: {
274 username: 'kefka'
275 },
276 username: 'kefka',
277 expectedStatus: HttpStatusCode.NOT_FOUND_404
278 })
279 })
280
281 it('Should have disabled this auth', async function () {
282 const config = await server.config.getConfig()
283
284 const auths = config.plugin.registeredExternalAuths
285 expect(auths).to.have.lengthOf(8)
286
287 const auth1 = auths.find(a => a.authName === 'external-auth-2')
288 expect(auth1).to.not.exist
289 })
290
291 it('Should uninstall the plugin one and do not login Cyan', async function () {
292 await server.plugins.uninstall({ npmName: 'peertube-plugin-test-external-auth-one' })
293
294 await loginExternal({
295 server,
296 npmName: 'test-external-auth-one',
297 authName: 'external-auth-1',
298 query: {
299 username: 'cyan'
300 },
301 username: 'cyan',
302 expectedStatus: HttpStatusCode.NOT_FOUND_404
303 })
304
305 await server.login.login({ user: { username: 'cyan', password: null }, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
306 await server.login.login({ user: { username: 'cyan', password: '' }, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
307 await server.login.login({ user: { username: 'cyan', password: 'fake' }, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
308 })
309
310 it('Should not login kefka with another plugin', async function () {
311 await loginExternal({
312 server,
313 npmName: 'test-external-auth-two',
314 authName: 'external-auth-4',
315 username: 'kefka2',
316 expectedStatusStep2: HttpStatusCode.BAD_REQUEST_400
317 })
318
319 await loginExternal({
320 server,
321 npmName: 'test-external-auth-two',
322 authName: 'external-auth-4',
323 username: 'kefka',
324 expectedStatusStep2: HttpStatusCode.BAD_REQUEST_400
325 })
326 })
327
328 it('Should not login an existing user email', async function () {
329 await server.users.create({ username: 'existing_user', password: 'super_password' })
330
331 await loginExternal({
332 server,
333 npmName: 'test-external-auth-two',
334 authName: 'external-auth-6',
335 username: 'existing_user',
336 expectedStatusStep2: HttpStatusCode.BAD_REQUEST_400
337 })
338 })
339
340 it('Should be able to login an existing user username and channel', async function () {
341 await server.users.create({ username: 'existing_user2' })
342 await server.users.create({ username: 'existing_user2-1_channel' })
343
344 // Test twice to ensure we don't generate a username on every login
345 for (let i = 0; i < 2; i++) {
346 const res = await loginExternal({
347 server,
348 npmName: 'test-external-auth-two',
349 authName: 'external-auth-7',
350 username: 'existing_user2'
351 })
352
353 const token = res.access_token
354
355 const myInfo = await server.users.getMyInfo({ token })
356 expect(myInfo.username).to.equal('existing_user2-1')
357
358 expect(myInfo.videoChannels[0].name).to.equal('existing_user2-1_channel-1')
359 }
360 })
361
362 it('Should display the correct configuration', async function () {
363 const config = await server.config.getConfig()
364
365 const auths = config.plugin.registeredExternalAuths
366 expect(auths).to.have.lengthOf(7)
367
368 const auth2 = auths.find((a) => a.authName === 'external-auth-2')
369 expect(auth2).to.not.exist
370 })
371
372 after(async function () {
373 await cleanupTests([ server ])
374 })
375
376 it('Should forward the redirectUrl if the plugin returns one', async function () {
377 const resLogin = await loginExternal({
378 server,
379 npmName: 'test-external-auth-three',
380 authName: 'external-auth-7',
381 username: 'cid'
382 })
383
384 const { redirectUrl } = await server.login.logout({ token: resLogin.access_token })
385 expect(redirectUrl).to.equal('https://example.com/redirectUrl')
386 })
387
388 it('Should call the plugin\'s onLogout method with the request', async function () {
389 const resLogin = await loginExternal({
390 server,
391 npmName: 'test-external-auth-three',
392 authName: 'external-auth-8',
393 username: 'cid'
394 })
395
396 const { redirectUrl } = await server.login.logout({ token: resLogin.access_token })
397 expect(redirectUrl).to.equal('https://example.com/redirectUrl?access_token=' + resLogin.access_token)
398 })
399 })