diff options
author | Chocobozzz <chocobozzz@framasoft.org> | 2020-11-20 15:36:43 +0100 |
---|---|---|
committer | Chocobozzz <chocobozzz@framasoft.org> | 2020-11-20 15:36:43 +0100 |
commit | 74fd2643b43057c25558b3da79398efe104e2660 (patch) | |
tree | 4fd7dd84775780eed82bc4b3caaa328c3526a14c | |
parent | 8f3ad70874f8769f5340632754dc2ca7f4c82733 (diff) | |
download | PeerTube-74fd2643b43057c25558b3da79398efe104e2660.tar.gz PeerTube-74fd2643b43057c25558b3da79398efe104e2660.tar.zst PeerTube-74fd2643b43057c25558b3da79398efe104e2660.zip |
Provide express request to onLogout call
+ pluginInfo related changes
-rw-r--r-- | client/src/app/core/auth/auth.service.ts | 8 | ||||
-rw-r--r-- | server/lib/auth.ts | 4 | ||||
-rw-r--r-- | server/lib/oauth-model.ts | 10 | ||||
-rw-r--r-- | server/lib/plugins/plugin-manager.ts | 12 | ||||
-rw-r--r-- | server/tests/fixtures/peertube-plugin-test-external-auth-three/main.js | 53 | ||||
-rw-r--r-- | server/tests/fixtures/peertube-plugin-test-external-auth-three/package.json | 20 | ||||
-rw-r--r-- | server/tests/plugins/external-auth.ts | 34 | ||||
-rw-r--r-- | server/types/plugins/register-server-auth.model.ts | 3 |
8 files changed, 129 insertions, 15 deletions
diff --git a/client/src/app/core/auth/auth.service.ts b/client/src/app/core/auth/auth.service.ts index 3410c5a6f..fd6062d3f 100644 --- a/client/src/app/core/auth/auth.service.ts +++ b/client/src/app/core/auth/auth.service.ts | |||
@@ -167,9 +167,13 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular | |||
167 | const authHeaderValue = this.getRequestHeaderValue() | 167 | const authHeaderValue = this.getRequestHeaderValue() |
168 | const headers = new HttpHeaders().set('Authorization', authHeaderValue) | 168 | const headers = new HttpHeaders().set('Authorization', authHeaderValue) |
169 | 169 | ||
170 | this.http.post<void>(AuthService.BASE_REVOKE_TOKEN_URL, {}, { headers }) | 170 | this.http.post<{ redirectUrl?: string }>(AuthService.BASE_REVOKE_TOKEN_URL, {}, { headers }) |
171 | .subscribe( | 171 | .subscribe( |
172 | () => { /* nothing to do */ }, | 172 | res => { |
173 | if (res.redirectUrl) { | ||
174 | window.location.href = res.redirectUrl | ||
175 | } | ||
176 | }, | ||
173 | 177 | ||
174 | err => console.error(err) | 178 | err => console.error(err) |
175 | ) | 179 | ) |
diff --git a/server/lib/auth.ts b/server/lib/auth.ts index 3f8e18633..acf0da18a 100644 --- a/server/lib/auth.ts +++ b/server/lib/auth.ts | |||
@@ -52,7 +52,7 @@ async function handleTokenRevocation (req: express.Request, res: express.Respons | |||
52 | const token = res.locals.oauth.token | 52 | const token = res.locals.oauth.token |
53 | 53 | ||
54 | res.locals.explicitLogout = true | 54 | res.locals.explicitLogout = true |
55 | await revokeToken(token) | 55 | const result = await revokeToken(token) |
56 | 56 | ||
57 | // FIXME: uncomment when https://github.com/oauthjs/node-oauth2-server/pull/289 is released | 57 | // FIXME: uncomment when https://github.com/oauthjs/node-oauth2-server/pull/289 is released |
58 | // oAuthServer.revoke(req, res, err => { | 58 | // oAuthServer.revoke(req, res, err => { |
@@ -68,7 +68,7 @@ async function handleTokenRevocation (req: express.Request, res: express.Respons | |||
68 | // } | 68 | // } |
69 | // }) | 69 | // }) |
70 | 70 | ||
71 | return res.json() | 71 | return res.json(result) |
72 | } | 72 | } |
73 | 73 | ||
74 | async function onExternalUserAuthenticated (options: { | 74 | async function onExternalUserAuthenticated (options: { |
diff --git a/server/lib/oauth-model.ts b/server/lib/oauth-model.ts index 3273c6c2d..f7ea98b41 100644 --- a/server/lib/oauth-model.ts +++ b/server/lib/oauth-model.ts | |||
@@ -141,13 +141,15 @@ async function getUser (usernameOrEmail?: string, password?: string) { | |||
141 | return user | 141 | return user |
142 | } | 142 | } |
143 | 143 | ||
144 | async function revokeToken (tokenInfo: { refreshToken: string }) { | 144 | async function revokeToken (tokenInfo: { refreshToken: string }): Promise<{ success: boolean, redirectUrl?: string }> { |
145 | const res: express.Response = this.request.res | 145 | const res: express.Response = this.request.res |
146 | const token = await OAuthTokenModel.getByRefreshTokenAndPopulateUser(tokenInfo.refreshToken) | 146 | const token = await OAuthTokenModel.getByRefreshTokenAndPopulateUser(tokenInfo.refreshToken) |
147 | 147 | ||
148 | if (token) { | 148 | if (token) { |
149 | let redirectUrl: string | ||
150 | |||
149 | if (res.locals.explicitLogout === true && token.User.pluginAuth && token.authName) { | 151 | if (res.locals.explicitLogout === true && token.User.pluginAuth && token.authName) { |
150 | PluginManager.Instance.onLogout(token.User.pluginAuth, token.authName, token.User) | 152 | redirectUrl = await PluginManager.Instance.onLogout(token.User.pluginAuth, token.authName, token.User, this.request) |
151 | } | 153 | } |
152 | 154 | ||
153 | clearCacheByToken(token.accessToken) | 155 | clearCacheByToken(token.accessToken) |
@@ -155,10 +157,10 @@ async function revokeToken (tokenInfo: { refreshToken: string }) { | |||
155 | token.destroy() | 157 | token.destroy() |
156 | .catch(err => logger.error('Cannot destroy token when revoking token.', { err })) | 158 | .catch(err => logger.error('Cannot destroy token when revoking token.', { err })) |
157 | 159 | ||
158 | return true | 160 | return { success: true, redirectUrl } |
159 | } | 161 | } |
160 | 162 | ||
161 | return false | 163 | return { success: false } |
162 | } | 164 | } |
163 | 165 | ||
164 | async function saveToken (token: TokenInfo, client: OAuthClientModel, user: UserModel) { | 166 | async function saveToken (token: TokenInfo, client: OAuthClientModel, user: UserModel) { |
diff --git a/server/lib/plugins/plugin-manager.ts b/server/lib/plugins/plugin-manager.ts index 94b5ecc41..8e7491257 100644 --- a/server/lib/plugins/plugin-manager.ts +++ b/server/lib/plugins/plugin-manager.ts | |||
@@ -1,3 +1,4 @@ | |||
1 | import * as express from 'express' | ||
1 | import { createReadStream, createWriteStream } from 'fs' | 2 | import { createReadStream, createWriteStream } from 'fs' |
2 | import { outputFile, readJSON } from 'fs-extra' | 3 | import { outputFile, readJSON } from 'fs-extra' |
3 | import { basename, join } from 'path' | 4 | import { basename, join } from 'path' |
@@ -166,18 +167,25 @@ export class PluginManager implements ServerHook { | |||
166 | 167 | ||
167 | // ###################### External events ###################### | 168 | // ###################### External events ###################### |
168 | 169 | ||
169 | onLogout (npmName: string, authName: string, user: MUser) { | 170 | async onLogout (npmName: string, authName: string, user: MUser, req: express.Request) { |
170 | const auth = this.getAuth(npmName, authName) | 171 | const auth = this.getAuth(npmName, authName) |
171 | 172 | ||
172 | if (auth?.onLogout) { | 173 | if (auth?.onLogout) { |
173 | logger.info('Running onLogout function from auth %s of plugin %s', authName, npmName) | 174 | logger.info('Running onLogout function from auth %s of plugin %s', authName, npmName) |
174 | 175 | ||
175 | try { | 176 | try { |
176 | auth.onLogout(user) | 177 | // Force await, in case or onLogout returns a promise |
178 | const result = await auth.onLogout(user, req) | ||
179 | |||
180 | return typeof result === 'string' | ||
181 | ? result | ||
182 | : undefined | ||
177 | } catch (err) { | 183 | } catch (err) { |
178 | logger.warn('Cannot run onLogout function from auth %s of plugin %s.', authName, npmName, { err }) | 184 | logger.warn('Cannot run onLogout function from auth %s of plugin %s.', authName, npmName, { err }) |
179 | } | 185 | } |
180 | } | 186 | } |
187 | |||
188 | return undefined | ||
181 | } | 189 | } |
182 | 190 | ||
183 | onSettingsChanged (name: string, settings: any) { | 191 | onSettingsChanged (name: string, settings: any) { |
diff --git a/server/tests/fixtures/peertube-plugin-test-external-auth-three/main.js b/server/tests/fixtures/peertube-plugin-test-external-auth-three/main.js new file mode 100644 index 000000000..30cedccc6 --- /dev/null +++ b/server/tests/fixtures/peertube-plugin-test-external-auth-three/main.js | |||
@@ -0,0 +1,53 @@ | |||
1 | async function register ({ | ||
2 | registerExternalAuth, | ||
3 | peertubeHelpers | ||
4 | }) { | ||
5 | { | ||
6 | const result = registerExternalAuth({ | ||
7 | authName: 'external-auth-7', | ||
8 | authDisplayName: () => 'External Auth 7', | ||
9 | onAuthRequest: (req, res) => { | ||
10 | result.userAuthenticated({ | ||
11 | req, | ||
12 | res, | ||
13 | username: 'cid', | ||
14 | email: 'cid@example.com', | ||
15 | displayName: 'Cid Marquez' | ||
16 | }) | ||
17 | }, | ||
18 | onLogout: (user, req) => { | ||
19 | return 'https://example.com/redirectUrl' | ||
20 | } | ||
21 | }) | ||
22 | } | ||
23 | |||
24 | { | ||
25 | const result = registerExternalAuth({ | ||
26 | authName: 'external-auth-8', | ||
27 | authDisplayName: () => 'External Auth 8', | ||
28 | onAuthRequest: (req, res) => { | ||
29 | result.userAuthenticated({ | ||
30 | req, | ||
31 | res, | ||
32 | username: 'cid', | ||
33 | email: 'cid@example.com', | ||
34 | displayName: 'Cid Marquez' | ||
35 | }) | ||
36 | }, | ||
37 | onLogout: (user, req) => { | ||
38 | return 'https://example.com/redirectUrl?access_token=' + req.headers['authorization'].split(' ')[1] | ||
39 | } | ||
40 | }) | ||
41 | } | ||
42 | } | ||
43 | |||
44 | async function unregister () { | ||
45 | |||
46 | } | ||
47 | |||
48 | module.exports = { | ||
49 | register, | ||
50 | unregister | ||
51 | } | ||
52 | |||
53 | // ########################################################################### | ||
diff --git a/server/tests/fixtures/peertube-plugin-test-external-auth-three/package.json b/server/tests/fixtures/peertube-plugin-test-external-auth-three/package.json new file mode 100644 index 000000000..f323d189d --- /dev/null +++ b/server/tests/fixtures/peertube-plugin-test-external-auth-three/package.json | |||
@@ -0,0 +1,20 @@ | |||
1 | { | ||
2 | "name": "peertube-plugin-test-external-auth-three", | ||
3 | "version": "0.0.1", | ||
4 | "description": "External auth three", | ||
5 | "engine": { | ||
6 | "peertube": ">=1.3.0" | ||
7 | }, | ||
8 | "keywords": [ | ||
9 | "peertube", | ||
10 | "plugin" | ||
11 | ], | ||
12 | "homepage": "https://github.com/Chocobozzz/PeerTube", | ||
13 | "author": "Chocobozzz", | ||
14 | "bugs": "https://github.com/Chocobozzz/PeerTube/issues", | ||
15 | "library": "./main.js", | ||
16 | "staticDirs": {}, | ||
17 | "css": [], | ||
18 | "clientScripts": [], | ||
19 | "translations": {} | ||
20 | } | ||
diff --git a/server/tests/plugins/external-auth.ts b/server/tests/plugins/external-auth.ts index 57361be05..6d907cc51 100644 --- a/server/tests/plugins/external-auth.ts +++ b/server/tests/plugins/external-auth.ts | |||
@@ -73,7 +73,7 @@ describe('Test external auth plugins', function () { | |||
73 | server = await flushAndRunServer(1) | 73 | server = await flushAndRunServer(1) |
74 | await setAccessTokensToServers([ server ]) | 74 | await setAccessTokensToServers([ server ]) |
75 | 75 | ||
76 | for (const suffix of [ 'one', 'two' ]) { | 76 | for (const suffix of [ 'one', 'two', 'three' ]) { |
77 | await installPlugin({ | 77 | await installPlugin({ |
78 | url: server.url, | 78 | url: server.url, |
79 | accessToken: server.accessToken, | 79 | accessToken: server.accessToken, |
@@ -88,7 +88,7 @@ describe('Test external auth plugins', function () { | |||
88 | const config: ServerConfig = res.body | 88 | const config: ServerConfig = res.body |
89 | 89 | ||
90 | const auths = config.plugin.registeredExternalAuths | 90 | const auths = config.plugin.registeredExternalAuths |
91 | expect(auths).to.have.lengthOf(6) | 91 | expect(auths).to.have.lengthOf(8) |
92 | 92 | ||
93 | const auth2 = auths.find((a) => a.authName === 'external-auth-2') | 93 | const auth2 = auths.find((a) => a.authName === 'external-auth-2') |
94 | expect(auth2).to.exist | 94 | expect(auth2).to.exist |
@@ -301,7 +301,7 @@ describe('Test external auth plugins', function () { | |||
301 | const config: ServerConfig = res.body | 301 | const config: ServerConfig = res.body |
302 | 302 | ||
303 | const auths = config.plugin.registeredExternalAuths | 303 | const auths = config.plugin.registeredExternalAuths |
304 | expect(auths).to.have.lengthOf(5) | 304 | expect(auths).to.have.lengthOf(7) |
305 | 305 | ||
306 | const auth1 = auths.find(a => a.authName === 'external-auth-2') | 306 | const auth1 = auths.find(a => a.authName === 'external-auth-2') |
307 | expect(auth1).to.not.exist | 307 | expect(auth1).to.not.exist |
@@ -371,7 +371,7 @@ describe('Test external auth plugins', function () { | |||
371 | const config: ServerConfig = res.body | 371 | const config: ServerConfig = res.body |
372 | 372 | ||
373 | const auths = config.plugin.registeredExternalAuths | 373 | const auths = config.plugin.registeredExternalAuths |
374 | expect(auths).to.have.lengthOf(4) | 374 | expect(auths).to.have.lengthOf(6) |
375 | 375 | ||
376 | const auth2 = auths.find((a) => a.authName === 'external-auth-2') | 376 | const auth2 = auths.find((a) => a.authName === 'external-auth-2') |
377 | expect(auth2).to.not.exist | 377 | expect(auth2).to.not.exist |
@@ -380,4 +380,30 @@ describe('Test external auth plugins', function () { | |||
380 | after(async function () { | 380 | after(async function () { |
381 | await cleanupTests([ server ]) | 381 | await cleanupTests([ server ]) |
382 | }) | 382 | }) |
383 | |||
384 | it('Should forward the redirectUrl if the plugin returns one', async function () { | ||
385 | const resLogin = await loginExternal({ | ||
386 | server, | ||
387 | npmName: 'test-external-auth-three', | ||
388 | authName: 'external-auth-7', | ||
389 | username: 'cid' | ||
390 | }) | ||
391 | |||
392 | const resLogout = await logout(server.url, resLogin.access_token) | ||
393 | |||
394 | expect(resLogout.body.redirectUrl).to.equal('https://example.com/redirectUrl') | ||
395 | }) | ||
396 | |||
397 | it('Should call the plugin\'s onLogout method with the request', async function () { | ||
398 | const resLogin = await loginExternal({ | ||
399 | server, | ||
400 | npmName: 'test-external-auth-three', | ||
401 | authName: 'external-auth-8', | ||
402 | username: 'cid' | ||
403 | }) | ||
404 | |||
405 | const resLogout = await logout(server.url, resLogin.access_token) | ||
406 | |||
407 | expect(resLogout.body.redirectUrl).to.equal('https://example.com/redirectUrl?access_token=' + resLogin.access_token) | ||
408 | }) | ||
383 | }) | 409 | }) |
diff --git a/server/types/plugins/register-server-auth.model.ts b/server/types/plugins/register-server-auth.model.ts index 31c71b0d0..3e1a5aeba 100644 --- a/server/types/plugins/register-server-auth.model.ts +++ b/server/types/plugins/register-server-auth.model.ts | |||
@@ -21,7 +21,8 @@ interface RegisterServerAuthBase { | |||
21 | authName: string | 21 | authName: string |
22 | 22 | ||
23 | // Called by PeerTube when a user from your plugin logged out | 23 | // Called by PeerTube when a user from your plugin logged out |
24 | onLogout?(user: MUser): void | 24 | // Returns a redirectUrl sent to the client or nothing |
25 | onLogout?(user: MUser, req: express.Request): Promise<string> | ||
25 | 26 | ||
26 | // Your plugin can hook PeerTube access/refresh token validity | 27 | // Your plugin can hook PeerTube access/refresh token validity |
27 | // So you can control for your plugin the user session lifetime | 28 | // So you can control for your plugin the user session lifetime |