]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blob - server/lib/auth.ts
Add plugin ldap tests
[github/Chocobozzz/PeerTube.git] / server / lib / auth.ts
1 import * as express from 'express'
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'
7 import { UserRole } from '@shared/models'
8 import { revokeToken } from '@server/lib/oauth-model'
9 import { OAuthTokenModel } from '@server/models/oauth/oauth-token'
10
11 const oAuthServer = new OAuthServer({
12 useErrorHandler: true,
13 accessTokenLifetime: OAUTH_LIFETIME.ACCESS_TOKEN,
14 refreshTokenLifetime: OAUTH_LIFETIME.REFRESH_TOKEN,
15 continueMiddleware: true,
16 model: require('./oauth-model')
17 })
18
19 function onExternalAuthPlugin (npmName: string, username: string, email: string) {
20
21 }
22
23 async function handleIdAndPassLogin (req: express.Request, res: express.Response, next: express.NextFunction) {
24 const grantType = req.body.grant_type
25
26 if (grantType === 'password') await proxifyPasswordGrant(req, res)
27 else if (grantType === 'refresh_token') await proxifyRefreshGrant(req, res)
28
29 return forwardTokenReq(req, res, next)
30 }
31
32 async function handleTokenRevocation (req: express.Request, res: express.Response) {
33 const token = res.locals.oauth.token
34
35 res.locals.explicitLogout = true
36 await revokeToken(token)
37
38 // FIXME: uncomment when https://github.com/oauthjs/node-oauth2-server/pull/289 is released
39 // oAuthServer.revoke(req, res, err => {
40 // if (err) {
41 // logger.warn('Error in revoke token handler.', { err })
42 //
43 // return res.status(err.status)
44 // .json({
45 // error: err.message,
46 // code: err.name
47 // })
48 // .end()
49 // }
50 // })
51
52 return res.sendStatus(200)
53 }
54
55 // ---------------------------------------------------------------------------
56
57 export {
58 oAuthServer,
59 handleIdAndPassLogin,
60 onExternalAuthPlugin,
61 handleTokenRevocation
62 }
63
64 // ---------------------------------------------------------------------------
65
66 function forwardTokenReq (req: express.Request, res: express.Response, next: express.NextFunction) {
67 return oAuthServer.token()(req, res, err => {
68 if (err) {
69 logger.warn('Login error.', { err })
70
71 return res.status(err.status)
72 .json({
73 error: err.message,
74 code: err.name
75 })
76 .end()
77 }
78
79 return next()
80 })
81 }
82
83 async function proxifyRefreshGrant (req: express.Request, res: express.Response) {
84 const refreshToken = req.body.refresh_token
85 if (!refreshToken) return
86
87 const tokenModel = await OAuthTokenModel.loadByRefreshToken(refreshToken)
88 if (tokenModel?.authName) res.locals.refreshTokenAuthName = tokenModel.authName
89 }
90
91 async function proxifyPasswordGrant (req: express.Request, res: express.Response) {
92 const plugins = PluginManager.Instance.getIdAndPassAuths()
93 const pluginAuths: { npmName?: string, registerAuthOptions: RegisterServerAuthPassOptions }[] = []
94
95 for (const plugin of plugins) {
96 const auths = plugin.idAndPassAuths
97
98 for (const auth of auths) {
99 pluginAuths.push({
100 npmName: plugin.npmName,
101 registerAuthOptions: auth
102 })
103 }
104 }
105
106 pluginAuths.sort((a, b) => {
107 const aWeight = a.registerAuthOptions.getWeight()
108 const bWeight = b.registerAuthOptions.getWeight()
109
110 // DESC weight order
111 if (aWeight === bWeight) return 0
112 if (aWeight < bWeight) return 1
113 return -1
114 })
115
116 const loginOptions = {
117 id: req.body.username,
118 password: req.body.password
119 }
120
121 for (const pluginAuth of pluginAuths) {
122 const authOptions = pluginAuth.registerAuthOptions
123
124 logger.debug(
125 'Using auth method %s of plugin %s to login %s with weight %d.',
126 authOptions.authName, pluginAuth.npmName, loginOptions.id, authOptions.getWeight()
127 )
128
129 try {
130 const loginResult = await authOptions.login(loginOptions)
131 if (loginResult) {
132 logger.info(
133 'Login success with auth method %s of plugin %s for %s.',
134 authOptions.authName, pluginAuth.npmName, loginOptions.id
135 )
136
137 res.locals.bypassLogin = {
138 bypass: true,
139 pluginName: pluginAuth.npmName,
140 authName: authOptions.authName,
141 user: {
142 username: loginResult.username,
143 email: loginResult.email,
144 role: loginResult.role || UserRole.USER,
145 displayName: loginResult.displayName || loginResult.username
146 }
147 }
148
149 return
150 }
151 } catch (err) {
152 logger.error('Error in auth method %s of plugin %s', authOptions.authName, pluginAuth.npmName, { err })
153 }
154 }
155 }