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