]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blame - server/tests/plugins/external-auth.ts
Translated using Weblate (Croatian)
[github/Chocobozzz/PeerTube.git] / server / tests / plugins / external-auth.ts
CommitLineData
9107d791
C
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2
3import 'mocha'
4import { expect } from 'chai'
c55e3d72
C
5import { wait } from '@shared/core-utils'
6import { HttpStatusCode, UserRole } from '@shared/models'
9107d791 7import {
ae2abfd3 8 cleanupTests,
254d3579 9 createSingleServer,
4c7e60bc 10 decodeQueryString,
254d3579 11 PeerTubeServer,
4c7e60bc 12 PluginsCommand,
c55e3d72 13 setAccessTokensToServers
bf54587a 14} from '@shared/server-commands'
9107d791
C
15
16async function loginExternal (options: {
254d3579 17 server: PeerTubeServer
9107d791
C
18 npmName: string
19 authName: string
20 username: string
21 query?: any
c0e8b12e
C
22 expectedStatus?: HttpStatusCode
23 expectedStatusStep2?: HttpStatusCode
9107d791 24}) {
89d241a7 25 const res = await options.server.plugins.getExternalAuth({
9107d791
C
26 npmName: options.npmName,
27 npmVersion: '0.0.1',
28 authName: options.authName,
29 query: options.query,
c0e8b12e 30 expectedStatus: options.expectedStatus || HttpStatusCode.FOUND_302
9107d791
C
31 })
32
2d53be02 33 if (res.status !== HttpStatusCode.FOUND_302) return
9107d791
C
34
35 const location = res.header.location
36 const { externalAuthToken } = decodeQueryString(location)
37
89d241a7 38 const resLogin = await options.server.login.loginUsingExternalToken({
41d1d075
C
39 username: options.username,
40 externalAuthToken: externalAuthToken as string,
c0e8b12e 41 expectedStatus: options.expectedStatusStep2
41d1d075 42 })
9107d791
C
43
44 return resLogin.body
45}
46
47describe('Test external auth plugins', function () {
254d3579 48 let server: PeerTubeServer
9107d791
C
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
0b6f5316
C
61 server = await createSingleServer(1, {
62 rates_limit: {
63 login: {
64 max: 30
65 }
66 }
67 })
68
9107d791
C
69 await setAccessTokensToServers([ server ])
70
74fd2643 71 for (const suffix of [ 'one', 'two', 'three' ]) {
89d241a7 72 await server.plugins.install({ path: PluginsCommand.getPluginTestPath('-external-auth-' + suffix) })
9107d791
C
73 }
74 })
75
76 it('Should display the correct configuration', async function () {
89d241a7 77 const config = await server.config.getConfig()
9107d791
C
78
79 const auths = config.plugin.registeredExternalAuths
0b6f5316 80 expect(auths).to.have.lengthOf(9)
9107d791
C
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 () {
89d241a7 89 const res = await server.plugins.getExternalAuth({
9107d791
C
90 npmName: 'test-external-auth-one',
91 npmVersion: '0.0.1',
92 authName: 'external-auth-1',
93 query: {
94 username: 'cyan'
95 },
ae2abfd3 96 expectedStatus: HttpStatusCode.FOUND_302
9107d791
C
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 () {
89d241a7 111 const command = server.login
41d1d075
C
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 })
9107d791
C
115 })
116
117 it('Should reject auto external login with a missing or invalid username', async function () {
89d241a7 118 const command = server.login
41d1d075
C
119
120 await command.loginUsingExternalToken({ username: '', externalAuthToken, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
121 await command.loginUsingExternalToken({ username: '', externalAuthToken, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
9107d791
C
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
89d241a7 129 await server.login.loginUsingExternalToken({
41d1d075
C
130 username: 'cyan',
131 externalAuthToken,
132 expectedStatus: HttpStatusCode.BAD_REQUEST_400
133 })
9107d791 134
65058050 135 await server.servers.waitUntilLog('expired external auth token', 4)
9107d791
C
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 {
89d241a7 155 const body = await server.users.getMyInfo({ token: cyanAccessToken })
9107d791
C
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 {
89d241a7 177 const body = await server.users.getMyInfo({ token: kefkaAccessToken })
9107d791
C
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 {
89d241a7 187 const resRefresh = await server.login.refreshToken({ refreshToken: cyanRefreshToken })
9107d791
C
188 cyanAccessToken = resRefresh.body.access_token
189 cyanRefreshToken = resRefresh.body.refresh_token
190
89d241a7 191 const body = await server.users.getMyInfo({ token: cyanAccessToken })
7926c5f9 192 expect(body.username).to.equal('cyan')
9107d791
C
193 }
194
195 {
89d241a7 196 await server.login.refreshToken({ refreshToken: kefkaRefreshToken, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
9107d791
C
197 }
198 })
199
200 it('Should update Cyan profile', async function () {
89d241a7 201 await server.users.updateMe({
7926c5f9 202 token: cyanAccessToken,
9107d791
C
203 displayName: 'Cyan Garamonde',
204 description: 'Retainer to the king of Doma'
205 })
206
89d241a7 207 const body = await server.users.getMyInfo({ token: cyanAccessToken })
9107d791
C
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 () {
89d241a7 213 await server.login.logout({ token: cyanAccessToken })
9107d791
C
214 })
215
216 it('Should have logged out Cyan', async function () {
89d241a7 217 await server.servers.waitUntilLog('On logout cyan')
9107d791 218
89d241a7 219 await server.users.getMyInfo({ token: cyanAccessToken, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
9107d791
C
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
89d241a7 237 const body = await server.users.getMyInfo({ token: cyanAccessToken })
9107d791
C
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
9a7fd960 244 it('Should not update an external auth email', async function () {
89d241a7 245 await server.users.updateMe({
7926c5f9 246 token: cyanAccessToken,
9a7fd960
C
247 email: 'toto@example.com',
248 currentPassword: 'toto',
7926c5f9 249 expectedStatus: HttpStatusCode.BAD_REQUEST_400
9a7fd960
C
250 })
251 })
252
9107d791
C
253 it('Should reject token of Kefka by the plugin hook', async function () {
254 this.timeout(10000)
255
256 await wait(5000)
257
89d241a7 258 await server.users.getMyInfo({ token: kefkaAccessToken, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
9107d791
C
259 })
260
a4995eb7 261 it('Should unregister external-auth-2 and do not login existing Kefka', async function () {
89d241a7 262 await server.plugins.updateSettings({
a4995eb7
C
263 npmName: 'peertube-plugin-test-external-auth-one',
264 settings: { disableKefka: true }
265 })
266
89d241a7 267 await server.login.login({ user: { username: 'kefka', password: 'fake' }, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
a4995eb7
C
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',
c0e8b12e 277 expectedStatus: HttpStatusCode.NOT_FOUND_404
a4995eb7
C
278 })
279 })
280
281 it('Should have disabled this auth', async function () {
89d241a7 282 const config = await server.config.getConfig()
a4995eb7
C
283
284 const auths = config.plugin.registeredExternalAuths
0b6f5316 285 expect(auths).to.have.lengthOf(8)
a4995eb7
C
286
287 const auth1 = auths.find(a => a.authName === 'external-auth-2')
288 expect(auth1).to.not.exist
289 })
290
9107d791 291 it('Should uninstall the plugin one and do not login Cyan', async function () {
89d241a7 292 await server.plugins.uninstall({ npmName: 'peertube-plugin-test-external-auth-one' })
9107d791
C
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',
c0e8b12e 302 expectedStatus: HttpStatusCode.NOT_FOUND_404
9107d791 303 })
d253bfaa 304
89d241a7
C
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 })
d253bfaa
C
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',
c0e8b12e 316 expectedStatusStep2: HttpStatusCode.BAD_REQUEST_400
d253bfaa
C
317 })
318
319 await loginExternal({
320 server,
321 npmName: 'test-external-auth-two',
322 authName: 'external-auth-4',
323 username: 'kefka',
c0e8b12e 324 expectedStatusStep2: HttpStatusCode.BAD_REQUEST_400
d253bfaa
C
325 })
326 })
327
0b6f5316 328 it('Should not login an existing user email', async function () {
89d241a7 329 await server.users.create({ username: 'existing_user', password: 'super_password' })
d253bfaa
C
330
331 await loginExternal({
332 server,
333 npmName: 'test-external-auth-two',
334 authName: 'external-auth-6',
335 username: 'existing_user',
c0e8b12e 336 expectedStatusStep2: HttpStatusCode.BAD_REQUEST_400
d253bfaa 337 })
9107d791
C
338 })
339
0b6f5316
C
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
9107d791 362 it('Should display the correct configuration', async function () {
89d241a7 363 const config = await server.config.getConfig()
9107d791
C
364
365 const auths = config.plugin.registeredExternalAuths
0b6f5316 366 expect(auths).to.have.lengthOf(7)
9107d791
C
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 })
74fd2643
C
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
89d241a7 384 const { redirectUrl } = await server.login.logout({ token: resLogin.access_token })
41d1d075 385 expect(redirectUrl).to.equal('https://example.com/redirectUrl')
74fd2643
C
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
89d241a7 396 const { redirectUrl } = await server.login.logout({ token: resLogin.access_token })
41d1d075 397 expect(redirectUrl).to.equal('https://example.com/redirectUrl?access_token=' + resLogin.access_token)
74fd2643 398 })
9107d791 399})