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