diff options
Diffstat (limited to 'server')
-rw-r--r-- | server/controllers/api/config.ts | 56 | ||||
-rw-r--r-- | server/controllers/plugins.ts | 20 | ||||
-rw-r--r-- | server/lib/auth.ts | 225 | ||||
-rw-r--r-- | server/lib/oauth-model.ts | 2 | ||||
-rw-r--r-- | server/lib/plugins/register-helpers-store.ts | 42 | ||||
-rw-r--r-- | server/middlewares/validators/plugins.ts | 25 | ||||
-rw-r--r-- | server/typings/express.ts | 3 |
7 files changed, 269 insertions, 104 deletions
diff --git a/server/controllers/api/config.ts b/server/controllers/api/config.ts index 06fe30371..e8941bc73 100644 --- a/server/controllers/api/config.ts +++ b/server/controllers/api/config.ts | |||
@@ -1,22 +1,22 @@ | |||
1 | import { Hooks } from '@server/lib/plugins/hooks' | ||
1 | import * as express from 'express' | 2 | import * as express from 'express' |
3 | import { remove, writeJSON } from 'fs-extra' | ||
2 | import { snakeCase } from 'lodash' | 4 | import { snakeCase } from 'lodash' |
3 | import { ServerConfig, UserRight } from '../../../shared' | 5 | import validator from 'validator' |
6 | import { RegisteredExternalAuthConfig, RegisteredIdAndPassAuthConfig, ServerConfig, UserRight } from '../../../shared' | ||
4 | import { About } from '../../../shared/models/server/about.model' | 7 | import { About } from '../../../shared/models/server/about.model' |
5 | import { CustomConfig } from '../../../shared/models/server/custom-config.model' | 8 | import { CustomConfig } from '../../../shared/models/server/custom-config.model' |
6 | import { isSignupAllowed, isSignupAllowedForCurrentIP } from '../../helpers/signup' | ||
7 | import { CONSTRAINTS_FIELDS, DEFAULT_THEME_NAME, PEERTUBE_VERSION } from '../../initializers/constants' | ||
8 | import { asyncMiddleware, authenticate, ensureUserHasRight } from '../../middlewares' | ||
9 | import { customConfigUpdateValidator } from '../../middlewares/validators/config' | ||
10 | import { ClientHtml } from '../../lib/client-html' | ||
11 | import { auditLoggerFactory, CustomConfigAuditView, getAuditIdFromRes } from '../../helpers/audit-logger' | 9 | import { auditLoggerFactory, CustomConfigAuditView, getAuditIdFromRes } from '../../helpers/audit-logger' |
12 | import { remove, writeJSON } from 'fs-extra' | ||
13 | import { getServerCommit } from '../../helpers/utils' | ||
14 | import validator from 'validator' | ||
15 | import { objectConverter } from '../../helpers/core-utils' | 10 | import { objectConverter } from '../../helpers/core-utils' |
11 | import { isSignupAllowed, isSignupAllowedForCurrentIP } from '../../helpers/signup' | ||
12 | import { getServerCommit } from '../../helpers/utils' | ||
16 | import { CONFIG, isEmailEnabled, reloadConfig } from '../../initializers/config' | 13 | import { CONFIG, isEmailEnabled, reloadConfig } from '../../initializers/config' |
14 | import { CONSTRAINTS_FIELDS, DEFAULT_THEME_NAME, PEERTUBE_VERSION } from '../../initializers/constants' | ||
15 | import { ClientHtml } from '../../lib/client-html' | ||
17 | import { PluginManager } from '../../lib/plugins/plugin-manager' | 16 | import { PluginManager } from '../../lib/plugins/plugin-manager' |
18 | import { getThemeOrDefault } from '../../lib/plugins/theme-utils' | 17 | import { getThemeOrDefault } from '../../lib/plugins/theme-utils' |
19 | import { Hooks } from '@server/lib/plugins/hooks' | 18 | import { asyncMiddleware, authenticate, ensureUserHasRight } from '../../middlewares' |
19 | import { customConfigUpdateValidator } from '../../middlewares/validators/config' | ||
20 | 20 | ||
21 | const configRouter = express.Router() | 21 | const configRouter = express.Router() |
22 | 22 | ||
@@ -79,7 +79,9 @@ async function getConfig (req: express.Request, res: express.Response) { | |||
79 | } | 79 | } |
80 | }, | 80 | }, |
81 | plugin: { | 81 | plugin: { |
82 | registered: getRegisteredPlugins() | 82 | registered: getRegisteredPlugins(), |
83 | registeredExternalAuths: getExternalAuthsPlugins(), | ||
84 | registeredIdAndPassAuths: getIdAndPassAuthPlugins() | ||
83 | }, | 85 | }, |
84 | theme: { | 86 | theme: { |
85 | registered: getRegisteredThemes(), | 87 | registered: getRegisteredThemes(), |
@@ -269,6 +271,38 @@ function getRegisteredPlugins () { | |||
269 | })) | 271 | })) |
270 | } | 272 | } |
271 | 273 | ||
274 | function getIdAndPassAuthPlugins () { | ||
275 | const result: RegisteredIdAndPassAuthConfig[] = [] | ||
276 | |||
277 | for (const p of PluginManager.Instance.getIdAndPassAuths()) { | ||
278 | for (const auth of p.idAndPassAuths) { | ||
279 | result.push({ | ||
280 | npmName: p.npmName, | ||
281 | authName: auth.authName, | ||
282 | weight: auth.getWeight() | ||
283 | }) | ||
284 | } | ||
285 | } | ||
286 | |||
287 | return result | ||
288 | } | ||
289 | |||
290 | function getExternalAuthsPlugins () { | ||
291 | const result: RegisteredExternalAuthConfig[] = [] | ||
292 | |||
293 | for (const p of PluginManager.Instance.getExternalAuths()) { | ||
294 | for (const auth of p.externalAuths) { | ||
295 | result.push({ | ||
296 | npmName: p.npmName, | ||
297 | authName: auth.authName, | ||
298 | authDisplayName: auth.authDisplayName | ||
299 | }) | ||
300 | } | ||
301 | } | ||
302 | |||
303 | return result | ||
304 | } | ||
305 | |||
272 | // --------------------------------------------------------------------------- | 306 | // --------------------------------------------------------------------------- |
273 | 307 | ||
274 | export { | 308 | export { |
diff --git a/server/controllers/plugins.ts b/server/controllers/plugins.ts index 1fc49b646..f12e1c0f5 100644 --- a/server/controllers/plugins.ts +++ b/server/controllers/plugins.ts | |||
@@ -2,11 +2,12 @@ import * as express from 'express' | |||
2 | import { PLUGIN_GLOBAL_CSS_PATH } from '../initializers/constants' | 2 | import { PLUGIN_GLOBAL_CSS_PATH } from '../initializers/constants' |
3 | import { join } from 'path' | 3 | import { join } from 'path' |
4 | import { PluginManager, RegisteredPlugin } from '../lib/plugins/plugin-manager' | 4 | import { PluginManager, RegisteredPlugin } from '../lib/plugins/plugin-manager' |
5 | import { getPluginValidator, pluginStaticDirectoryValidator } from '../middlewares/validators/plugins' | 5 | import { getPluginValidator, pluginStaticDirectoryValidator, getExternalAuthValidator } from '../middlewares/validators/plugins' |
6 | import { serveThemeCSSValidator } from '../middlewares/validators/themes' | 6 | import { serveThemeCSSValidator } from '../middlewares/validators/themes' |
7 | import { PluginType } from '../../shared/models/plugins/plugin.type' | 7 | import { PluginType } from '../../shared/models/plugins/plugin.type' |
8 | import { isTestInstance } from '../helpers/core-utils' | 8 | import { isTestInstance } from '../helpers/core-utils' |
9 | import { getCompleteLocale, is18nLocale } from '../../shared/models/i18n' | 9 | import { getCompleteLocale, is18nLocale } from '../../shared/models/i18n' |
10 | import { logger } from '@server/helpers/logger' | ||
10 | 11 | ||
11 | const sendFileOptions = { | 12 | const sendFileOptions = { |
12 | maxAge: '30 days', | 13 | maxAge: '30 days', |
@@ -23,6 +24,12 @@ pluginsRouter.get('/plugins/translations/:locale.json', | |||
23 | getPluginTranslations | 24 | getPluginTranslations |
24 | ) | 25 | ) |
25 | 26 | ||
27 | pluginsRouter.get('/plugins/:pluginName/:pluginVersion/auth/:authName', | ||
28 | getPluginValidator(PluginType.PLUGIN), | ||
29 | getExternalAuthValidator, | ||
30 | handleAuthInPlugin | ||
31 | ) | ||
32 | |||
26 | pluginsRouter.get('/plugins/:pluginName/:pluginVersion/static/:staticEndpoint(*)', | 33 | pluginsRouter.get('/plugins/:pluginName/:pluginVersion/static/:staticEndpoint(*)', |
27 | getPluginValidator(PluginType.PLUGIN), | 34 | getPluginValidator(PluginType.PLUGIN), |
28 | pluginStaticDirectoryValidator, | 35 | pluginStaticDirectoryValidator, |
@@ -134,3 +141,14 @@ function serveThemeCSSDirectory (req: express.Request, res: express.Response) { | |||
134 | 141 | ||
135 | return res.sendFile(join(plugin.path, staticEndpoint), sendFileOptions) | 142 | return res.sendFile(join(plugin.path, staticEndpoint), sendFileOptions) |
136 | } | 143 | } |
144 | |||
145 | function handleAuthInPlugin (req: express.Request, res: express.Response) { | ||
146 | const authOptions = res.locals.externalAuth | ||
147 | |||
148 | try { | ||
149 | logger.debug('Forwarding auth plugin request in %s of plugin %s.', authOptions.authName, res.locals.registeredPlugin.npmName) | ||
150 | authOptions.onAuthRequest(req, res) | ||
151 | } catch (err) { | ||
152 | logger.error('Forward request error in auth %s of plugin %s.', authOptions.authName, res.locals.registeredPlugin.npmName) | ||
153 | } | ||
154 | } | ||
diff --git a/server/lib/auth.ts b/server/lib/auth.ts index 5a6dd9dec..eaae5fdf3 100644 --- a/server/lib/auth.ts +++ b/server/lib/auth.ts | |||
@@ -1,13 +1,18 @@ | |||
1 | import * as express from 'express' | 1 | import { isUserDisplayNameValid, isUserRoleValid, isUserUsernameValid } from '@server/helpers/custom-validators/users' |
2 | import { OAUTH_LIFETIME } from '@server/initializers/constants' | ||
3 | import * as OAuthServer from 'express-oauth-server' | ||
4 | import { PluginManager } from '@server/lib/plugins/plugin-manager' | ||
5 | import { RegisterServerAuthPassOptions } from '@shared/models/plugins/register-server-auth.model' | ||
6 | import { logger } from '@server/helpers/logger' | 2 | import { logger } from '@server/helpers/logger' |
7 | import { UserRole } from '@shared/models' | 3 | import { generateRandomString } from '@server/helpers/utils' |
4 | import { OAUTH_LIFETIME, WEBSERVER } from '@server/initializers/constants' | ||
8 | import { revokeToken } from '@server/lib/oauth-model' | 5 | import { revokeToken } from '@server/lib/oauth-model' |
6 | import { PluginManager } from '@server/lib/plugins/plugin-manager' | ||
9 | import { OAuthTokenModel } from '@server/models/oauth/oauth-token' | 7 | import { OAuthTokenModel } from '@server/models/oauth/oauth-token' |
10 | import { isUserUsernameValid, isUserRoleValid, isUserDisplayNameValid } from '@server/helpers/custom-validators/users' | 8 | import { UserRole } from '@shared/models' |
9 | import { | ||
10 | RegisterServerAuthenticatedResult, | ||
11 | RegisterServerAuthPassOptions, | ||
12 | RegisterServerExternalAuthenticatedResult | ||
13 | } from '@shared/models/plugins/register-server-auth.model' | ||
14 | import * as express from 'express' | ||
15 | import * as OAuthServer from 'express-oauth-server' | ||
11 | 16 | ||
12 | const oAuthServer = new OAuthServer({ | 17 | const oAuthServer = new OAuthServer({ |
13 | useErrorHandler: true, | 18 | useErrorHandler: true, |
@@ -17,15 +22,28 @@ const oAuthServer = new OAuthServer({ | |||
17 | model: require('./oauth-model') | 22 | model: require('./oauth-model') |
18 | }) | 23 | }) |
19 | 24 | ||
20 | function onExternalAuthPlugin (npmName: string, username: string, email: string) { | 25 | // Token is the key, expiration date is the value |
21 | 26 | const authBypassTokens = new Map<string, { | |
22 | } | 27 | expires: Date |
28 | user: { | ||
29 | username: string | ||
30 | email: string | ||
31 | displayName: string | ||
32 | role: UserRole | ||
33 | } | ||
34 | authName: string | ||
35 | npmName: string | ||
36 | }>() | ||
23 | 37 | ||
24 | async function handleIdAndPassLogin (req: express.Request, res: express.Response, next: express.NextFunction) { | 38 | async function handleIdAndPassLogin (req: express.Request, res: express.Response, next: express.NextFunction) { |
25 | const grantType = req.body.grant_type | 39 | const grantType = req.body.grant_type |
26 | 40 | ||
27 | if (grantType === 'password') await proxifyPasswordGrant(req, res) | 41 | if (grantType === 'password') { |
28 | else if (grantType === 'refresh_token') await proxifyRefreshGrant(req, res) | 42 | if (req.body.externalAuthToken) proxifyExternalAuthBypass(req, res) |
43 | else await proxifyPasswordGrant(req, res) | ||
44 | } else if (grantType === 'refresh_token') { | ||
45 | await proxifyRefreshGrant(req, res) | ||
46 | } | ||
29 | 47 | ||
30 | return forwardTokenReq(req, res, next) | 48 | return forwardTokenReq(req, res, next) |
31 | } | 49 | } |
@@ -53,31 +71,60 @@ async function handleTokenRevocation (req: express.Request, res: express.Respons | |||
53 | return res.sendStatus(200) | 71 | return res.sendStatus(200) |
54 | } | 72 | } |
55 | 73 | ||
56 | // --------------------------------------------------------------------------- | 74 | async function onExternalUserAuthenticated (options: { |
75 | npmName: string | ||
76 | authName: string | ||
77 | authResult: RegisterServerExternalAuthenticatedResult | ||
78 | }) { | ||
79 | const { npmName, authName, authResult } = options | ||
57 | 80 | ||
58 | export { | 81 | if (!authResult.req || !authResult.res) { |
59 | oAuthServer, | 82 | logger.error('Cannot authenticate external user for auth %s of plugin %s: no req or res are provided.', authName, npmName) |
60 | handleIdAndPassLogin, | 83 | return |
61 | onExternalAuthPlugin, | 84 | } |
62 | handleTokenRevocation | 85 | |
86 | if (!isAuthResultValid(npmName, authName, authResult)) return | ||
87 | |||
88 | const { res } = authResult | ||
89 | |||
90 | logger.info('Generating auth bypass token for %s in auth %s of plugin %s.', authResult.username, authName, npmName) | ||
91 | |||
92 | const bypassToken = await generateRandomString(32) | ||
93 | const tokenLifetime = 1000 * 60 * 5 // 5 minutes | ||
94 | |||
95 | const expires = new Date() | ||
96 | expires.setTime(expires.getTime() + tokenLifetime) | ||
97 | |||
98 | const user = buildUserResult(authResult) | ||
99 | authBypassTokens.set(bypassToken, { | ||
100 | expires, | ||
101 | user, | ||
102 | npmName, | ||
103 | authName | ||
104 | }) | ||
105 | |||
106 | res.redirect(`/login?externalAuthToken=${bypassToken}&username=${user.username}`) | ||
63 | } | 107 | } |
64 | 108 | ||
65 | // --------------------------------------------------------------------------- | 109 | // --------------------------------------------------------------------------- |
66 | 110 | ||
67 | function forwardTokenReq (req: express.Request, res: express.Response, next: express.NextFunction) { | 111 | export { oAuthServer, handleIdAndPassLogin, onExternalUserAuthenticated, handleTokenRevocation } |
112 | |||
113 | // --------------------------------------------------------------------------- | ||
114 | |||
115 | function forwardTokenReq (req: express.Request, res: express.Response, next?: express.NextFunction) { | ||
68 | return oAuthServer.token()(req, res, err => { | 116 | return oAuthServer.token()(req, res, err => { |
69 | if (err) { | 117 | if (err) { |
70 | logger.warn('Login error.', { err }) | 118 | logger.warn('Login error.', { err }) |
71 | 119 | ||
72 | return res.status(err.status) | 120 | return res.status(err.status) |
73 | .json({ | 121 | .json({ |
74 | error: err.message, | 122 | error: err.message, |
75 | code: err.name | 123 | code: err.name |
76 | }) | 124 | }) |
77 | .end() | ||
78 | } | 125 | } |
79 | 126 | ||
80 | return next() | 127 | if (next) return next() |
81 | }) | 128 | }) |
82 | } | 129 | } |
83 | 130 | ||
@@ -131,50 +178,96 @@ async function proxifyPasswordGrant (req: express.Request, res: express.Response | |||
131 | 178 | ||
132 | try { | 179 | try { |
133 | const loginResult = await authOptions.login(loginOptions) | 180 | const loginResult = await authOptions.login(loginOptions) |
134 | if (loginResult) { | 181 | |
135 | logger.info( | 182 | if (!loginResult) continue |
136 | 'Login success with auth method %s of plugin %s for %s.', | 183 | if (!isAuthResultValid(pluginAuth.npmName, authOptions.authName, loginResult)) continue |
137 | authName, npmName, loginOptions.id | 184 | |
138 | ) | 185 | logger.info( |
139 | 186 | 'Login success with auth method %s of plugin %s for %s.', | |
140 | if (!isUserUsernameValid(loginResult.username)) { | 187 | authName, npmName, loginOptions.id |
141 | logger.error('Auth method %s of plugin %s did not provide a valid username.', authName, npmName, { loginResult }) | 188 | ) |
142 | continue | 189 | |
143 | } | 190 | res.locals.bypassLogin = { |
144 | 191 | bypass: true, | |
145 | if (!loginResult.email) { | 192 | pluginName: pluginAuth.npmName, |
146 | logger.error('Auth method %s of plugin %s did not provide a valid email.', authName, npmName, { loginResult }) | 193 | authName: authOptions.authName, |
147 | continue | 194 | user: buildUserResult(loginResult) |
148 | } | ||
149 | |||
150 | // role is optional | ||
151 | if (loginResult.role && !isUserRoleValid(loginResult.role)) { | ||
152 | logger.error('Auth method %s of plugin %s did not provide a valid role.', authName, npmName, { loginResult }) | ||
153 | continue | ||
154 | } | ||
155 | |||
156 | // display name is optional | ||
157 | if (loginResult.displayName && !isUserDisplayNameValid(loginResult.displayName)) { | ||
158 | logger.error('Auth method %s of plugin %s did not provide a valid display name.', authName, npmName, { loginResult }) | ||
159 | continue | ||
160 | } | ||
161 | |||
162 | res.locals.bypassLogin = { | ||
163 | bypass: true, | ||
164 | pluginName: pluginAuth.npmName, | ||
165 | authName: authOptions.authName, | ||
166 | user: { | ||
167 | username: loginResult.username, | ||
168 | email: loginResult.email, | ||
169 | role: loginResult.role || UserRole.USER, | ||
170 | displayName: loginResult.displayName || loginResult.username | ||
171 | } | ||
172 | } | ||
173 | |||
174 | return | ||
175 | } | 195 | } |
196 | |||
197 | return | ||
176 | } catch (err) { | 198 | } catch (err) { |
177 | logger.error('Error in auth method %s of plugin %s', authOptions.authName, pluginAuth.npmName, { err }) | 199 | logger.error('Error in auth method %s of plugin %s', authOptions.authName, pluginAuth.npmName, { err }) |
178 | } | 200 | } |
179 | } | 201 | } |
180 | } | 202 | } |
203 | |||
204 | function proxifyExternalAuthBypass (req: express.Request, res: express.Response) { | ||
205 | const obj = authBypassTokens.get(req.body.externalAuthToken) | ||
206 | if (!obj) { | ||
207 | logger.error('Cannot authenticate user with unknown bypass token') | ||
208 | return res.sendStatus(400) | ||
209 | } | ||
210 | |||
211 | const { expires, user, authName, npmName } = obj | ||
212 | |||
213 | const now = new Date() | ||
214 | if (now.getTime() > expires.getTime()) { | ||
215 | logger.error('Cannot authenticate user with an expired bypass token') | ||
216 | return res.sendStatus(400) | ||
217 | } | ||
218 | |||
219 | if (user.username !== req.body.username) { | ||
220 | logger.error('Cannot authenticate user %s with invalid username %s.', req.body.username) | ||
221 | return res.sendStatus(400) | ||
222 | } | ||
223 | |||
224 | // Bypass oauth library validation | ||
225 | req.body.password = 'fake' | ||
226 | |||
227 | logger.info( | ||
228 | 'Auth success with external auth method %s of plugin %s for %s.', | ||
229 | authName, npmName, user.email | ||
230 | ) | ||
231 | |||
232 | res.locals.bypassLogin = { | ||
233 | bypass: true, | ||
234 | pluginName: npmName, | ||
235 | authName: authName, | ||
236 | user | ||
237 | } | ||
238 | } | ||
239 | |||
240 | function isAuthResultValid (npmName: string, authName: string, result: RegisterServerAuthenticatedResult) { | ||
241 | if (!isUserUsernameValid(result.username)) { | ||
242 | logger.error('Auth method %s of plugin %s did not provide a valid username.', authName, npmName, { result }) | ||
243 | return false | ||
244 | } | ||
245 | |||
246 | if (!result.email) { | ||
247 | logger.error('Auth method %s of plugin %s did not provide a valid email.', authName, npmName, { result }) | ||
248 | return false | ||
249 | } | ||
250 | |||
251 | // role is optional | ||
252 | if (result.role && !isUserRoleValid(result.role)) { | ||
253 | logger.error('Auth method %s of plugin %s did not provide a valid role.', authName, npmName, { result }) | ||
254 | return false | ||
255 | } | ||
256 | |||
257 | // display name is optional | ||
258 | if (result.displayName && !isUserDisplayNameValid(result.displayName)) { | ||
259 | logger.error('Auth method %s of plugin %s did not provide a valid display name.', authName, npmName, { result }) | ||
260 | return false | ||
261 | } | ||
262 | |||
263 | return true | ||
264 | } | ||
265 | |||
266 | function buildUserResult (pluginResult: RegisterServerAuthenticatedResult) { | ||
267 | return { | ||
268 | username: pluginResult.username, | ||
269 | email: pluginResult.email, | ||
270 | role: pluginResult.role || UserRole.USER, | ||
271 | displayName: pluginResult.displayName || pluginResult.username | ||
272 | } | ||
273 | } | ||
diff --git a/server/lib/oauth-model.ts b/server/lib/oauth-model.ts index 6eb0e4473..8b9975bb4 100644 --- a/server/lib/oauth-model.ts +++ b/server/lib/oauth-model.ts | |||
@@ -98,7 +98,7 @@ async function getRefreshToken (refreshToken: string) { | |||
98 | return tokenInfo | 98 | return tokenInfo |
99 | } | 99 | } |
100 | 100 | ||
101 | async function getUser (usernameOrEmail: string, password: string) { | 101 | async function getUser (usernameOrEmail?: string, password?: string) { |
102 | const res: express.Response = this.request.res | 102 | const res: express.Response = this.request.res |
103 | 103 | ||
104 | // Special treatment coming from a plugin | 104 | // Special treatment coming from a plugin |
diff --git a/server/lib/plugins/register-helpers-store.ts b/server/lib/plugins/register-helpers-store.ts index 687974ccf..277f2b687 100644 --- a/server/lib/plugins/register-helpers-store.ts +++ b/server/lib/plugins/register-helpers-store.ts | |||
@@ -1,31 +1,21 @@ | |||
1 | import { PluginSettingsManager } from '@shared/models/plugins/plugin-settings-manager.model' | 1 | import { logger } from '@server/helpers/logger' |
2 | import { VIDEO_CATEGORIES, VIDEO_LANGUAGES, VIDEO_LICENCES, VIDEO_PLAYLIST_PRIVACIES, VIDEO_PRIVACIES } from '@server/initializers/constants' | ||
3 | import { onExternalUserAuthenticated } from '@server/lib/auth' | ||
2 | import { PluginModel } from '@server/models/server/plugin' | 4 | import { PluginModel } from '@server/models/server/plugin' |
5 | import { RegisterServerOptions } from '@server/typings/plugins' | ||
6 | import { PluginPlaylistPrivacyManager } from '@shared/models/plugins/plugin-playlist-privacy-manager.model' | ||
7 | import { PluginSettingsManager } from '@shared/models/plugins/plugin-settings-manager.model' | ||
3 | import { PluginStorageManager } from '@shared/models/plugins/plugin-storage-manager.model' | 8 | import { PluginStorageManager } from '@shared/models/plugins/plugin-storage-manager.model' |
9 | import { PluginVideoCategoryManager } from '@shared/models/plugins/plugin-video-category-manager.model' | ||
4 | import { PluginVideoLanguageManager } from '@shared/models/plugins/plugin-video-language-manager.model' | 10 | import { PluginVideoLanguageManager } from '@shared/models/plugins/plugin-video-language-manager.model' |
5 | import { | ||
6 | VIDEO_CATEGORIES, | ||
7 | VIDEO_LANGUAGES, | ||
8 | VIDEO_LICENCES, | ||
9 | VIDEO_PLAYLIST_PRIVACIES, | ||
10 | VIDEO_PRIVACIES | ||
11 | } from '@server/initializers/constants' | ||
12 | import { PluginVideoLicenceManager } from '@shared/models/plugins/plugin-video-licence-manager.model' | 11 | import { PluginVideoLicenceManager } from '@shared/models/plugins/plugin-video-licence-manager.model' |
13 | import { PluginVideoCategoryManager } from '@shared/models/plugins/plugin-video-category-manager.model' | 12 | import { PluginVideoPrivacyManager } from '@shared/models/plugins/plugin-video-privacy-manager.model' |
14 | import { RegisterServerOptions } from '@server/typings/plugins' | 13 | import { RegisterServerAuthExternalOptions, RegisterServerAuthExternalResult, RegisterServerAuthPassOptions, RegisterServerExternalAuthenticatedResult } from '@shared/models/plugins/register-server-auth.model' |
15 | import { buildPluginHelpers } from './plugin-helpers' | ||
16 | import { logger } from '@server/helpers/logger' | ||
17 | import { RegisterServerHookOptions } from '@shared/models/plugins/register-server-hook.model' | 14 | import { RegisterServerHookOptions } from '@shared/models/plugins/register-server-hook.model' |
18 | import { serverHookObject } from '@shared/models/plugins/server-hook.model' | ||
19 | import { RegisterServerSettingOptions } from '@shared/models/plugins/register-server-setting.model' | 15 | import { RegisterServerSettingOptions } from '@shared/models/plugins/register-server-setting.model' |
16 | import { serverHookObject } from '@shared/models/plugins/server-hook.model' | ||
20 | import * as express from 'express' | 17 | import * as express from 'express' |
21 | import { PluginVideoPrivacyManager } from '@shared/models/plugins/plugin-video-privacy-manager.model' | 18 | import { buildPluginHelpers } from './plugin-helpers' |
22 | import { PluginPlaylistPrivacyManager } from '@shared/models/plugins/plugin-playlist-privacy-manager.model' | ||
23 | import { | ||
24 | RegisterServerAuthExternalOptions, | ||
25 | RegisterServerAuthExternalResult, | ||
26 | RegisterServerAuthPassOptions | ||
27 | } from '@shared/models/plugins/register-server-auth.model' | ||
28 | import { onExternalAuthPlugin } from '@server/lib/auth' | ||
29 | 19 | ||
30 | type AlterableVideoConstant = 'language' | 'licence' | 'category' | 'privacy' | 'playlistPrivacy' | 20 | type AlterableVideoConstant = 'language' | 'licence' | 'category' | 'privacy' | 'playlistPrivacy' |
31 | type VideoConstant = { [key in number | string]: string } | 21 | type VideoConstant = { [key in number | string]: string } |
@@ -187,8 +177,14 @@ export class RegisterHelpersStore { | |||
187 | this.externalAuths.push(options) | 177 | this.externalAuths.push(options) |
188 | 178 | ||
189 | return { | 179 | return { |
190 | onAuth (options: { username: string, email: string }): void { | 180 | userAuthenticated (result: RegisterServerExternalAuthenticatedResult): void { |
191 | onExternalAuthPlugin(self.npmName, options.username, options.email) | 181 | onExternalUserAuthenticated({ |
182 | npmName: self.npmName, | ||
183 | authName: options.authName, | ||
184 | authResult: result | ||
185 | }).catch(err => { | ||
186 | logger.error('Cannot execute onExternalUserAuthenticated.', { npmName: self.npmName, authName: options.authName, err }) | ||
187 | }) | ||
192 | } | 188 | } |
193 | } as RegisterServerAuthExternalResult | 189 | } as RegisterServerAuthExternalResult |
194 | } | 190 | } |
diff --git a/server/middlewares/validators/plugins.ts b/server/middlewares/validators/plugins.ts index 65765f473..2cb49ec43 100644 --- a/server/middlewares/validators/plugins.ts +++ b/server/middlewares/validators/plugins.ts | |||
@@ -4,7 +4,7 @@ import { logger } from '../../helpers/logger' | |||
4 | import { areValidationErrors } from './utils' | 4 | import { areValidationErrors } from './utils' |
5 | import { isNpmPluginNameValid, isPluginNameValid, isPluginTypeValid, isPluginVersionValid } from '../../helpers/custom-validators/plugins' | 5 | import { isNpmPluginNameValid, isPluginNameValid, isPluginTypeValid, isPluginVersionValid } from '../../helpers/custom-validators/plugins' |
6 | import { PluginManager } from '../../lib/plugins/plugin-manager' | 6 | import { PluginManager } from '../../lib/plugins/plugin-manager' |
7 | import { isBooleanValid, isSafePath, toBooleanOrNull } from '../../helpers/custom-validators/misc' | 7 | import { isBooleanValid, isSafePath, toBooleanOrNull, exists } from '../../helpers/custom-validators/misc' |
8 | import { PluginModel } from '../../models/server/plugin' | 8 | import { PluginModel } from '../../models/server/plugin' |
9 | import { InstallOrUpdatePlugin } from '../../../shared/models/plugins/install-plugin.model' | 9 | import { InstallOrUpdatePlugin } from '../../../shared/models/plugins/install-plugin.model' |
10 | import { PluginType } from '../../../shared/models/plugins/plugin.type' | 10 | import { PluginType } from '../../../shared/models/plugins/plugin.type' |
@@ -40,6 +40,26 @@ const getPluginValidator = (pluginType: PluginType, withVersion = true) => { | |||
40 | ]) | 40 | ]) |
41 | } | 41 | } |
42 | 42 | ||
43 | const getExternalAuthValidator = [ | ||
44 | param('authName').custom(exists).withMessage('Should have a valid auth name'), | ||
45 | |||
46 | (req: express.Request, res: express.Response, next: express.NextFunction) => { | ||
47 | logger.debug('Checking getExternalAuthValidator parameters', { parameters: req.params }) | ||
48 | |||
49 | if (areValidationErrors(req, res)) return | ||
50 | |||
51 | const plugin = res.locals.registeredPlugin | ||
52 | if (!plugin.registerHelpersStore) return res.sendStatus(404) | ||
53 | |||
54 | const externalAuth = plugin.registerHelpersStore.getExternalAuths().find(a => a.authName === req.params.authName) | ||
55 | if (!externalAuth) return res.sendStatus(404) | ||
56 | |||
57 | res.locals.externalAuth = externalAuth | ||
58 | |||
59 | return next() | ||
60 | } | ||
61 | ] | ||
62 | |||
43 | const pluginStaticDirectoryValidator = [ | 63 | const pluginStaticDirectoryValidator = [ |
44 | param('staticEndpoint').custom(isSafePath).withMessage('Should have a valid static endpoint'), | 64 | param('staticEndpoint').custom(isSafePath).withMessage('Should have a valid static endpoint'), |
45 | 65 | ||
@@ -175,5 +195,6 @@ export { | |||
175 | listAvailablePluginsValidator, | 195 | listAvailablePluginsValidator, |
176 | existingPluginValidator, | 196 | existingPluginValidator, |
177 | installOrUpdatePluginValidator, | 197 | installOrUpdatePluginValidator, |
178 | listPluginsValidator | 198 | listPluginsValidator, |
199 | getExternalAuthValidator | ||
179 | } | 200 | } |
diff --git a/server/typings/express.ts b/server/typings/express.ts index e6e120403..5973496f1 100644 --- a/server/typings/express.ts +++ b/server/typings/express.ts | |||
@@ -29,6 +29,7 @@ import { MPlugin, MServer } from '@server/typings/models/server' | |||
29 | import { MServerBlocklist } from './models/server/server-blocklist' | 29 | import { MServerBlocklist } from './models/server/server-blocklist' |
30 | import { MOAuthTokenUser } from '@server/typings/models/oauth/oauth-token' | 30 | import { MOAuthTokenUser } from '@server/typings/models/oauth/oauth-token' |
31 | import { UserRole } from '@shared/models' | 31 | import { UserRole } from '@shared/models' |
32 | import { RegisterServerAuthExternalOptions } from '@shared/models/plugins/register-server-auth.model' | ||
32 | 33 | ||
33 | declare module 'express' { | 34 | declare module 'express' { |
34 | interface Response { | 35 | interface Response { |
@@ -115,6 +116,8 @@ declare module 'express' { | |||
115 | 116 | ||
116 | registeredPlugin?: RegisteredPlugin | 117 | registeredPlugin?: RegisteredPlugin |
117 | 118 | ||
119 | externalAuth?: RegisterServerAuthExternalOptions | ||
120 | |||
118 | plugin?: MPlugin | 121 | plugin?: MPlugin |
119 | } | 122 | } |
120 | } | 123 | } |