This commit is contained in:
yzx9 2023-11-23 17:06:44 +08:00
parent 242183d601
commit f53790c452
5 changed files with 110 additions and 63 deletions

View File

@ -42,13 +42,10 @@ COPY sharelatex/navbar.pug /overleaf/services/web/app/views/layout/
# Non LDAP User Registration for Admins # Non LDAP User Registration for Admins
COPY sharelatex/admin-index.pug /overleaf/services/web/app/views/admin/index.pug COPY sharelatex/admin-index.pug /overleaf/services/web/app/views/admin/index.pug
COPY sharelatex/admin-sysadmin.pug /tmp/admin-sysadmin.pug COPY sharelatex/admin-sysadmin.pug /tmp/admin-sysadmin.pug
## instead of copying the login.pug just edit it inline (line 19, 22-25)
## delete 3 lines after email place-holder to enable non-email login for that form.
RUN sed -iE '/type=.*email.*/d' /overleaf/services/web/app/views/user/login.pug && \
## comment out this line to prevent sed accidently remove the brackets of the email(username) field ## comment out this line to prevent sed accidently remove the brackets of the email(username) field
# sed -iE '/email@example.com/{n;N;N;d}' /overleaf/services/web/app/views/user/login.pug && \ # sed -iE '/email@example.com/{n;N;N;d}' /overleaf/services/web/app/views/user/login.pug && \
sed -iE "s/email@example.com/${login_text:-user}/g" /overleaf/services/web/app/views/user/login.pug && \ RUN sed -iE "s/email@example.com/${login_text:-user}/g" /overleaf/services/web/app/views/user/login.pug && \
## Collaboration settings display (share project placeholder) | edit line 146 ## Collaboration settings display (share project placeholder) | edit line 146
## share.pug file was removed in later versions ## share.pug file was removed in later versions
# sed -iE "s%placeholder=.*$%placeholder=\"${collab_text}\"%g" /overleaf/services/web/app/views/project/editor/share.pug && \ # sed -iE "s%placeholder=.*$%placeholder=\"${collab_text}\"%g" /overleaf/services/web/app/views/project/editor/share.pug && \

View File

