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