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