1 /* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
4 import { expect } from 'chai'
5 import { ServerConfig, User, UserRole } from '@shared/models'
13 loginUsingExternalToken,
16 setAccessTokensToServers,
23 } from '../../../shared/extra-utils'
24 import { cleanupTests, flushAndRunServer, ServerInfo, waitUntilLog } from '../../../shared/extra-utils/server/servers'
26 async function loginExternal (options: {
32 statusCodeExpected?: number
33 statusCodeExpectedStep2?: number
35 const res = await getExternalAuth({
36 url: options.server.url,
37 npmName: options.npmName,
39 authName: options.authName,
41 statusCodeExpected: options.statusCodeExpected || 302
44 if (res.status !== 302) return
46 const location = res.header.location
47 const { externalAuthToken } = decodeQueryString(location)
49 const resLogin = await loginUsingExternalToken(
52 externalAuthToken as string,
53 options.statusCodeExpectedStep2
59 describe('Test external auth plugins', function () {
60 let server: ServerInfo
62 let cyanAccessToken: string
63 let cyanRefreshToken: string
65 let kefkaAccessToken: string
66 let kefkaRefreshToken: string
68 let externalAuthToken: string
70 before(async function () {
73 server = await flushAndRunServer(1)
74 await setAccessTokensToServers([ server ])
76 for (const suffix of [ 'one', 'two' ]) {
79 accessToken: server.accessToken,
80 path: getPluginTestPath('-external-auth-' + suffix)
85 it('Should display the correct configuration', async function () {
86 const res = await getConfig(server.url)
88 const config: ServerConfig = res.body
90 const auths = config.plugin.registeredExternalAuths
91 expect(auths).to.have.lengthOf(6)
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')
99 it('Should redirect for a Cyan login', async function () {
100 const res = await getExternalAuth({
102 npmName: 'test-external-auth-one',
104 authName: 'external-auth-1',
108 statusCodeExpected: 302
111 const location = res.header.location
112 expect(location.startsWith('/login?')).to.be.true
114 const searchParams = decodeQueryString(location)
116 expect(searchParams.externalAuthToken).to.exist
117 expect(searchParams.username).to.equal('cyan')
119 externalAuthToken = searchParams.externalAuthToken as string
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)
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)
132 it('Should reject auto external login with an expired token', async function () {
137 await loginUsingExternalToken(server, 'cyan', externalAuthToken, 400)
139 await waitUntilLog(server, 'expired external auth token')
142 it('Should auto login Cyan, create the user and use the token', async function () {
144 const res = await loginExternal({
146 npmName: 'test-external-auth-one',
147 authName: 'external-auth-1',
154 cyanAccessToken = res.access_token
155 cyanRefreshToken = res.refresh_token
159 const res = await getMyUserInformation(server.url, cyanAccessToken)
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)
169 it('Should auto login Kefka, create the user and use the token', async function () {
171 const res = await loginExternal({
173 npmName: 'test-external-auth-one',
174 authName: 'external-auth-2',
178 kefkaAccessToken = res.access_token
179 kefkaRefreshToken = res.refresh_token
183 const res = await getMyUserInformation(server.url, kefkaAccessToken)
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)
193 it('Should refresh Cyan token, but not Kefka token', async function () {
195 const resRefresh = await refreshToken(server, cyanRefreshToken)
196 cyanAccessToken = resRefresh.body.access_token
197 cyanRefreshToken = resRefresh.body.refresh_token
199 const res = await getMyUserInformation(server.url, cyanAccessToken)
200 const user: User = res.body
201 expect(user.username).to.equal('cyan')
205 await refreshToken(server, kefkaRefreshToken, 400)
209 it('Should update Cyan profile', async function () {
212 accessToken: cyanAccessToken,
213 displayName: 'Cyan Garamonde',
214 description: 'Retainer to the king of Doma'
217 const res = await getMyUserInformation(server.url, cyanAccessToken)
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')
224 it('Should logout Cyan', async function () {
225 await logout(server.url, cyanAccessToken)
228 it('Should have logged out Cyan', async function () {
229 await waitUntilLog(server, 'On logout cyan')
231 await getMyUserInformation(server.url, cyanAccessToken, 401)
234 it('Should login Cyan and keep the old existing profile', async function () {
236 const res = await loginExternal({
238 npmName: 'test-external-auth-one',
239 authName: 'external-auth-1',
246 cyanAccessToken = res.access_token
249 const res = await getMyUserInformation(server.url, cyanAccessToken)
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)
258 it('Should reject token of Kefka by the plugin hook', async function () {
263 await getMyUserInformation(server.url, kefkaAccessToken, 401)
266 it('Should unregister external-auth-2 and do not login existing Kefka', async function () {
267 await updatePluginSettings({
269 accessToken: server.accessToken,
270 npmName: 'peertube-plugin-test-external-auth-one',
271 settings: { disableKefka: true }
274 await userLogin(server, { username: 'kefka', password: 'fake' }, 400)
276 await loginExternal({
278 npmName: 'test-external-auth-one',
279 authName: 'external-auth-2',
284 statusCodeExpected: 404
288 it('Should have disabled this auth', async function () {
289 const res = await getConfig(server.url)
291 const config: ServerConfig = res.body
293 const auths = config.plugin.registeredExternalAuths
294 expect(auths).to.have.lengthOf(5)
296 const auth1 = auths.find(a => a.authName === 'external-auth-2')
297 expect(auth1).to.not.exist
300 it('Should uninstall the plugin one and do not login Cyan', async function () {
301 await uninstallPlugin({
303 accessToken: server.accessToken,
304 npmName: 'peertube-plugin-test-external-auth-one'
307 await loginExternal({
309 npmName: 'test-external-auth-one',
310 authName: 'external-auth-1',
315 statusCodeExpected: 404
318 await userLogin(server, { username: 'cyan', password: null }, 400)
319 await userLogin(server, { username: 'cyan', password: '' }, 400)
320 await userLogin(server, { username: 'cyan', password: 'fake' }, 400)
323 it('Should not login kefka with another plugin', async function () {
324 await loginExternal({
326 npmName: 'test-external-auth-two',
327 authName: 'external-auth-4',
329 statusCodeExpectedStep2: 400
332 await loginExternal({
334 npmName: 'test-external-auth-two',
335 authName: 'external-auth-4',
337 statusCodeExpectedStep2: 400
341 it('Should not login an existing user', async function () {
344 accessToken: server.accessToken,
345 username: 'existing_user',
346 password: 'super_password'
349 await loginExternal({
351 npmName: 'test-external-auth-two',
352 authName: 'external-auth-6',
353 username: 'existing_user',
354 statusCodeExpectedStep2: 400
358 it('Should display the correct configuration', async function () {
359 const res = await getConfig(server.url)
361 const config: ServerConfig = res.body
363 const auths = config.plugin.registeredExternalAuths
364 expect(auths).to.have.lengthOf(4)
366 const auth2 = auths.find((a) => a.authName === 'external-auth-2')
367 expect(auth2).to.not.exist
370 after(async function () {
371 await cleanupTests([ server ])