@ -275,81 +275,79 @@ const AuthenticationController = {
// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>> // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
oauth2Redirect(req, res, next) { oauth2Redirect(req, res, next) {
const redirectURI = encodeURIComponent(`${process.env.SHARELATEX_SITE_URL}/oauth/callback`) const redirectURI = encodeURIComponent(`${process.env.SHARELATEX_SITE_URL}/oauth/callback`)
const next = ( const authURL = (
process.env.OAUTH2_AUTHORIZATION_URL process.env.OAUTH2_AUTHORIZATION_URL
+ `?response_type=code` + `?response_type=code`
+ `&client_id=${process.env.OAUTH2_CLIENT_ID}` + `&client_id=${process.env.OAUTH2_CLIENT_ID}`
+ `&redirect_uri=${redirectURI}` + `&redirect_uri=${redirectURI}`
+ `&scope=${process.env.OAUTH2_SCOPE ?? ""}` // TODO: state + `&scope=${process.env.OAUTH2_SCOPE ?? ""}` // TODO: state
) )
res.redirect(next) res.redirect(authURL)
}, },
async oauth2Callback(req, res, next) { async oauth2Callback(req, res, next) {
try { try {
const redirectURI = encodeURIComponent(`${process.env.SHARELATEX_SITE_URL}/oauth/callback`); console.log("OAuth2 code", req.query.code)
const tokenResponse = await fetch(process.env.OAUTH2_TOKEN_URL, { const tokenResponse = await fetch(process.env.OAUTH2_TOKEN_URL, {
method: 'POST', method: 'POST',
headers: { headers: {
'Content-Type': 'application/json' "Accept": "application/json",
"Content-Type": "application/json",
}, },
body: JSON.stringify({ body: JSON.stringify({
grant_type: "authorization_code", grant_type: "authorization_code",
client_id: process.env.OAUTH2_CLIENT_ID, client_id: process.env.OAUTH2_CLIENT_ID,
client_secret: process.env.OAUTH2_CLIENT_SECRET, client_secret: process.env.OAUTH2_CLIENT_SECRET,
code: req.query.code, code: req.query.code,
redirect_uri: redirectURI, redirect_uri: `${process.env.SHARELATEX_SITE_URL}/oauth/callback`,
}) })
}) })
const tokenData = await tokenResponse.json() const tokenData = await tokenResponse.json()
console.log("OAuth2 respond", JSON.stringify(tokenData)) // TODO: remove console.log("OAuth2 respond", JSON.stringify(tokenData))
console.log("OAuth2 accessToken", tokenData.access_token) // TODO: remove
const profileResponse = await fetch(process.env.OAUTH2_PROFILE_URL, { const profileResponse = await fetch(process.env.OAUTH2_PROFILE_URL, {
method: 'GET', method: 'GET',
headers: { headers: {
"Accept": "application/json",
"Authorization": `Bearer ${tokenData.access_token}`,
"Content-Type": "application/json", "Content-Type": "application/json",
"Authorization": `Bearer ${tokenData.access_token}`
} }
}) })
const profile = await profileResponse.json() const profile = await profileResponse.json()
console.log("OAuth2 user info", JSON.stringify(profile.data)) console.log("OAuth2 user profile", JSON.stringify(profile))
const email = profile[process.env.OAUTH2_USER_ATTR_EMAIL ?? "email"] const email = profile[process.env.OAUTH2_USER_ATTR_EMAIL ?? "email"]
const uid = profile[process.env.OAUTH2_USER_ATTR_UID ?? "uid"] const uid = profile[process.env.OAUTH2_USER_ATTR_UID ?? "uid"]
const firstname = profile?.[process.env.OAUTH2_USER_ATTR_FIRSTNAME] ?? email const firstname = profile?.[process.env.OAUTH2_USER_ATTR_FIRSTNAME] ?? email
const lastname = profile?.[process.env.OAUTH2_USER_ATTR_LASTNAME] ?? "" const lastname = process.env.OAUTH2_USER_ATTR_LASTNAME
? profile?.[process.env.OAUTH2_USER_ATTR_LASTNAME] ?? ""
const isAdmin = false // TODO: how to determine? : ""
const isAdmin = process.env.OAUTH2_USER_ATTR_IS_ADMIN
? !!profile?.[process.env.OAUTH2_USER_ATTR_IS_ADMIN] ?? false
: false
const query = { email } const query = { email }
User.findOne(query, (error, user) => { const callback = (error, user) => {
if (error) { if (error) {
console.log(error) res.json({message: error});
} else {
console.log("OAuth user", JSON.stringify(user));
AuthenticationController.finishLogin(user, req, res, next);
} }
}
const callback = (error, user) => { AuthenticationManager.createIfNotFoundAndLogin(
if (error) { query,
res.json({message: error}); callback,
} else { uid,
// console.log("real_user: ", user); firstname,
AuthenticationController.finishLogin(user, req, res, next); lastname,
} email,
} isAdmin
AuthenticationManager.createIfNotExistAndLogin( )
query,
user,
callback,
uid,
firstname,
lastname,
email,
isAdmin
)
})
} catch(e) { } catch(e) {
console.log("Fails to access by OAuth2: " + String(e)) res.redirect("/login")
console.error("Fails to access by OAuth2: " + String(e))
} }
}, },
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<< // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

View File

@ -184,6 +184,33 @@ const AuthenticationManager = {
callback(null, user, true) callback(null, user, true)
}, },
createIfNotFoundAndLogin(
query,
callback,
uid,
firstname,
lastname,
mail,
isAdmin
) {
User.findOne(query, (error, user) => {
if (error) {
console.log(error)
}
AuthenticationManager.createIfNotExistAndLogin(
query,
user,
callback,
uid,
firstname,
lastname,
mail,
isAdmin
)
})
},
createIfNotExistAndLogin( createIfNotExistAndLogin(
query, query,
user, user,
@ -195,10 +222,9 @@ const AuthenticationManager = {
isAdmin isAdmin
) { ) {
if (!user) { if (!user) {
//console.log('Creating User:' + JSON.stringify(query))
//create random pass for local userdb, does not get checked for ldap users during login //create random pass for local userdb, does not get checked for ldap users during login
let pass = require("crypto").randomBytes(32).toString("hex") const pass = require("crypto").randomBytes(32).toString("hex")
//console.log('Creating User:' + JSON.stringify(query) + 'Random Pass' + pass) console.log('Creating User', { mail, uid, firstname, lastname, isAdmin, pass })
const userRegHand = require("../User/UserRegistrationHandler.js") const userRegHand = require("../User/UserRegistrationHandler.js")
userRegHand.registerNewUser( userRegHand.registerNewUser(
@ -228,6 +254,7 @@ const AuthenticationManager = {
} }
) // end register user ) // end register user
} else { } else {
console.log('User exists', { mail })
AuthenticationManager.login(user, "randomPass", callback) AuthenticationManager.login(user, "randomPass", callback)
} }
}, },

View File

@ -16,13 +16,21 @@ block content
input(name='_csrf', type='hidden', value=csrfToken) input(name='_csrf', type='hidden', value=csrfToken)
+formMessages() +formMessages()
.form-group .form-group
//- >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
//- input.form-control(
//- type='email',
//- name='email',
//- required,
//- placeholder='email@example.com',
//- autofocus="true"
//- )
input.form-control( input.form-control(
type='email',
name='email', name='email',
required, required,
placeholder='email@example.com', placeholder='email@example.com',
autofocus="true" autofocus="true"
) )
//- <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
.form-group .form-group
input.form-control( input.form-control(
type='password', type='password',
@ -38,8 +46,8 @@ block content
span(data-ol-inflight="idle") #{translate("login")} span(data-ol-inflight="idle") #{translate("login")}
span(hidden data-ol-inflight="pending") #{translate("logging_in")}… span(hidden data-ol-inflight="pending") #{translate("logging_in")}…
a.pull-right(href='/user/password/reset') #{translate("forgot_your_password")}? a.pull-right(href='/user/password/reset') #{translate("forgot_your_password")}?
//- >>>>>>>>>>>>>>>>>>>>>>>>>>>>>> //- >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
.form-group.text-center(style="padding-top: 10px") .form-group.text-center(style="padding-top: 10px")
a.btn-block.login-btn(href="/oauth/redirect" style='padding-left: 0px') a.btn-block.login-btn(href="/oauth/redirect" style='padding-left: 0px')
| Log in via OAuth | Log in via OAuth
//- <<<<<<<<<<<<<<<<<<<<<<<<<<<<<< //- <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

View File

@ -1,6 +1,6 @@
/** /**
* >>>>>>>>>>>>>>>>>>>>>>>>>>>>>> * >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
* Modified from 6408d15 * Modified from bf92436
* <<<<<<<<<<<<<<<<<<<<<<<<<<<<<< * <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
*/ */
@ -27,7 +27,6 @@ const UserInfoController = require('./Features/User/UserInfoController')
const UserController = require('./Features/User/UserController') const UserController = require('./Features/User/UserController')
const UserEmailsController = require('./Features/User/UserEmailsController') const UserEmailsController = require('./Features/User/UserEmailsController')
const UserPagesController = require('./Features/User/UserPagesController') const UserPagesController = require('./Features/User/UserPagesController')
const TutorialController = require('./Features/Tutorial/TutorialController')
const DocumentController = require('./Features/Documents/DocumentController') const DocumentController = require('./Features/Documents/DocumentController')
const CompileManager = require('./Features/Compile/CompileManager') const CompileManager = require('./Features/Compile/CompileManager')
const CompileController = require('./Features/Compile/CompileController') const CompileController = require('./Features/Compile/CompileController')
@ -105,6 +104,10 @@ const rateLimiters = {
points: 10, points: 10,
duration: 60, duration: 60,
}), }),
confirmUniversityDomain: new RateLimiter('confirm-university-domain', {
points: 1,
duration: 60,
}),
createProject: new RateLimiter('create-project', { createProject: new RateLimiter('create-project', {
points: 20, points: 20,
duration: 60, duration: 60,
@ -149,6 +152,10 @@ const rateLimiters = {
points: 30, points: 30,
duration: 60, duration: 60,
}), }),
indexProjectReferences: new RateLimiter('index-project-references', {
points: 30,
duration: 60,
}),
miscOutputDownload: new RateLimiter('misc-output-download', { miscOutputDownload: new RateLimiter('misc-output-download', {
points: 1000, points: 1000,
duration: 60 * 60, duration: 60 * 60,
@ -185,7 +192,7 @@ const rateLimiters = {
duration: 60, duration: 60,
}), }),
resendConfirmation: new RateLimiter('resend-confirmation', { resendConfirmation: new RateLimiter('resend-confirmation', {
points: 1, points: 10,
duration: 60, duration: 60,
}), }),
sendChatMessage: new RateLimiter('send-chat-message', { sendChatMessage: new RateLimiter('send-chat-message', {
@ -256,12 +263,12 @@ function initialize(webRouter, privateApiRouter, publicApiRouter) {
AuthenticationController.addEndpointToLoginWhitelist('/register') AuthenticationController.addEndpointToLoginWhitelist('/register')
} }
// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>> // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
webRouter.get('/oauth/redirect', AuthenticationController.oauth2Redirect) webRouter.get('/oauth/redirect', AuthenticationController.oauth2Redirect)
webRouter.get('/oauth/callback', AuthenticationController.oauth2Callback) webRouter.get('/oauth/callback', AuthenticationController.oauth2Callback)
AuthenticationController.addEndpointToLoginWhitelist('/oauth/redirect') AuthenticationController.addEndpointToLoginWhitelist('/oauth/redirect')
AuthenticationController.addEndpointToLoginWhitelist('/oauth/callback') AuthenticationController.addEndpointToLoginWhitelist('/oauth/callback')
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<< // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
EditorRouter.apply(webRouter, privateApiRouter) EditorRouter.apply(webRouter, privateApiRouter)
CollaboratorsRouter.apply(webRouter, privateApiRouter) CollaboratorsRouter.apply(webRouter, privateApiRouter)
@ -433,12 +440,6 @@ AuthenticationController.addEndpointToLoginWhitelist('/oauth/callback')
TpdsController.getQueues TpdsController.getQueues
) )
webRouter.post(
'/tutorial/:tutorialKey/complete',
AuthenticationController.requireLogin(),
TutorialController.completeTutorial
)
webRouter.get( webRouter.get(
'/user/projects', '/user/projects',
AuthenticationController.requireLogin(), AuthenticationController.requireLogin(),
@ -734,6 +735,16 @@ AuthenticationController.addEndpointToLoginWhitelist('/oauth/callback')
AuthorizationMiddleware.ensureUserCanReadProject, AuthorizationMiddleware.ensureUserCanReadProject,
HistoryController.proxyToHistoryApi HistoryController.proxyToHistoryApi
) )
webRouter.post(
'/project/:Project_id/doc/:doc_id/version/:version_id/restore',
AuthorizationMiddleware.ensureUserCanWriteProjectContent,
HistoryController.proxyToHistoryApi
)
webRouter.post(
'/project/:project_id/doc/:doc_id/restore',
AuthorizationMiddleware.ensureUserCanWriteProjectContent,
HistoryController.restoreDocFromDeletedDoc
)
webRouter.post( webRouter.post(
'/project/:project_id/restore_file', '/project/:project_id/restore_file',
AuthorizationMiddleware.ensureUserCanWriteProjectContent, AuthorizationMiddleware.ensureUserCanWriteProjectContent,
@ -1082,6 +1093,12 @@ AuthenticationController.addEndpointToLoginWhitelist('/oauth/callback')
ChatController.sendMessage ChatController.sendMessage
) )
webRouter.post(
'/project/:Project_id/references/index',
AuthorizationMiddleware.ensureUserCanReadProject,
RateLimiterMiddleware.rateLimit(rateLimiters.indexProjectReferences),
ReferencesController.index
)
webRouter.post( webRouter.post(
'/project/:Project_id/references/indexAll', '/project/:Project_id/references/indexAll',
AuthorizationMiddleware.ensureUserCanReadProject, AuthorizationMiddleware.ensureUserCanReadProject,
@ -1130,6 +1147,7 @@ AuthenticationController.addEndpointToLoginWhitelist('/oauth/callback')
) )
publicApiRouter.post( publicApiRouter.post(
'/api/institutions/confirm_university_domain', '/api/institutions/confirm_university_domain',
RateLimiterMiddleware.rateLimit(rateLimiters.confirmUniversityDomain),
AuthenticationController.requirePrivateApiAuth(), AuthenticationController.requirePrivateApiAuth(),
InstitutionsController.confirmDomain InstitutionsController.confirmDomain
) )
@ -1357,5 +1375,4 @@ AuthenticationController.addEndpointToLoginWhitelist('/oauth/callback')
webRouter.get('*', ErrorController.notFound) webRouter.get('*', ErrorController.notFound)
} }
module.exports = { initialize, rateLimiters }
module.exports = { initialize, rateLimiters }