]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blob - server/tests/plugins/external-auth.ts
Merge branch 'master' into develop
[github/Chocobozzz/PeerTube.git] / server / tests / plugins / external-auth.ts
1 /* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2
3 import 'mocha'
4 import { expect } from 'chai'
5 import { ServerConfig, User, UserRole } from '@shared/models'
6 import {
7 decodeQueryString,
8 getConfig,
9 getExternalAuth,
10 getMyUserInformation,
11 getPluginTestPath,
12 installPlugin,
13 loginUsingExternalToken,
14 logout,
15 refreshToken,
16 setAccessTokensToServers,
17 uninstallPlugin,
18 updateMyUser,
19 wait,
20 userLogin,
21 updatePluginSettings
22 } from '../../../shared/extra-utils'
23 import { cleanupTests, flushAndRunServer, ServerInfo, waitUntilLog } from '../../../shared/extra-utils/server/servers'
24
25 async function loginExternal (options: {
26 server: ServerInfo
27 npmName: string
28 authName: string
29 username: string
30 query?: any
31 statusCodeExpected?: number
32 }) {
33 const res = await getExternalAuth({
34 url: options.server.url,
35 npmName: options.npmName,
36 npmVersion: '0.0.1',
37 authName: options.authName,
38 query: options.query,
39 statusCodeExpected: options.statusCodeExpected || 302
40 })
41
42 if (res.status !== 302) return
43
44 const location = res.header.location
45 const { externalAuthToken } = decodeQueryString(location)
46
47 const resLogin = await loginUsingExternalToken(
48 options.server,
49 options.username,
50 externalAuthToken as string
51 )
52
53 return resLogin.body
54 }
55
56 describe('Test external auth plugins', function () {
57 let server: ServerInfo
58
59 let cyanAccessToken: string
60 let cyanRefreshToken: string
61
62 let kefkaAccessToken: string
63 let kefkaRefreshToken: string
64
65 let externalAuthToken: string
66
67 before(async function () {
68 this.timeout(30000)
69
70 server = await flushAndRunServer(1)
71 await setAccessTokensToServers([ server ])
72
73 for (const suffix of [ 'one', 'two' ]) {
74 await installPlugin({
75 url: server.url,
76 accessToken: server.accessToken,
77 path: getPluginTestPath('-external-auth-' + suffix)
78 })
79 }
80 })
81
82 it('Should display the correct configuration', async function () {
83 const res = await getConfig(server.url)
84
85 const config: ServerConfig = res.body
86
87 const auths = config.plugin.registeredExternalAuths
88 expect(auths).to.have.lengthOf(3)
89
90 const auth2 = auths.find((a) => a.authName === 'external-auth-2')
91 expect(auth2).to.exist
92 expect(auth2.authDisplayName).to.equal('External Auth 2')
93 expect(auth2.npmName).to.equal('peertube-plugin-test-external-auth-one')
94 })
95
96 it('Should redirect for a Cyan login', async function () {
97 const res = await getExternalAuth({
98 url: server.url,
99 npmName: 'test-external-auth-one',
100 npmVersion: '0.0.1',
101 authName: 'external-auth-1',
102 query: {
103 username: 'cyan'
104 },
105 statusCodeExpected: 302
106 })
107
108 const location = res.header.location
109 expect(location.startsWith('/login?')).to.be.true
110
111 const searchParams = decodeQueryString(location)
112
113 expect(searchParams.externalAuthToken).to.exist
114 expect(searchParams.username).to.equal('cyan')
115
116 externalAuthToken = searchParams.externalAuthToken as string
117 })
118
119 it('Should reject auto external login with a missing or invalid token', async function () {
120 await loginUsingExternalToken(server, 'cyan', '', 400)
121 await loginUsingExternalToken(server, 'cyan', 'blabla', 400)
122 })
123
124 it('Should reject auto external login with a missing or invalid username', async function () {
125 await loginUsingExternalToken(server, '', externalAuthToken, 400)
126 await loginUsingExternalToken(server, '', externalAuthToken, 400)
127 })
128
129 it('Should reject auto external login with an expired token', async function () {
130 this.timeout(15000)
131
132 await wait(5000)
133
134 await loginUsingExternalToken(server, 'cyan', externalAuthToken, 400)
135
136 await waitUntilLog(server, 'expired external auth token')
137 })
138
139 it('Should auto login Cyan, create the user and use the token', async function () {
140 {
141 const res = await loginExternal({
142 server,
143 npmName: 'test-external-auth-one',
144 authName: 'external-auth-1',
145 query: {
146 username: 'cyan'
147 },
148 username: 'cyan'
149 })
150
151 cyanAccessToken = res.access_token
152 cyanRefreshToken = res.refresh_token
153 }
154
155 {
156 const res = await getMyUserInformation(server.url, cyanAccessToken)
157
158 const body: User = res.body
159 expect(body.username).to.equal('cyan')
160 expect(body.account.displayName).to.equal('cyan')
161 expect(body.email).to.equal('cyan@example.com')
162 expect(body.role).to.equal(UserRole.USER)
163 }
164 })
165
166 it('Should auto login Kefka, create the user and use the token', async function () {
167 {
168 const res = await loginExternal({
169 server,
170 npmName: 'test-external-auth-one',
171 authName: 'external-auth-2',
172 username: 'kefka'
173 })
174
175 kefkaAccessToken = res.access_token
176 kefkaRefreshToken = res.refresh_token
177 }
178
179 {
180 const res = await getMyUserInformation(server.url, kefkaAccessToken)
181
182 const body: User = res.body
183 expect(body.username).to.equal('kefka')
184 expect(body.account.displayName).to.equal('Kefka Palazzo')
185 expect(body.email).to.equal('kefka@example.com')
186 expect(body.role).to.equal(UserRole.ADMINISTRATOR)
187 }
188 })
189
190 it('Should refresh Cyan token, but not Kefka token', async function () {
191 {
192 const resRefresh = await refreshToken(server, cyanRefreshToken)
193 cyanAccessToken = resRefresh.body.access_token
194 cyanRefreshToken = resRefresh.body.refresh_token
195
196 const res = await getMyUserInformation(server.url, cyanAccessToken)
197 const user: User = res.body
198 expect(user.username).to.equal('cyan')
199 }
200
201 {
202 await refreshToken(server, kefkaRefreshToken, 400)
203 }
204 })
205
206 it('Should update Cyan profile', async function () {
207 await updateMyUser({
208 url: server.url,
209 accessToken: cyanAccessToken,
210 displayName: 'Cyan Garamonde',
211 description: 'Retainer to the king of Doma'
212 })
213
214 const res = await getMyUserInformation(server.url, cyanAccessToken)
215
216 const body: User = res.body
217 expect(body.account.displayName).to.equal('Cyan Garamonde')
218 expect(body.account.description).to.equal('Retainer to the king of Doma')
219 })
220
221 it('Should logout Cyan', async function () {
222 await logout(server.url, cyanAccessToken)
223 })
224
225 it('Should have logged out Cyan', async function () {
226 await waitUntilLog(server, 'On logout cyan')
227
228 await getMyUserInformation(server.url, cyanAccessToken, 401)
229 })
230
231 it('Should login Cyan and keep the old existing profile', async function () {
232 {
233 const res = await loginExternal({
234 server,
235 npmName: 'test-external-auth-one',
236 authName: 'external-auth-1',
237 query: {
238 username: 'cyan'
239 },
240 username: 'cyan'
241 })
242
243 cyanAccessToken = res.access_token
244 }
245
246 const res = await getMyUserInformation(server.url, cyanAccessToken)
247
248 const body: User = res.body
249 expect(body.username).to.equal('cyan')
250 expect(body.account.displayName).to.equal('Cyan Garamonde')
251 expect(body.account.description).to.equal('Retainer to the king of Doma')
252 expect(body.role).to.equal(UserRole.USER)
253 })
254
255 it('Should reject token of Kefka by the plugin hook', async function () {
256 this.timeout(10000)
257
258 await wait(5000)
259
260 await getMyUserInformation(server.url, kefkaAccessToken, 401)
261 })
262
263 it('Should unregister external-auth-2 and do not login existing Kefka', async function () {
264 await updatePluginSettings({
265 url: server.url,
266 accessToken: server.accessToken,
267 npmName: 'peertube-plugin-test-external-auth-one',
268 settings: { disableKefka: true }
269 })
270
271 await userLogin(server, { username: 'kefka', password: 'fake' }, 400)
272
273 await loginExternal({
274 server,
275 npmName: 'test-external-auth-one',
276 authName: 'external-auth-2',
277 query: {
278 username: 'kefka'
279 },
280 username: 'kefka',
281 statusCodeExpected: 404
282 })
283 })
284
285 it('Should have disabled this auth', async function () {
286 const res = await getConfig(server.url)
287
288 const config: ServerConfig = res.body
289
290 const auths = config.plugin.registeredExternalAuths
291 expect(auths).to.have.lengthOf(2)
292
293 const auth1 = auths.find(a => a.authName === 'external-auth-2')
294 expect(auth1).to.not.exist
295 })
296
297 it('Should uninstall the plugin one and do not login Cyan', async function () {
298 await uninstallPlugin({
299 url: server.url,
300 accessToken: server.accessToken,
301 npmName: 'peertube-plugin-test-external-auth-one'
302 })
303
304 await loginExternal({
305 server,
306 npmName: 'test-external-auth-one',
307 authName: 'external-auth-1',
308 query: {
309 username: 'cyan'
310 },
311 username: 'cyan',
312 statusCodeExpected: 404
313 })
314 })
315
316 it('Should display the correct configuration', async function () {
317 const res = await getConfig(server.url)
318
319 const config: ServerConfig = res.body
320
321 const auths = config.plugin.registeredExternalAuths
322 expect(auths).to.have.lengthOf(1)
323
324 const auth2 = auths.find((a) => a.authName === 'external-auth-2')
325 expect(auth2).to.not.exist
326 })
327
328 after(async function () {
329 await cleanupTests([ server ])
330 })
331 })