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