]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blame_incremental - server/tests/plugins/external-auth.ts
Fix server run
[github/Chocobozzz/PeerTube.git] / server / tests / plugins / external-auth.ts
... / ...
CommitLineData
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2
3import 'mocha'
4import { expect } from 'chai'
5import { HttpStatusCode } from '@shared/core-utils'
6import {
7 cleanupTests,
8 decodeQueryString,
9 createSingleServer,
10 PluginsCommand,
11 PeerTubeServer,
12 setAccessTokensToServers,
13 wait
14} from '@shared/extra-utils'
15import { UserRole } from '@shared/models'
16
17async function loginExternal (options: {
18 server: PeerTubeServer
19 npmName: string
20 authName: string
21 username: string
22 query?: any
23 statusCodeExpected?: HttpStatusCode
24 statusCodeExpectedStep2?: HttpStatusCode
25}) {
26 const res = await options.server.plugins.getExternalAuth({
27 npmName: options.npmName,
28 npmVersion: '0.0.1',
29 authName: options.authName,
30 query: options.query,
31 expectedStatus: options.statusCodeExpected || HttpStatusCode.FOUND_302
32 })
33
34 if (res.status !== HttpStatusCode.FOUND_302) return
35
36 const location = res.header.location
37 const { externalAuthToken } = decodeQueryString(location)
38
39 const resLogin = await options.server.login.loginUsingExternalToken({
40 username: options.username,
41 externalAuthToken: externalAuthToken as string,
42 expectedStatus: options.statusCodeExpectedStep2
43 })
44
45 return resLogin.body
46}
47
48describe('Test external auth plugins', function () {
49 let server: PeerTubeServer
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
62 server = await createSingleServer(1)
63 await setAccessTokensToServers([ server ])
64
65 for (const suffix of [ 'one', 'two', 'three' ]) {
66 await server.plugins.install({ path: PluginsCommand.getPluginTestPath('-external-auth-' + suffix) })
67 }
68 })
69
70 it('Should display the correct configuration', async function () {
71 const config = await server.config.getConfig()
72
73 const auths = config.plugin.registeredExternalAuths
74 expect(auths).to.have.lengthOf(8)
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 () {
83 const res = await server.plugins.getExternalAuth({
84 npmName: 'test-external-auth-one',
85 npmVersion: '0.0.1',
86 authName: 'external-auth-1',
87 query: {
88 username: 'cyan'
89 },
90 expectedStatus: HttpStatusCode.FOUND_302
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 () {
105 const command = server.login
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 })
109 })
110
111 it('Should reject auto external login with a missing or invalid username', async function () {
112 const command = server.login
113
114 await command.loginUsingExternalToken({ username: '', externalAuthToken, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
115 await command.loginUsingExternalToken({ username: '', externalAuthToken, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
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
123 await server.login.loginUsingExternalToken({
124 username: 'cyan',
125 externalAuthToken,
126 expectedStatus: HttpStatusCode.BAD_REQUEST_400
127 })
128
129 await server.servers.waitUntilLog('expired external auth token', 2)
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 {
149 const body = await server.users.getMyInfo({ token: cyanAccessToken })
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 {
171 const body = await server.users.getMyInfo({ token: kefkaAccessToken })
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 {
181 const resRefresh = await server.login.refreshToken({ refreshToken: cyanRefreshToken })
182 cyanAccessToken = resRefresh.body.access_token
183 cyanRefreshToken = resRefresh.body.refresh_token
184
185 const body = await server.users.getMyInfo({ token: cyanAccessToken })
186 expect(body.username).to.equal('cyan')
187 }
188
189 {
190 await server.login.refreshToken({ refreshToken: kefkaRefreshToken, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
191 }
192 })
193
194 it('Should update Cyan profile', async function () {
195 await server.users.updateMe({
196 token: cyanAccessToken,
197 displayName: 'Cyan Garamonde',
198 description: 'Retainer to the king of Doma'
199 })
200
201 const body = await server.users.getMyInfo({ token: cyanAccessToken })
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 () {
207 await server.login.logout({ token: cyanAccessToken })
208 })
209
210 it('Should have logged out Cyan', async function () {
211 await server.servers.waitUntilLog('On logout cyan')
212
213 await server.users.getMyInfo({ token: cyanAccessToken, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
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
231 const body = await server.users.getMyInfo({ token: cyanAccessToken })
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
238 it('Should not update an external auth email', async function () {
239 await server.users.updateMe({
240 token: cyanAccessToken,
241 email: 'toto@example.com',
242 currentPassword: 'toto',
243 expectedStatus: HttpStatusCode.BAD_REQUEST_400
244 })
245 })
246
247 it('Should reject token of Kefka by the plugin hook', async function () {
248 this.timeout(10000)
249
250 await wait(5000)
251
252 await server.users.getMyInfo({ token: kefkaAccessToken, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
253 })
254
255 it('Should unregister external-auth-2 and do not login existing Kefka', async function () {
256 await server.plugins.updateSettings({
257 npmName: 'peertube-plugin-test-external-auth-one',
258 settings: { disableKefka: true }
259 })
260
261 await server.login.login({ user: { username: 'kefka', password: 'fake' }, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
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',
271 statusCodeExpected: HttpStatusCode.NOT_FOUND_404
272 })
273 })
274
275 it('Should have disabled this auth', async function () {
276 const config = await server.config.getConfig()
277
278 const auths = config.plugin.registeredExternalAuths
279 expect(auths).to.have.lengthOf(7)
280
281 const auth1 = auths.find(a => a.authName === 'external-auth-2')
282 expect(auth1).to.not.exist
283 })
284
285 it('Should uninstall the plugin one and do not login Cyan', async function () {
286 await server.plugins.uninstall({ npmName: 'peertube-plugin-test-external-auth-one' })
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',
296 statusCodeExpected: HttpStatusCode.NOT_FOUND_404
297 })
298
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 })
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',
310 statusCodeExpectedStep2: HttpStatusCode.BAD_REQUEST_400
311 })
312
313 await loginExternal({
314 server,
315 npmName: 'test-external-auth-two',
316 authName: 'external-auth-4',
317 username: 'kefka',
318 statusCodeExpectedStep2: HttpStatusCode.BAD_REQUEST_400
319 })
320 })
321
322 it('Should not login an existing user', async function () {
323 await server.users.create({ username: 'existing_user', password: 'super_password' })
324
325 await loginExternal({
326 server,
327 npmName: 'test-external-auth-two',
328 authName: 'external-auth-6',
329 username: 'existing_user',
330 statusCodeExpectedStep2: HttpStatusCode.BAD_REQUEST_400
331 })
332 })
333
334 it('Should display the correct configuration', async function () {
335 const config = await server.config.getConfig()
336
337 const auths = config.plugin.registeredExternalAuths
338 expect(auths).to.have.lengthOf(6)
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 })
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
356 const { redirectUrl } = await server.login.logout({ token: resLogin.access_token })
357 expect(redirectUrl).to.equal('https://example.com/redirectUrl')
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
368 const { redirectUrl } = await server.login.logout({ token: resLogin.access_token })
369 expect(redirectUrl).to.equal('https://example.com/redirectUrl?access_token=' + resLogin.access_token)
370 })
371})