From 6497f321fd785190aac0ef4dc6ad9c676a2b52e6 Mon Sep 17 00:00:00 2001 From: armanddidierjean <95971503+armanddidierjean@users.noreply.github.com> Date: Sun, 29 Sep 2024 16:48:45 +0200 Subject: [PATCH 1/4] Update docker compose to use Mongo 5 and OVERLEAF prefixed env variables --- docker-compose.certbot.yml | 54 +++++++++++++++++++------------------- docker-compose.traefik.yml | 50 +++++++++++++++++------------------ docker-compose.yml | 54 +++++++++++++++++++------------------- 3 files changed, 79 insertions(+), 79 deletions(-) diff --git a/docker-compose.certbot.yml b/docker-compose.certbot.yml index 69f5486..4dbabf0 100644 --- a/docker-compose.certbot.yml +++ b/docker-compose.certbot.yml @@ -19,40 +19,40 @@ services: - redis - simple-certbot volumes: - - ${MYDATA}/sharelatex:/var/lib/sharelatex + - ${MYDATA}/sharelatex:/var/lib/overleaf - ${MYDATA}/letsencrypt:/etc/letsencrypt - ${MYDATA}/letsencrypt/live/${MYDOMAIN}/:/etc/letsencrypt/certs/domain environment: - SHARELATEX_APP_NAME: Overleaf - SHARELATEX_MONGO_URL: mongodb://mongo/sharelatex - SHARELATEX_SITE_URL: https://${MYDOMAIN} - SHARELATEX_NAV_TITLE: Overleaf - run by ${MYDOMAIN} - #SHARELATEX_HEADER_IMAGE_URL: https://${MYDOMAIN}/logo.svg - SHARELATEX_ADMIN_EMAIL: ${MYMAIL} - SHARELATEX_LEFT_FOOTER: '[{"text": "Powered by ShareLaTeX 2016"} ]' - SHARELATEX_RIGHT_FOOTER: '[{"text": "LDAP Overleaf (beta)"} ]' - SHARELATEX_EMAIL_FROM_ADDRESS: "noreply@${MYDOMAIN}" - # SHARELATEX_EMAIL_AWS_SES_ACCESS_KEY_ID: - # SHARELATEX_EMAIL_AWS_SES_SECRET_KEY: - SHARELATEX_EMAIL_SMTP_HOST: smtp.${MYDOMAIN} - SHARELATEX_EMAIL_SMTP_PORT: 587 - SHARELATEX_EMAIL_SMTP_SECURE: "false" - # SHARELATEX_EMAIL_SMTP_USER: - # SHARELATEX_EMAIL_SMTP_PASS: - # SHARELATEX_EMAIL_SMTP_TLS_REJECT_UNAUTH: true - # SHARELATEX_EMAIL_SMTP_IGNORE_TLS: false - SHARELATEX_CUSTOM_EMAIL_FOOTER: "This system is run by ${MYDOMAIN} - please contact ${MYMAIL} if you experience any issues." + OVERLEAF_APP_NAME: Overleaf + OVERLEAF_MONGO_URL: mongodb://mongo/sharelatex + OVERLEAF_SITE_URL: https://${MYDOMAIN} + OVERLEAF_NAV_TITLE: Overleaf - run by ${MYDOMAIN} + #OVERLEAF_HEADER_IMAGE_URL: https://${MYDOMAIN}/logo.svg + OVERLEAF_ADMIN_EMAIL: ${MYMAIL} + OVERLEAF_LEFT_FOOTER: '[{"text": "Powered by ShareLaTeX 2016"} ]' + OVERLEAF_RIGHT_FOOTER: '[{"text": "LDAP Overleaf (beta)"} ]' + OVERLEAF_EMAIL_FROM_ADDRESS: "noreply@${MYDOMAIN}" + # OVERLEAF_EMAIL_AWS_SES_ACCESS_KEY_ID: + # OVERLEAF_EMAIL_AWS_SES_SECRET_KEY: + OVERLEAF_EMAIL_SMTP_HOST: smtp.${MYDOMAIN} + OVERLEAF_EMAIL_SMTP_PORT: 587 + OVERLEAF_EMAIL_SMTP_SECURE: "false" + # OVERLEAF_EMAIL_SMTP_USER: + # OVERLEAF_EMAIL_SMTP_PASS: + # OVERLEAF_EMAIL_SMTP_TLS_REJECT_UNAUTH: true + # OVERLEAF_EMAIL_SMTP_IGNORE_TLS: false + OVERLEAF_CUSTOM_EMAIL_FOOTER: "This system is run by ${MYDOMAIN} - please contact ${MYMAIL} if you experience any issues." # make public links accessible w/o login (link sharing issue) # https://github.com/overleaf/docker-image/issues/66 # https://github.com/overleaf/overleaf/issues/628 # https://github.com/overleaf/web/issues/367 # Fixed in 2.0.2 (Release date: 2019-11-26) - SHARELATEX_ALLOW_PUBLIC_ACCESS: "true" - SHARELATEX_ALLOW_ANONYMOUS_READ_AND_WRITE_SHARING: "true" + OVERLEAF_ALLOW_PUBLIC_ACCESS: "true" + OVERLEAF_ALLOW_ANONYMOUS_READ_AND_WRITE_SHARING: "true" - SHARELATEX_SECURE_COOKIE: "true" - SHARELATEX_BEHIND_PROXY: "true" + OVERLEAF_SECURE_COOKIE: "true" + OVERLEAF_BEHIND_PROXY: "true" LDAP_SERVER: ldaps://LDAPSERVER:636 LDAP_BASE: ou=people,dc=DOMAIN,dc=TLD @@ -100,7 +100,7 @@ services: # Same property, unfortunately with different names in # different locations - SHARELATEX_REDIS_HOST: redis + OVERLEAF_REDIS_HOST: redis REDIS_HOST: redis REDIS_PORT: 6379 @@ -111,7 +111,7 @@ services: mongo: restart: always - image: mongo:4.4 + image: mongo:5.0 container_name: mongo expose: - 27017 @@ -126,7 +126,7 @@ services: # See also: https://github.com/overleaf/overleaf/issues/1120 mongoinit: - image: mongo:4.4 + image: mongo:5.0 # this container will exit after executing the command restart: "no" depends_on: diff --git a/docker-compose.traefik.yml b/docker-compose.traefik.yml index 69fd810..be25d34 100644 --- a/docker-compose.traefik.yml +++ b/docker-compose.traefik.yml @@ -80,7 +80,7 @@ services: - mongo - redis volumes: - - ${MYDATA}/sharelatex:/var/lib/sharelatex + - ${MYDATA}/sharelatex:/var/lib/overleaf - ${MYDATA}/letsencrypt:/etc/letsencrypt:ro # - ${MYDATA}/letsencrypt/live/${MYDOMAIN}/:/etc/letsencrypt/certs/domain labels: @@ -106,34 +106,34 @@ services: - "traefik.http.services.sharel.loadbalancer.sticky.cookie.samesite=io" environment: - SHARELATEX_APP_NAME: Overleaf - SHARELATEX_MONGO_URL: mongodb://mongo/sharelatex - SHARELATEX_SITE_URL: https://${MYDOMAIN} - SHARELATEX_NAV_TITLE: Overleaf - run by ${MYDOMAIN} - #SHARELATEX_HEADER_IMAGE_URL: https://${MYDOMAIN}/logo.svg - SHARELATEX_ADMIN_EMAIL: ${MYMAIL} - SHARELATEX_LEFT_FOOTER: '[{"text": "Powered by ShareLaTeX 2016"} ]' - SHARELATEX_RIGHT_FOOTER: '[{"text": "LDAP Overleaf (beta)"} ]' - SHARELATEX_EMAIL_FROM_ADDRESS: "noreply@${MYDOMAIN}" - SHARELATEX_EMAIL_SMTP_HOST: smtp.${MYDOMAIN} - SHARELATEX_EMAIL_SMTP_PORT: 587 - SHARELATEX_EMAIL_SMTP_SECURE: "false" - # SHARELATEX_EMAIL_SMTP_USER: - # SHARELATEX_EMAIL_SMTP_PASS: - # SHARELATEX_EMAIL_SMTP_TLS_REJECT_UNAUTH: true - # SHARELATEX_EMAIL_SMTP_IGNORE_TLS: false - SHARELATEX_CUSTOM_EMAIL_FOOTER: "This system is run by ${MYDOMAIN} - please contact ${MYMAIL} if you experience any issues." + OVERLEAF_APP_NAME: Overleaf + OVERLEAF_MONGO_URL: mongodb://mongo/sharelatex + OVERLEAF_SITE_URL: https://${MYDOMAIN} + OVERLEAF_NAV_TITLE: Overleaf - run by ${MYDOMAIN} + #OVERLEAF_HEADER_IMAGE_URL: https://${MYDOMAIN}/logo.svg + OVERLEAF_ADMIN_EMAIL: ${MYMAIL} + OVERLEAF_LEFT_FOOTER: '[{"text": "Powered by ShareLaTeX 2016"} ]' + OVERLEAF_RIGHT_FOOTER: '[{"text": "LDAP Overleaf (beta)"} ]' + OVERLEAF_EMAIL_FROM_ADDRESS: "noreply@${MYDOMAIN}" + OVERLEAF_EMAIL_SMTP_HOST: smtp.${MYDOMAIN} + OVERLEAF_EMAIL_SMTP_PORT: 587 + OVERLEAF_EMAIL_SMTP_SECURE: "false" + # OVERLEAF_EMAIL_SMTP_USER: + # OVERLEAF_EMAIL_SMTP_PASS: + # OVERLEAF_EMAIL_SMTP_TLS_REJECT_UNAUTH: true + # OVERLEAF_EMAIL_SMTP_IGNORE_TLS: false + OVERLEAF_CUSTOM_EMAIL_FOOTER: "This system is run by ${MYDOMAIN} - please contact ${MYMAIL} if you experience any issues." # make public links accessible w/o login (link sharing issue) # https://github.com/overleaf/docker-image/issues/66 # https://github.com/overleaf/overleaf/issues/628 # https://github.com/overleaf/web/issues/367 # Fixed in 2.0.2 (Release date: 2019-11-26) - SHARELATEX_ALLOW_PUBLIC_ACCESS: "true" - SHARELATEX_ALLOW_ANONYMOUS_READ_AND_WRITE_SHARING: "true" + OVERLEAF_ALLOW_PUBLIC_ACCESS: "true" + OVERLEAF_ALLOW_ANONYMOUS_READ_AND_WRITE_SHARING: "true" - SHARELATEX_SECURE_COOKIE: "true" - SHARELATEX_BEHIND_PROXY: "true" + OVERLEAF_SECURE_COOKIE: "true" + OVERLEAF_BEHIND_PROXY: "true" LDAP_SERVER: ldaps://LDAPSERVER:636 LDAP_BASE: ou=people,dc=DOMAIN,dc=TLD @@ -181,7 +181,7 @@ services: # Same property, unfortunately with different names in # different locations - SHARELATEX_REDIS_HOST: redis + OVERLEAF_REDIS_HOST: redis REDIS_HOST: redis REDIS_PORT: 6379 @@ -192,7 +192,7 @@ services: mongo: restart: always - image: mongo:4.4 + image: mongo:5.0 container_name: mongo expose: - 27017 @@ -215,7 +215,7 @@ services: # See also: https://github.com/overleaf/overleaf/issues/1120 mongoinit: - image: mongo:4.4 + image: mongo:5.0 # this container will exit after executing the command restart: "no" depends_on: diff --git a/docker-compose.yml b/docker-compose.yml index 811dea9..d2fda22 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -16,41 +16,41 @@ services: - mongo - redis volumes: - - ${MYDATA}/sharelatex:/var/lib/sharelatex + - ${MYDATA}/sharelatex:/var/lib/overleaf - ${MYDATA}/letsencrypt:/etc/letsencrypt - ${MYDATA}/letsencrypt/live/${MYDOMAIN}/:/etc/letsencrypt/certs/domain environment: - SHARELATEX_APP_NAME: Overleaf - SHARELATEX_MONGO_URL: mongodb://mongo/sharelatex - SHARELATEX_SITE_URL: https://${MYDOMAIN} - SHARELATEX_NAV_TITLE: Overleaf - run by ${MYDOMAIN} - #SHARELATEX_HEADER_IMAGE_URL: https://${MYDOMAIN}/logo.svg - SHARELATEX_ADMIN_EMAIL: ${MYMAIL} - SHARELATEX_LEFT_FOOTER: '[{"text": "Powered by ShareLaTeX 2016"} ]' - SHARELATEX_RIGHT_FOOTER: '[{"text": "LDAP Overleaf (beta)"} ]' - SHARELATEX_EMAIL_FROM_ADDRESS: "noreply@${MYDOMAIN}" - # SHARELATEX_EMAIL_AWS_SES_ACCESS_KEY_ID: - # SHARELATEX_EMAIL_AWS_SES_SECRET_KEY: - SHARELATEX_EMAIL_SMTP_HOST: smtp.${MYDOMAIN} - SHARELATEX_EMAIL_SMTP_PORT: 587 - SHARELATEX_EMAIL_SMTP_SECURE: "false" - # SHARELATEX_EMAIL_SMTP_USER: - # SHARELATEX_EMAIL_SMTP_PASS: - # SHARELATEX_EMAIL_SMTP_TLS_REJECT_UNAUTH: true - # SHARELATEX_EMAIL_SMTP_IGNORE_TLS: false - SHARELATEX_CUSTOM_EMAIL_FOOTER: "This system is run by ${MYDOMAIN} - please contact ${MYMAIL} if you experience any issues." + OVERLEAF_APP_NAME: Overleaf + OVERLEAF_MONGO_URL: mongodb://mongo/sharelatex + OVERLEAF_SITE_URL: https://${MYDOMAIN} + OVERLEAF_NAV_TITLE: Overleaf - run by ${MYDOMAIN} + #OVERLEAF_HEADER_IMAGE_URL: https://${MYDOMAIN}/logo.svg + OVERLEAF_ADMIN_EMAIL: ${MYMAIL} + OVERLEAF_LEFT_FOOTER: '[{"text": "Powered by ShareLaTeX 2016"} ]' + OVERLEAF_RIGHT_FOOTER: '[{"text": "LDAP Overleaf (beta)"} ]' + OVERLEAF_EMAIL_FROM_ADDRESS: "noreply@${MYDOMAIN}" + # OVERLEAF_EMAIL_AWS_SES_ACCESS_KEY_ID: + # OVERLEAF_EMAIL_AWS_SES_SECRET_KEY: + OVERLEAF_EMAIL_SMTP_HOST: smtp.${MYDOMAIN} + OVERLEAF_EMAIL_SMTP_PORT: 587 + OVERLEAF_EMAIL_SMTP_SECURE: "false" + # OVERLEAF_EMAIL_SMTP_USER: + # OVERLEAF_EMAIL_SMTP_PASS: + # OVERLEAF_EMAIL_SMTP_TLS_REJECT_UNAUTH: true + # OVERLEAF_EMAIL_SMTP_IGNORE_TLS: false + OVERLEAF_CUSTOM_EMAIL_FOOTER: "This system is run by ${MYDOMAIN} - please contact ${MYMAIL} if you experience any issues." # make public links accessible w/o login (link sharing issue) # https://github.com/overleaf/docker-image/issues/66 # https://github.com/overleaf/overleaf/issues/628 # https://github.com/overleaf/web/issues/367 # Fixed in 2.0.2 (Release date: 2019-11-26) - SHARELATEX_ALLOW_PUBLIC_ACCESS: "true" - SHARELATEX_ALLOW_ANONYMOUS_READ_AND_WRITE_SHARING: "true" + OVERLEAF_ALLOW_PUBLIC_ACCESS: "true" + OVERLEAF_ALLOW_ANONYMOUS_READ_AND_WRITE_SHARING: "true" # Uncomment the following line to enable secure cookies if you are using SSL - # SHARELATEX_SECURE_COOKIE: "true" - # SHARELATEX_BEHIND_PROXY: "true" + # OVERLEAF_SECURE_COOKIE: "true" + # OVERLEAF_BEHIND_PROXY: "true" LDAP_SERVER: ldaps://LDAPSERVER:636 LDAP_BASE: ou=people,dc=DOMAIN,dc=TLD @@ -98,7 +98,7 @@ services: # Same property, unfortunately with different names in # different locations - SHARELATEX_REDIS_HOST: redis + OVERLEAF_REDIS_HOST: redis REDIS_HOST: redis REDIS_PORT: 6379 @@ -109,7 +109,7 @@ services: mongo: restart: always - image: mongo:4.4 + image: mongo:5.0 container_name: mongo expose: - 27017 @@ -124,7 +124,7 @@ services: # See also: https://github.com/overleaf/overleaf/issues/1120 mongoinit: - image: mongo:4.4 + image: mongo:5.0 # this container will exit after executing the command restart: "no" depends_on: From 130586cfd2309c5f05fefd249436d7cbd17f85ec Mon Sep 17 00:00:00 2001 From: armanddidierjean <95971503+armanddidierjean@users.noreply.github.com> Date: Sun, 29 Sep 2024 17:21:33 +0200 Subject: [PATCH 2/4] Update diff files for Overleaf 5.1.1 --- .../AuthenticationController.js.diff | 25 +- .../AuthenticationManager.js.diff | 452 ++++++++---------- .../sharelatex_diff/ContactController.js.diff | 126 +++-- .../sharelatex_diff/admin-index.pug.diff | 15 +- .../sharelatex_diff/admin-sysadmin.pug.diff | 131 ++--- .../sharelatex_diff/login.pug.diff | 15 +- .../sharelatex_diff/navbar-marketing.pug.diff | 2 +- ....diff => navbar-website-redesign.pug.diff} | 247 +++++----- .../sharelatex_diff/router.js.diff | 2 +- .../sharelatex_diff/settings.pug.diff | 86 ++-- scripts/extract_files.sh | 4 +- 11 files changed, 526 insertions(+), 579 deletions(-) rename ldap-overleaf-sl/sharelatex_diff/{navbar.pug.diff => navbar-website-redesign.pug.diff} (56%) diff --git a/ldap-overleaf-sl/sharelatex_diff/AuthenticationController.js.diff b/ldap-overleaf-sl/sharelatex_diff/AuthenticationController.js.diff index 5705a43..67b8d43 100644 --- a/ldap-overleaf-sl/sharelatex_diff/AuthenticationController.js.diff +++ b/ldap-overleaf-sl/sharelatex_diff/AuthenticationController.js.diff @@ -1,4 +1,6 @@ -268a268,364 +326a327,346 +> }, +> > > // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>> > oauth2Redirect(req, res, next) { @@ -7,7 +9,7 @@ > const state = new Array(6).fill(0).map(() => characters.charAt(Math.floor(Math.random() * characters.length))).join("") > req.session.oauth2State = state > -> const redirectURI = encodeURIComponent(`${process.env.SHARELATEX_SITE_URL}/oauth/callback`) +> const redirectURI = encodeURIComponent(`${process.env.OVERLEAF_SITE_URL}/oauth/callback`) > const authURL = ( > process.env.OAUTH2_AUTHORIZATION_URL > + `?response_type=code` @@ -17,8 +19,7 @@ > + `&state=${state}` > ) > res.redirect(authURL) -> }, -> +328a349,419 > async oauth2Callback(req, res, next) { > console.log(`OAuth, receive code ${req.query.code} and state ${req.query.state}`) > const saveState = req.session.oauth2State @@ -34,7 +35,7 @@ > client_id: process.env.OAUTH2_CLIENT_ID, > client_secret: process.env.OAUTH2_CLIENT_SECRET, > code: req.query.code, -> redirect_uri: `${process.env.SHARELATEX_SITE_URL}/oauth/callback`, +> redirect_uri: `${process.env.OVERLEAF_SITE_URL}/oauth/callback`, > } > const body = contentType === 'application/json' > ? JSON.stringify(bodyParams) @@ -73,26 +74,20 @@ > : false > > const query = { email } -> const callback = (error, user) => { -> if (error) { -> res.json({message: error}); -> } else { -> console.log("OAuth user", JSON.stringify(user)); -> AuthenticationController.finishLogin(user, req, res, next); -> } -> } -> AuthenticationManager.createIfNotFoundAndLogin( +> +> const { user } = await AuthenticationManager.createIfNotFoundAndLogin( > query, -> callback, > uid, > firstname, > lastname, > email, > isAdmin > ) +> AuthenticationController.finishLogin(user, req, res, next); > } catch(e) { > res.redirect("/login") > console.error("Fails to access by OAuth2: " + String(e)) +> console.error(e); > } > }, > // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<< diff --git a/ldap-overleaf-sl/sharelatex_diff/AuthenticationManager.js.diff b/ldap-overleaf-sl/sharelatex_diff/AuthenticationManager.js.diff index f732928..8ca95bb 100644 --- a/ldap-overleaf-sl/sharelatex_diff/AuthenticationManager.js.diff +++ b/ldap-overleaf-sl/sharelatex_diff/AuthenticationManager.js.diff @@ -1,68 +1,56 @@ -19a20,25 +22a23,29 > // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>> -> const fs = require("fs") -> const { Client } = require("ldapts") -> const ldapEscape = require("ldap-escape") +> // TODO(ldap) +> // const fs = require("fs") +> // const { Client } = require("ldapts") +> // const ldapEscape = require("ldap-escape") > // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<< > -120a127,136 -> // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>> -> _checkUserPassword2(query, password, callback) { +116a124,133 +> // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>> +> async _checkUserPassword2(query, password) { > // leave original _checkUserPassword untouched, because it will be called by > // setUserPasswordInV2 (e.g. UserRegistrationHandler.js ) -> User.findOne(query, (error, user) => { -> AuthenticationManager.authUserObj(error, user, query, password, callback) -> }) +> const user = await User.findOne(query).exec() +> +> return await AuthenticationManager.authUserObj(user, query, password) > }, -> // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<< +> // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<< > -126c142,144 -< AuthenticationManager._checkUserPassword( +118c135,137 +< const { user, match } = await AuthenticationManager._checkUserPassword( --- -> // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>> -> AuthenticationManager._checkUserPassword2( -> // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<< -190a209,488 +> // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>> +> const { user, match } = await AuthenticationManager._checkUserPassword2( +> // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<< +179a199,445 > -> // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>> -> /** -> * login with any password -> */ -> login(user, password, callback) { -> callback(null, user, true) -> }, -> -> createIfNotFoundAndLogin( +> // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>> +> +> async 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 -> ) -> }) +> const user = await User.findOne(query).exec() +> +> return await AuthenticationManager.createIfNotExistAndLogin( +> query, +> user, +> uid, +> firstname, +> lastname, +> mail, +> isAdmin +> ) > }, > -> createIfNotExistAndLogin( +> async createIfNotExistAndLogin( > query, > user, -> callback, > uid, > firstname, > lastname, @@ -75,231 +63,213 @@ > console.log('Creating User', { mail, uid, firstname, lastname, isAdmin, pass }) > > const userRegHand = require("../User/UserRegistrationHandler.js") -> userRegHand.registerNewUser( +> const createdUser = await userRegHand.promises.registerNewUser( > { > email: mail, > first_name: firstname, > last_name: lastname, > password: pass, -> }, -> function (error, user, setNewPasswordUrl) { -> if (error) { -> console.log(error) -> } -> user.email = mail -> user.isAdmin = isAdmin -> user.emails[0].confirmedAt = Date.now() -> user.save() -> //console.log('user %s added to local library: ', mail) -> User.findOne(query, (error, user) => { -> if (error) { -> console.log(error) -> } -> if (user && user.hashedPassword) { -> AuthenticationManager.login(user, "randomPass", callback) -> } -> }) > } -> ) // end register user +> ) // end register user +> return { user: createdUser, match: true } > } else { > console.log('User exists', { mail }) -> AuthenticationManager.login(user, "randomPass", callback) +> return { user: user, match: true } > } > }, > -> authUserObj(error, user, query, password, callback) { +> async authUserObj(user, query, password) { > if (process.env.ALLOW_EMAIL_LOGIN && user && user.hashedPassword) { > console.log("email login for existing user " + query.email) > // check passwd against local db -> bcrypt.compare(password, user.hashedPassword, function (error, match) { +> const match = await bcrypt.compare(password, user.hashedPassword) > if (match) { > console.log("Local user password match") > _metricsForSuccessfulPasswordMatch(password) -> //callback(null, user, match) -> AuthenticationManager.login(user, "randomPass", callback) +> return { user, match } > } else { > console.log("Local user password mismatch, trying LDAP") > // check passwd against ldap -> AuthenticationManager.ldapAuth( -> query, -> password, -> AuthenticationManager.createIfNotExistAndLogin, -> callback, -> user -> ) +> // TODO(ldap) +> return { user: null, match: null } +> // return AuthenticationManager.ldapAuth( +> // query, +> // password, +> // AuthenticationManager.createIfNotExistAndLogin, +> // user +> // ) > } -> }) +> > } else { > // No local passwd check user has to be in ldap and use ldap credentials -> AuthenticationManager.ldapAuth( -> query, -> password, -> AuthenticationManager.createIfNotExistAndLogin, -> callback, -> user -> ) +> // TODO(ldap) +> return { user: null, match: null } +> // return AuthenticationManager.ldapAuth( +> // query, +> // password, +> // AuthenticationManager.createIfNotExistAndLogin, +> // user +> // ) > } -> return null > }, > -> async ldapAuth( -> query, -> password, -> onSuccessCreateUserIfNotExistent, -> callback, -> user -> ) { -> const client = fs.existsSync(process.env.LDAP_SERVER_CACERT) -> ? new Client({ -> url: process.env.LDAP_SERVER, -> tlsOptions: { -> ca: [fs.readFileSync(process.env.LDAP_SERVER_CACERT)], -> }, -> }) -> : new Client({ -> url: process.env.LDAP_SERVER, -> }) +> // TODO(ldap) +> // async ldapAuth( +> // query, +> // password, +> // onSuccessCreateUserIfNotExistent, +> // user +> // ) { +> // const client = fs.existsSync(process.env.LDAP_SERVER_CACERT) +> // ? new Client({ +> // url: process.env.LDAP_SERVER, +> // tlsOptions: { +> // ca: [fs.readFileSync(process.env.LDAP_SERVER_CACERT)], +> // }, +> // }) +> // : new Client({ +> // url: process.env.LDAP_SERVER, +> // }) > -> const ldap_reader = process.env.LDAP_BIND_USER -> const ldap_reader_pass = process.env.LDAP_BIND_PW -> const ldap_base = process.env.LDAP_BASE +> // const ldap_reader = process.env.LDAP_BIND_USER +> // const ldap_reader_pass = process.env.LDAP_BIND_PW +> // const ldap_base = process.env.LDAP_BASE > -> var mail = query.email -> var uid = query.email.split("@")[0] -> var firstname = "" -> var lastname = "" -> var isAdmin = false -> var userDn = "" +> // var mail = query.email +> // var uid = query.email.split("@")[0] +> // var firstname = "" +> // var lastname = "" +> // var isAdmin = false +> // var userDn = "" > -> //replace all appearences of %u with uid and all %m with mail: -> const replacerUid = new RegExp("%u", "g") -> const replacerMail = new RegExp("%m", "g") -> const filterstr = process.env.LDAP_USER_FILTER.replace( -> replacerUid, -> ldapEscape.filter`${uid}` -> ).replace(replacerMail, ldapEscape.filter`${mail}`) //replace all appearances -> // check bind -> try { -> if (process.env.LDAP_BINDDN) { -> //try to bind directly with the user trying to log in -> userDn = process.env.LDAP_BINDDN.replace( -> replacerUid, -> ldapEscape.filter`${uid}` -> ).replace(replacerMail, ldapEscape.filter`${mail}`) -> await client.bind(userDn, password) -> } else { -> // use fixed bind user -> await client.bind(ldap_reader, ldap_reader_pass) -> } -> } catch (ex) { -> if (process.env.LDAP_BINDDN) { -> console.log("Could not bind user: " + userDn) -> } else { -> console.log( -> "Could not bind LDAP reader: " + ldap_reader + " err: " + String(ex) -> ) -> } -> return callback(null, null) -> } +> // //replace all appearences of %u with uid and all %m with mail: +> // const replacerUid = new RegExp("%u", "g") +> // const replacerMail = new RegExp("%m", "g") +> // const filterstr = process.env.LDAP_USER_FILTER.replace( +> // replacerUid, +> // ldapEscape.filter`${uid}` +> // ).replace(replacerMail, ldapEscape.filter`${mail}`) //replace all appearances +> // // check bind +> // try { +> // if (process.env.LDAP_BINDDN) { +> // //try to bind directly with the user trying to log in +> // userDn = process.env.LDAP_BINDDN.replace( +> // replacerUid, +> // ldapEscape.filter`${uid}` +> // ).replace(replacerMail, ldapEscape.filter`${mail}`) +> // await client.bind(userDn, password) +> // } else { +> // // use fixed bind user +> // await client.bind(ldap_reader, ldap_reader_pass) +> // } +> // } catch (ex) { +> // if (process.env.LDAP_BINDDN) { +> // console.log("Could not bind user: " + userDn) +> // } else { +> // console.log( +> // "Could not bind LDAP reader: " + ldap_reader + " err: " + String(ex) +> // ) +> // } +> // return { user: null, match: null } +> // } > -> // get user data -> try { -> const { searchEntries, searchRef } = await client.search(ldap_base, { -> scope: "sub", -> filter: filterstr, -> }) -> await searchEntries -> console.log(JSON.stringify(searchEntries)) -> if (searchEntries[0]) { -> mail = searchEntries[0].mail -> uid = searchEntries[0].uid -> firstname = searchEntries[0].givenName -> lastname = searchEntries[0].sn -> if (!process.env.LDAP_BINDDN) { -> //dn is already correctly assembled -> userDn = searchEntries[0].dn -> } -> console.log( -> `Found user: ${mail} Name: ${firstname} ${lastname} DN: ${userDn}` -> ) -> } -> } catch (ex) { -> console.log( -> "An Error occured while getting user data during ldapsearch: " + -> String(ex) -> ) -> await client.unbind() -> return callback(null, null) -> } +> // // get user data +> // try { +> // const { searchEntries, searchRef } = await client.search(ldap_base, { +> // scope: "sub", +> // filter: filterstr, +> // }) +> // await searchEntries +> // console.log(JSON.stringify(searchEntries)) +> // if (searchEntries[0]) { +> // mail = searchEntries[0].mail +> // uid = searchEntries[0].uid +> // firstname = searchEntries[0].givenName +> // lastname = searchEntries[0].sn +> // if (!process.env.LDAP_BINDDN) { +> // //dn is already correctly assembled +> // userDn = searchEntries[0].dn +> // } +> // console.log( +> // `Found user: ${mail} Name: ${firstname} ${lastname} DN: ${userDn}` +> // ) +> // } +> // } catch (ex) { +> // console.log( +> // "An Error occured while getting user data during ldapsearch: " + +> // String(ex) +> // ) +> // await client.unbind() +> // return { user: null, match: null } +> // } > -> try { -> // if admin filter is set - only set admin for user in ldap group -> // does not matter - admin is deactivated: managed through ldap -> if (process.env.LDAP_ADMIN_GROUP_FILTER) { -> const adminfilter = process.env.LDAP_ADMIN_GROUP_FILTER.replace( -> replacerUid, -> ldapEscape.filter`${uid}` -> ).replace(replacerMail, ldapEscape.filter`${mail}`) -> adminEntry = await client.search(ldap_base, { -> scope: "sub", -> filter: adminfilter, -> }) -> await adminEntry -> //console.log('Admin Search response:' + JSON.stringify(adminEntry.searchEntries)) -> if (adminEntry.searchEntries[0]) { -> console.log("is Admin") -> isAdmin = true -> } -> } -> } catch (ex) { -> console.log( -> "An Error occured while checking for admin rights - setting admin rights to false: " + -> String(ex) -> ) -> isAdmin = false -> } finally { -> await client.unbind() -> } -> if (mail == "" || userDn == "") { -> console.log( -> "Mail / userDn not set - exit. This should not happen - please set mail-entry in ldap." -> ) -> return callback(null, null) -> } +> // try { +> // // if admin filter is set - only set admin for user in ldap group +> // // does not matter - admin is deactivated: managed through ldap +> // if (process.env.LDAP_ADMIN_GROUP_FILTER) { +> // const adminfilter = process.env.LDAP_ADMIN_GROUP_FILTER.replace( +> // replacerUid, +> // ldapEscape.filter`${uid}` +> // ).replace(replacerMail, ldapEscape.filter`${mail}`) +> // let adminEntry = await client.search(ldap_base, { +> // scope: "sub", +> // filter: adminfilter, +> // }) +> // await adminEntry +> // //console.log('Admin Search response:' + JSON.stringify(adminEntry.searchEntries)) +> // if (adminEntry.searchEntries[0]) { +> // console.log("is Admin") +> // isAdmin = true +> // } +> // } +> // } catch (ex) { +> // console.log( +> // "An Error occured while checking for admin rights - setting admin rights to false: " + +> // String(ex) +> // ) +> // isAdmin = false +> // } finally { +> // await client.unbind() +> // } +> // if (mail == "" || userDn == "") { +> // console.log( +> // "Mail / userDn not set - exit. This should not happen - please set mail-entry in ldap." +> // ) +> // return { user: null, match: null } +> // } > -> if (!process.env.BINDDN) { -> //since we used a fixed bind user to obtain the correct userDn we need to bind again to authenticate -> try { -> await client.bind(userDn, password) -> } catch (ex) { -> console.log("Could not bind User: " + userDn + " err: " + String(ex)) -> return callback(null, null) -> } finally { -> await client.unbind() -> } -> } -> //console.log('Logging in user: ' + mail + ' Name: ' + firstname + ' ' + lastname + ' isAdmin: ' + String(isAdmin)) -> // we are authenticated now let's set the query to the correct mail from ldap -> query.email = mail -> User.findOne(query, (error, user) => { -> if (error) { -> console.log(error) -> } -> if (user && user.hashedPassword) { -> //console.log('******************** LOGIN ******************') -> AuthenticationManager.login(user, "randomPass", callback) -> } else { -> onSuccessCreateUserIfNotExistent( -> query, -> user, -> callback, -> uid, -> firstname, -> lastname, -> mail, -> isAdmin -> ) -> } -> }) -> }, -> // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<< +> // if (!process.env.BINDDN) { +> // //since we used a fixed bind user to obtain the correct userDn we need to bind again to authenticate +> // try { +> // await client.bind(userDn, password) +> // } catch (ex) { +> // console.log("Could not bind User: " + userDn + " err: " + String(ex)) +> // return { user: null, match: null } +> // } finally { +> // await client.unbind() +> // } +> // } +> // //console.log('Logging in user: ' + mail + ' Name: ' + firstname + ' ' + lastname + ' isAdmin: ' + String(isAdmin)) +> // // we are authenticated now let's set the query to the correct mail from ldap +> // query.email = mail +> // const createdUser = await User.findOne(query).exec() +> +> // if (createdUser && createdUser.hashedPassword) { +> // //console.log('******************** LOGIN ******************') +> // return { user: createdUser, match: true } +> // } else { +> // return await onSuccessCreateUserIfNotExistent( +> // query, +> // createdUser, +> // uid, +> // firstname, +> // lastname, +> // mail, +> // isAdmin +> // ) +> // } +> +> // }, +> // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<< +475a742 +> createIfNotFoundAndLogin: AuthenticationManager.createIfNotFoundAndLogin, diff --git a/ldap-overleaf-sl/sharelatex_diff/ContactController.js.diff b/ldap-overleaf-sl/sharelatex_diff/ContactController.js.diff index 78b9c6e..0fd3e62 100644 --- a/ldap-overleaf-sl/sharelatex_diff/ContactController.js.diff +++ b/ldap-overleaf-sl/sharelatex_diff/ContactController.js.diff @@ -1,76 +1,70 @@ -6a7,10 +6a7,11 > // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>> -> const { Client } = require('ldapts') +> // TODO(ldap) +> //const { Client } = require('ldapts') > // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<< > -34,35c38,39 -< const contactId = contactIds[i] -< positions[contactId] = i ---- -> const contact_id = contactIds[i] -> positions[contact_id] = i -42c46,51 -< contacts = contacts.filter(c => !c.holdingAccount) ---- -> contacts = contacts.filter((c) => !c.holdingAccount) -> +43a49,54 > // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>> -> const ldapcontacts = getLdapContacts(contacts) -> contacts.push(ldapcontacts) +> // TODO(ldap) +> //const ldapcontacts = getLdapContacts(contacts) +> //contacts.push(ldapcontacts) > // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<< -56a66,120 > +57a69,124 > // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>> -> async function getLdapContacts(contacts) { -> if ( -> process.env.LDAP_CONTACTS === undefined || -> !(process.env.LDAP_CONTACTS.toLowerCase() === 'true') -> ) { -> return contacts -> } -> const client = new Client({ -> url: process.env.LDAP_SERVER, -> }) +> // TODO(ldap) +> // async function getLdapContacts(contacts) { +> // if ( +> // process.env.LDAP_CONTACTS === undefined || +> // !(process.env.LDAP_CONTACTS.toLowerCase() === 'true') +> // ) { +> // return contacts +> // } +> // const client = new Client({ +> // url: process.env.LDAP_SERVER, +> // }) > -> // if we need a ldap user try to bind -> if (process.env.LDAP_BIND_USER) { -> try { -> await client.bind(process.env.LDAP_BIND_USER, process.env.LDAP_BIND_PW) -> } catch (ex) { -> console.log('Could not bind LDAP reader user: ' + String(ex)) -> } -> } +> // // if we need a ldap user try to bind +> // if (process.env.LDAP_BIND_USER) { +> // try { +> // await client.bind(process.env.LDAP_BIND_USER, process.env.LDAP_BIND_PW) +> // } catch (ex) { +> // console.log('Could not bind LDAP reader user: ' + String(ex)) +> // } +> // } > -> const ldap_base = process.env.LDAP_BASE -> // get user data -> try { -> // if you need an client.bind do it here. -> const { searchEntries, searchReferences } = await client.search(ldap_base, { -> scope: 'sub', -> filter: process.env.LDAP_CONTACT_FILTER, -> }) -> await searchEntries -> for (var i = 0; i < searchEntries.length; i++) { -> var entry = new Map() -> var obj = searchEntries[i] -> entry['_id'] = undefined -> entry['email'] = obj['mail'] -> entry['first_name'] = obj['givenName'] -> entry['last_name'] = obj['sn'] -> entry['type'] = 'user' -> // Only add to contacts if entry is not there. -> if (contacts.indexOf(entry) === -1) { -> contacts.push(entry) -> } -> } -> } catch (ex) { -> console.log(String(ex)) -> } finally { -> // console.log(JSON.stringify(contacts)) -> // even if we did not use bind - the constructor of -> // new Client() opens a socket to the ldap server -> client.unbind() -> return contacts -> } -> } +> // const ldap_base = process.env.LDAP_BASE +> // // get user data +> // try { +> // // if you need an client.bind do it here. +> // const { searchEntries, searchReferences } = await client.search(ldap_base, { +> // scope: 'sub', +> // filter: process.env.LDAP_CONTACT_FILTER, +> // }) +> // await searchEntries +> // for (var i = 0; i < searchEntries.length; i++) { +> // var entry = new Map() +> // var obj = searchEntries[i] +> // entry['_id'] = undefined +> // entry['email'] = obj['mail'] +> // entry['first_name'] = obj['givenName'] +> // entry['last_name'] = obj['sn'] +> // entry['type'] = 'user' +> // // Only add to contacts if entry is not there. +> // if (contacts.indexOf(entry) === -1) { +> // contacts.push(entry) +> // } +> // } +> // } catch (ex) { +> // console.log(String(ex)) +> // } finally { +> // // console.log(JSON.stringify(contacts)) +> // // even if we did not use bind - the constructor of +> // // new Client() opens a socket to the ldap server +> // client.unbind() +> // return contacts +> // } +> // } > // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<< +> diff --git a/ldap-overleaf-sl/sharelatex_diff/admin-index.pug.diff b/ldap-overleaf-sl/sharelatex_diff/admin-index.pug.diff index 5dc2488..ee2c9e9 100644 --- a/ldap-overleaf-sl/sharelatex_diff/admin-index.pug.diff +++ b/ldap-overleaf-sl/sharelatex_diff/admin-index.pug.diff @@ -13,15 +13,15 @@ < .row-spaced < ul < each agents, url in openSockets -< li(ng-non-bindable) #{url} - total : #{agents.length} +< li #{url} - total : #{agents.length} < ul < each agent in agents -< li(ng-non-bindable) #{agent} +< li #{agent} 53c39 < id='open-close-editor' --- > id='register-user' -55,74c41,42 +55,99c41,43 < if hasFeature('saas') < | The "Open/Close Editor" feature is not available in SAAS. < else @@ -42,10 +42,7 @@ < input(name="_csrf", type="hidden", value=csrfToken) < button.btn.btn-danger(type="submit") Reopen Editor < p.small Will reopen the editor after closing. ---- -> hr -> a(href="/admin/register") Register User -76,99d43 +< < if hasFeature('saas') < .tab-pane( < role="tabpanel" @@ -70,3 +67,7 @@ < input.form-control(type='text', name='user_id', placeholder='user_id', required) < .form-group < button.btn-primary.btn(type='submit') Poll +--- +> hr +> a(href="/admin/register") Register User +> diff --git a/ldap-overleaf-sl/sharelatex_diff/admin-sysadmin.pug.diff b/ldap-overleaf-sl/sharelatex_diff/admin-sysadmin.pug.diff index 598696c..2eb5ed1 100644 --- a/ldap-overleaf-sl/sharelatex_diff/admin-sysadmin.pug.diff +++ b/ldap-overleaf-sl/sharelatex_diff/admin-sysadmin.pug.diff @@ -7,12 +7,14 @@ < .card --- > .card(ng-controller="RegisterUsersController") -12,16c11,58 +12,18c11,25 < div(data-ol-bookmarkable-tabset) < ul.nav.nav-tabs(role="tablist") < +bookmarkable-tabset-header('system-messages', 'System Messages', true) < +bookmarkable-tabset-header('open-sockets', 'Open Sockets') < +bookmarkable-tabset-header('open-close-editor', 'Open/Close Editor') +< if hasFeature('saas') +< +bookmarkable-tabset-header('tpds', 'TPDS/Dropbox Management') --- > tabset(ng-cloak) > tab(heading="System Messages") @@ -29,57 +31,14 @@ > form(method='post', action='/admin/messages/clear') > input(name="_csrf", type="hidden", value=csrfToken) > button.btn.btn-danger(type="submit") Clear all messages -> -> -> tab(heading="Register non LDAP User") -> form.form -> .row -> .col-md-4.col-xs-8 -> input.form-control( -> name="email", -> type="text", -> placeholder="jane@example.com, joe@example.com", -> ng-model="inputs.emails", -> on-enter="registerUsers()" -> ) -> .col-md-8.col-xs-4 -> button.btn.btn-primary(ng-click="registerUsers()") #{translate("register")} -> -> .row-spaced(ng-show="error").ng-cloak.text-danger -> p Sorry, an error occured -> -> .row-spaced(ng-show="users.length > 0").ng-cloak.text-success -> p We've sent out welcome emails to the registered users. -> p You can also manually send them URLs below to allow them to reset their password and log in for the first time. -> p (Password reset tokens will expire after one week and the user will need registering again). -> -> hr(ng-show="users.length > 0").ng-cloak -> table(ng-show="users.length > 0").table.table-striped.ng-cloak -> tr -> th #{translate("email")} -> th Set Password Url -> tr(ng-repeat="user in users") -> td {{ user.email }} -> td(style="word-break: break-all;") {{ user.setNewPasswordUrl }} -> tab(heading="Open/Close Editor" bookmarkable-tab="open-close-editor") -18c60,66 -< +bookmarkable-tabset-header('tpds', 'TPDS/Dropbox Management') ---- -> | The "Open/Close Editor" feature is not available in SAAS. -> else -> .row-spaced -> form(method='post',action='/admin/closeEditor') -> input(name="_csrf", type="hidden", value=csrfToken) -> button.btn.btn-danger(type="submit") Close Editor -> p.small Will stop anyone opening the editor. Will NOT disconnect already connected users. -20,42d67 +20,37d26 < .tab-content < .tab-pane.active( < role="tabpanel" < id='system-messages' < ) < each message in systemMessages -< .alert.alert-info.row-spaced(ng-non-bindable) #{message.content} +< .alert.alert-info.row-spaced #{message.content} < hr < form(method='post', action='/admin/messages') < input(name="_csrf", type="hidden", value=csrfToken) @@ -91,19 +50,33 @@ < form(method='post', action='/admin/messages/clear') < input(name="_csrf", type="hidden", value=csrfToken) < button.btn.btn-danger(type="submit") Clear all messages -< +39,49c28,40 < .tab-pane( < role="tabpanel" < id='open-sockets' < ) -44,74c69,78 +< .row-spaced < ul < each agents, url in openSockets -< li(ng-non-bindable) #{url} - total : #{agents.length} +< li #{url} - total : #{agents.length} < ul < each agent in agents -< li(ng-non-bindable) #{agent} -< +< li #{agent} +--- +> tab(heading="Register non LDAP User") +> form.form +> .row +> .col-md-4.col-xs-8 +> input.form-control( +> name="email", +> type="text", +> placeholder="jane@example.com, joe@example.com", +> ng-model="inputs.emails", +> on-enter="registerUsers()" +> ) +> .col-md-8.col-xs-4 +> button.btn.btn-primary(ng-click="registerUsers()") #{translate("register")} +51,62c42,43 < .tab-pane( < role="tabpanel" < id='open-close-editor' @@ -116,31 +89,38 @@ < input(name="_csrf", type="hidden", value=csrfToken) < button.btn.btn-danger(type="submit") Close Editor < p.small Will stop anyone opening the editor. Will NOT disconnect already connected users. -< +--- +> .row-spaced(ng-show="error").ng-cloak.text-danger +> p Sorry, an error occured +64,68c45,48 < .row-spaced < form(method='post',action='/admin/disconnectAllUsers') < input(name="_csrf", type="hidden", value=csrfToken) < button.btn.btn-danger(type="submit") Disconnect all users < p.small Will force disconnect all users with the editor open. Make sure to close the editor first to avoid them reconnecting. -< +--- +> .row-spaced(ng-show="users.length > 0").ng-cloak.text-success +> p We've sent out welcome emails to the registered users. +> p You can also manually send them URLs below to allow them to reset their password and log in for the first time. +> p (Password reset tokens will expire after one week and the user will need registering again). +70,75c50,58 < .row-spaced < form(method='post',action='/admin/openEditor') < input(name="_csrf", type="hidden", value=csrfToken) < button.btn.btn-danger(type="submit") Reopen Editor < p.small Will reopen the editor after closing. +< --- -> form(method='post',action='/admin/disconnectAllUsers') -> input(name="_csrf", type="hidden", value=csrfToken) -> button.btn.btn-danger(type="submit") Disconnect all users -> p.small Will force disconnect all users with the editor open. Make sure to close the editor first to avoid them reconnecting. -> -> .row-spaced -> form(method='post',action='/admin/openEditor') -> input(name="_csrf", type="hidden", value=csrfToken) -> button.btn.btn-danger(type="submit") Reopen Editor -> p.small Will reopen the editor after closing. -76,99d79 -< if hasFeature('saas') +> hr(ng-show="users.length > 0").ng-cloak +> table(ng-show="users.length > 0").table.table-striped.ng-cloak +> tr +> th #{translate("email")} +> th Set Password Url +> tr(ng-repeat="user in users") +> td {{ user.email }} +> td(style="word-break: break-all;") {{ user.setNewPasswordUrl }} +> tab(heading="Open/Close Editor" bookmarkable-tab="open-close-editor") +77,99c60,78 < .tab-pane( < role="tabpanel" < id='tpds' @@ -164,3 +144,24 @@ < input.form-control(type='text', name='user_id', placeholder='user_id', required) < .form-group < button.btn-primary.btn(type='submit') Poll +--- +> | The "Open/Close Editor" feature is not available in SAAS. +> else +> .row-spaced +> form(method='post',action='/admin/closeEditor') +> input(name="_csrf", type="hidden", value=csrfToken) +> button.btn.btn-danger(type="submit") Close Editor +> p.small Will stop anyone opening the editor. Will NOT disconnect already connected users. +> +> .row-spaced +> form(method='post',action='/admin/disconnectAllUsers') +> input(name="_csrf", type="hidden", value=csrfToken) +> button.btn.btn-danger(type="submit") Disconnect all users +> p.small Will force disconnect all users with the editor open. Make sure to close the editor first to avoid them reconnecting. +> +> .row-spaced +> form(method='post',action='/admin/openEditor') +> input(name="_csrf", type="hidden", value=csrfToken) +> button.btn.btn-danger(type="submit") Reopen Editor +> p.small Will reopen the editor after closing. +\ No newline at end of file diff --git a/ldap-overleaf-sl/sharelatex_diff/login.pug.diff b/ldap-overleaf-sl/sharelatex_diff/login.pug.diff index 3b01643..b80030c 100644 --- a/ldap-overleaf-sl/sharelatex_diff/login.pug.diff +++ b/ldap-overleaf-sl/sharelatex_diff/login.pug.diff @@ -1,17 +1,10 @@ -14a15,22 +20a21 > //- >>>>>>>>>>>>>>>>>>>>>>>>>>>>>> -> //- input.form-control( -> //- type='email', -> //- name='email', -> //- required, -> //- placeholder='email@example.com', -> //- autofocus="true" -> //- ) -16d23 +22d22 < type='email', -21a29 +27a28 > //- <<<<<<<<<<<<<<<<<<<<<<<<<<<<<< -36a45,50 +42a44,49 > //- >>>>>>>>>>>>>>>>>>>>>>>>>>>>>> > if process.env.OAUTH2_ENABLED === 'true' > .form-group.text-center(style="padding-top: 10px") diff --git a/ldap-overleaf-sl/sharelatex_diff/navbar-marketing.pug.diff b/ldap-overleaf-sl/sharelatex_diff/navbar-marketing.pug.diff index 0fab55b..6a2d26c 100644 --- a/ldap-overleaf-sl/sharelatex_diff/navbar-marketing.pug.diff +++ b/ldap-overleaf-sl/sharelatex_diff/navbar-marketing.pug.diff @@ -1,3 +1,3 @@ -55,56d54 +54,55d53 < li < a(href="/admin/project") Project URL Lookup diff --git a/ldap-overleaf-sl/sharelatex_diff/navbar.pug.diff b/ldap-overleaf-sl/sharelatex_diff/navbar-website-redesign.pug.diff similarity index 56% rename from ldap-overleaf-sl/sharelatex_diff/navbar.pug.diff rename to ldap-overleaf-sl/sharelatex_diff/navbar-website-redesign.pug.diff index b1aaa94..a03b833 100644 --- a/ldap-overleaf-sl/sharelatex_diff/navbar.pug.diff +++ b/ldap-overleaf-sl/sharelatex_diff/navbar-website-redesign.pug.diff @@ -1,25 +1,37 @@ -4,6c4,5 -< if (typeof(suppressNavbarRight) == "undefined") -< button.navbar-toggle(ng-init="navCollapsed = true", ng-click="navCollapsed = !navCollapsed", ng-class="{active: !navCollapsed}", aria-label="Toggle " + translate('navigation')) -< i.fa.fa-bars(aria-hidden="true") ---- -> button.navbar-toggle(ng-init="navCollapsed = true", ng-click="navCollapsed = !navCollapsed", ng-class="{active: !navCollapsed}", aria-label="Toggle " + translate('navigation')) -> i.fa.fa-bars(aria-hidden="true") -14,106c13,74 +29,32c29,41 < - var canDisplayAdminMenu = hasAdminAccess() < - var canDisplayAdminRedirect = canRedirectToAdminDomain() < - var canDisplaySplitTestMenu = hasFeature('saas') && (canDisplayAdminMenu || (getSessionUser() && getSessionUser().staffAccess && (getSessionUser().staffAccess.splitTestMetrics || getSessionUser().staffAccess.splitTestManagement))) < - var canDisplaySurveyMenu = hasFeature('saas') && canDisplayAdminMenu -< - var featuresPageVariant = splitTestVariants && splitTestVariants['features-page'] -< +--- +> .navbar-collapse.collapse#navbar-main-collapse +> ul.nav.navbar-nav.navbar-right +> if (getSessionUser() && getSessionUser().isAdmin) +> li.dropdown.subdued +> a.dropdown-toggle( +> href="#", +> role="button", +> aria-haspopup="true", +> aria-expanded="false", +> data-toggle="dropdown" +> ) +> | Admin +> span.caret +34,64c43,54 < if (typeof(suppressNavbarRight) == "undefined") -< .navbar-collapse.collapse(collapse="navCollapsed") +< .navbar-collapse.collapse#navbar-main-collapse < ul.nav.navbar-nav.navbar-right < if (canDisplayAdminMenu || canDisplayAdminRedirect || canDisplaySplitTestMenu) -< li.dropdown(class="subdued", dropdown) -< a.dropdown-toggle(href, dropdown-toggle) +< li.dropdown.subdued +< a.dropdown-toggle( +< href="#", +< role="button", +< aria-haspopup="true", +< aria-expanded="false", +< data-toggle="dropdown" +< ) < | Admin -< b.caret +< span.caret < ul.dropdown-menu < if canDisplayAdminMenu < li @@ -28,8 +40,6 @@ < a(href="/admin/user") Manage Users < li < a(href="/admin/project") Project URL Lookup -< li -< a(href="/admin/saml/logs") SAML logs < if canDisplayAdminRedirect < li < a(href=settings.adminUrl) Switch to Admin @@ -39,47 +49,100 @@ < if canDisplaySurveyMenu < li < a(href="/admin/survey") Manage Surveys -< +--- +> // loop over header_extras +> each item in nav.header_extras +> - +> if ((item.only_when_logged_in && getSessionUser()) +> || (item.only_when_logged_out && (!getSessionUser())) +> || (!item.only_when_logged_out && !item.only_when_logged_in && !item.only_content_pages) +> || (item.only_content_pages && (typeof(suppressNavContentLinks) == "undefined" || !suppressNavContentLinks)) +> ){ +> var showNavItem = true +> } else { +> var showNavItem = false +> } +66,77c56,89 < // loop over header_extras < each item in nav.header_extras < - < if ((item.only_when_logged_in && getSessionUser()) -< || (item.only_when_logged_out && (!getSessionUser())) -< || (!item.only_when_logged_out && !item.only_when_logged_in && !item.only_content_pages) -< || (item.only_content_pages && (typeof(suppressNavContentLinks) == "undefined" || !suppressNavContentLinks)) -< ){ +< || (item.only_when_logged_out && (!getSessionUser())) +< || (!item.only_when_logged_out && !item.only_when_logged_in && !item.only_content_pages) +< || (item.only_content_pages && (typeof(suppressNavContentLinks) == "undefined" || !suppressNavContentLinks)) +< ){ < var showNavItem = true < } else { < var showNavItem = false < } -< +--- +> if showNavItem +> if item.dropdown +> li.dropdown(class=item.class) +> a.dropdown-toggle( +> href="#", +> role="button", +> aria-haspopup="true", +> aria-expanded="false", +> data-toggle="dropdown" +> ) +> | !{translate(item.text)} +> span.caret +> ul.dropdown-menu +> each child in item.dropdown +> if child.divider +> li.divider +> else if child.isContactUs +> li +> a(data-ol-open-contact-form-modal="contact-us" href) +> span(event-tracking="menu-clicked-contact" event-tracking-mb="true" event-tracking-trigger="click") +> | #{translate("contact_us")} +> else +> li +> if child.url +> a( +> href=child.url, +> class=child.class, +> event-tracking=child.event +> event-tracking-mb="true" +> event-tracking-trigger="click" +> event-segmentation=child.eventSegmentation +> ) !{translate(child.text)} +> else +> | !{translate(child.text)} +79,125d90 < if showNavItem < if item.dropdown -< li.dropdown(class=item.class, dropdown) -< a.dropdown-toggle(href, dropdown-toggle) +< li.dropdown(class=item.class) +< a.dropdown-toggle( +< href="#", +< role="button", +< aria-haspopup="true", +< aria-expanded="false", +< data-toggle="dropdown" +< ) < | !{translate(item.text)} -< b.caret +< span.caret < ul.dropdown-menu < each child in item.dropdown < if child.divider < li.divider < else if child.isContactUs < li -< a(ng-controller="ContactModal" ng-click="contactUsModal()" href) +< a(data-ol-open-contact-form-modal="contact-us" href) < span(event-tracking="menu-clicked-contact" event-tracking-mb="true" event-tracking-trigger="click") < | #{translate("contact_us")} < else < li < if child.url -< if !child.splitTest || child.splitTest && child.splitTest === 'features-page' && child.splitTestVariant === featuresPageVariant -< a( -< href=child.url, -< class=child.class, -< event-tracking=child.event -< event-tracking-mb="true" -< event-tracking-trigger="click" -< event-segmentation=child.eventSegmentation -< ) !{translate(child.text)} +< a( +< href=child.url, +< class=child.class, +< event-tracking=child.event +< event-tracking-mb="true" +< event-tracking-trigger="click" +< event-segmentation=child.eventSegmentation +< ) !{translate(child.text)} < else < | !{translate(child.text)} < else @@ -95,74 +158,10 @@ < else < | !{translate(item.text)} < -< // logged out -< if !getSessionUser() +128,139d92 < // register link < if hasFeature('registration-page') ---- -> .navbar-collapse.collapse(collapse="navCollapsed") -> -> ul.nav.navbar-nav.navbar-right -> if (getSessionUser() && getSessionUser().isAdmin) -> li -> a(href="/admin") Admin -> -> -> // loop over header_extras -> each item in nav.header_extras -> - -> if ((item.only_when_logged_in && getSessionUser()) -> || (item.only_when_logged_out && (!getSessionUser())) -> || (!item.only_when_logged_out && !item.only_when_logged_in && !item.only_content_pages) -> || (item.only_content_pages && (typeof(suppressNavContentLinks) == "undefined" || !suppressNavContentLinks)) -> ){ -> var showNavItem = true -> } else { -> var showNavItem = false -> } -> -> if showNavItem -> if item.dropdown -> li.dropdown(class=item.class, dropdown) -> a.dropdown-toggle(href, dropdown-toggle) -> | !{translate(item.text)} -> b.caret -> ul.dropdown-menu -> each child in item.dropdown -> if child.divider -> li.divider -> else -> li -> if child.url -> a(href=child.url, class=child.class) !{translate(child.text)} -> else -> | !{translate(child.text)} -> else -> li(class=item.class) -> if item.url -> a(href=item.url, class=item.class) !{translate(item.text)} -> else -> | !{translate(item.text)} -> -> // logged out -> if !getSessionUser() -> // login link -> li -> a(href="/login") #{translate('log_in')} -> -> // projects link and account menu -> if getSessionUser() -> li -> a(href="/project") #{translate('Projects')} -> li.dropdown(dropdown) -> a.dropdown-toggle(href, dropdown-toggle) -> | #{translate('Account')} -> b.caret -> ul.dropdown-menu -> //li -> // div.subdued(ng-non-bindable) #{getUserEmail()} -> //li.divider.hidden-xs.hidden-sm -108,139c76,77 +< li.primary < a( < href="/register" < event-tracking="menu-clicked-register" @@ -170,35 +169,32 @@ < event-tracking-trigger="click" < event-tracking-mb="true" < event-segmentation={ page: currentUrl } -< ) #{translate('register')} -< -< // login link -< li -< a( -< href="/login" -< event-tracking="menu-clicked-login" -< event-tracking-action="clicked" -< event-tracking-trigger="click" -< event-tracking-mb="true" -< event-segmentation={ page: currentUrl } -< ) #{translate('log_in')} +< ) #{translate('sign_up')} < +141c94 +< li.secondary +--- +> li +151,178c104,114 < // projects link and account menu < if getSessionUser() -< li +< li.secondary < a(href="/project") #{translate('Projects')} -< li.dropdown(dropdown) -< a.dropdown-toggle(href, dropdown-toggle) +< li.secondary.dropdown +< a.dropdown-toggle( +< href="#", +< role="button", +< aria-haspopup="true", +< aria-expanded="false", +< data-toggle="dropdown" +< ) < | #{translate('Account')} -< b.caret +< span.caret < ul.dropdown-menu < li -< div.subdued {{ usersEmail }} +< div.subdued #{getSessionUser().email} < li.divider.hidden-xs.hidden-sm ---- -> a(href="/user/settings") #{translate('Account Settings')} -> if nav.showSubscriptionLink -141,149c79,84 +< li < a(href="/user/settings") #{translate('Account Settings')} < if nav.showSubscriptionLink < li @@ -209,7 +205,12 @@ < input(name='_csrf', type='hidden', value=csrfToken) < button.btn-link.text-left.dropdown-menu-button #{translate('log_out')} --- -> a(href="/user/subscription") #{translate('subscription')} +> +> li +> a(href="/user/settings") #{translate('Account Settings')} +> if nav.showSubscriptionLink +> li +> a(href="/user/subscription") #{translate('subscription')} > li.divider.hidden-xs.hidden-sm > li > form(method="POST" action="/logout") diff --git a/ldap-overleaf-sl/sharelatex_diff/router.js.diff b/ldap-overleaf-sl/sharelatex_diff/router.js.diff index 0077d06..747e55c 100644 --- a/ldap-overleaf-sl/sharelatex_diff/router.js.diff +++ b/ldap-overleaf-sl/sharelatex_diff/router.js.diff @@ -1,4 +1,4 @@ -259a260,268 +269a270,278 > // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>> > if (process.env.OAUTH2_ENABLED === 'true') { > webRouter.get('/oauth/redirect', AuthenticationController.oauth2Redirect) diff --git a/ldap-overleaf-sl/sharelatex_diff/settings.pug.diff b/ldap-overleaf-sl/sharelatex_diff/settings.pug.diff index eedb873..e06328b 100644 --- a/ldap-overleaf-sl/sharelatex_diff/settings.pug.diff +++ b/ldap-overleaf-sl/sharelatex_diff/settings.pug.diff @@ -1,10 +1,10 @@ -1c1 -< extends ../layout-marketing ---- -> extends ../layout -3,4c3,14 +3,8c3,82 < block entrypointVar < - entrypoint = 'pages/user/settings' +< +< block vars +< - bootstrap5PageStatus = 'enabled' // One of 'disabled', 'enabled', and 'queryStringOnly' +< - bootstrap5PageSplitTest = 'bootstrap-5' --- > block content > .content.content-alt @@ -18,38 +18,6 @@ > .page-header > h1 #{translate("account_settings")} > .account-settings(ng-controller="AccountSettingsController", ng-cloak) -6,29c16,17 -< block append meta -< meta(name="ol-hasPassword" data-type="boolean" content=hasPassword) -< meta(name="ol-shouldAllowEditingDetails" data-type="boolean" content=shouldAllowEditingDetails) -< meta(name="ol-oauthProviders", data-type="json", content=oauthProviders) -< meta(name="ol-institutionLinked", data-type="json", content=institutionLinked) -< meta(name="ol-samlError", data-type="json", content=samlError) -< meta(name="ol-institutionEmailNonCanonical", content=institutionEmailNonCanonical) -< -< meta(name="ol-reconfirmedViaSAML", content=reconfirmedViaSAML) -< meta(name="ol-reconfirmationRemoveEmail", content=reconfirmationRemoveEmail) -< meta(name="ol-samlBeta", content=samlBeta) -< meta(name="ol-ssoErrorMessage", content=ssoErrorMessage) -< meta(name="ol-thirdPartyIds", data-type="json", content=thirdPartyIds || {}) -< meta(name="ol-passwordStrengthOptions", data-type="json", content=settings.passwordStrengthOptions || {}) -< meta(name="ol-isExternalAuthenticationSystemUsed" data-type="boolean" content=externalAuthenticationSystemUsed()) -< meta(name="ol-user" data-type="json" content=user) -< meta(name="ol-dropbox" data-type="json" content=dropbox) -< meta(name="ol-github" data-type="json" content=github) -< meta(name="ol-projectSyncSuccessMessage", content=projectSyncSuccessMessage) -< meta(name="ol-showPersonalAccessToken", data-type="boolean" content=showPersonalAccessToken) -< meta(name="ol-optionalPersonalAccessToken", data-type="boolean" content=optionalPersonalAccessToken) -< meta(name="ol-personalAccessTokens", data-type="json" content=personalAccessTokens) -< meta(name="ol-emailAddressLimit", data-type="json", content=emailAddressLimit) -< meta(name="ol-currentManagedUserAdminEmail" data-type="string" content=currentManagedUserAdminEmail) ---- -> -> -31,32c19,178 -< block content -< main.content.content-alt#settings-page-root ---- > .row > .col-md-5 > h3 #{translate("update_account_info")} @@ -63,7 +31,6 @@ > readonly="true", > ng-non-bindable > ) #{user.email} -> > if shouldAllowEditingDetails > .form-group > label(for='firstName').control-label #{translate("first_name")} @@ -105,7 +72,6 @@ > readonly="true", > ng-non-bindable > ) #{user.last_name} -> > .col-md-5.col-md-offset-1 > h3 > | Set Password for Email login @@ -113,10 +79,6 @@ > | Note: you can not change the LDAP password from here. You can set/reset a password for > | your email login: > | #[a(href="/user/password/reset", target='_blank') Reset.] -> -> | !{moduleIncludes("userSettings", locals)} -> hr -> > h3 > | Contact > div @@ -124,9 +86,35 @@ > > p #{translate("need_to_leave")} > a(href, ng-click="deleteAccount()") #{translate("delete_your_account")} -> -> -> +10,16d83 +< block append meta +< meta(name="ol-hasPassword" data-type="boolean" content=hasPassword) +< meta(name="ol-shouldAllowEditingDetails" data-type="boolean" content=shouldAllowEditingDetails) +< meta(name="ol-oauthProviders", data-type="json", content=oauthProviders) +< meta(name="ol-institutionLinked", data-type="json", content=institutionLinked) +< meta(name="ol-samlError", data-type="json", content=samlError) +< meta(name="ol-institutionEmailNonCanonical", content=institutionEmailNonCanonical) +18,36c85,95 +< meta(name="ol-reconfirmedViaSAML", content=reconfirmedViaSAML) +< meta(name="ol-reconfirmationRemoveEmail", content=reconfirmationRemoveEmail) +< meta(name="ol-samlBeta", content=samlBeta) +< meta(name="ol-ssoErrorMessage", content=ssoErrorMessage) +< meta(name="ol-thirdPartyIds", data-type="json", content=thirdPartyIds || {}) +< meta(name="ol-passwordStrengthOptions", data-type="json", content=settings.passwordStrengthOptions || {}) +< meta(name="ol-isExternalAuthenticationSystemUsed" data-type="boolean" content=externalAuthenticationSystemUsed()) +< meta(name="ol-user" data-type="json" content=user) +< meta(name="ol-dropbox" data-type="json" content=dropbox) +< meta(name="ol-github" data-type="json" content=github) +< meta(name="ol-projectSyncSuccessMessage", content=projectSyncSuccessMessage) +< meta(name="ol-showPersonalAccessToken", data-type="boolean" content=showPersonalAccessToken) +< meta(name="ol-optionalPersonalAccessToken", data-type="boolean" content=optionalPersonalAccessToken) +< meta(name="ol-personalAccessTokens", data-type="json" content=personalAccessTokens) +< meta(name="ol-emailAddressLimit", data-type="json", content=emailAddressLimit) +< meta(name="ol-currentManagedUserAdminEmail" data-type="string" content=currentManagedUserAdminEmail) +< meta(name="ol-gitBridgeEnabled" data-type="boolean" content=gitBridgeEnabled) +< meta(name="ol-isSaas" data-type="boolean" content=isSaas) +< meta(name="ol-memberOfSSOEnabledGroups" data-type="json" content=memberOfSSOEnabledGroups) +--- > script(type='text/ng-template', id='deleteAccountModalTemplate') > .modal-header > h3 #{translate("delete_account")} @@ -138,7 +126,11 @@ > | Your Overleaf v2 projects will be deleted if you delete your account. > | If you want to remove any remaining Overleaf v1 projects in your account, > | please first make sure they are imported to Overleaf v2. -> +38,40c97,167 +< block content +< main.content.content-alt#main-content +< #settings-page-root +--- > if settings.overleaf && !hasPassword > p > b diff --git a/scripts/extract_files.sh b/scripts/extract_files.sh index 17c8b8f..5e8562f 100644 --- a/scripts/extract_files.sh +++ b/scripts/extract_files.sh @@ -9,7 +9,7 @@ CONTAINER_FILE_PATHS=( "/overleaf/services/web/app/src/router.js" "/overleaf/services/web/app/views/user/settings.pug" "/overleaf/services/web/app/views/user/login.pug" - "/overleaf/services/web/app/views/layout/navbar.pug" + "/overleaf/services/web/app/views/layout/navbar-website-redesign.pug" "/overleaf/services/web/app/views/layout/navbar-marketing.pug" "/overleaf/services/web/app/views/admin/index.pug" "/overleaf/services/web/app/views/admin/index.pug" @@ -22,7 +22,7 @@ FILENAMES=( "router.js" "settings.pug" "login.pug" - "navbar.pug" + "navbar-website-redesign.pug" "navbar-marketing.pug" "admin-index.pug" "admin-sysadmin.pug" From ab60d4a7a4558438d03ff3c4e71c91263bfc4c19 Mon Sep 17 00:00:00 2001 From: armanddidierjean <95971503+armanddidierjean@users.noreply.github.com> Date: Sun, 29 Sep 2024 17:25:41 +0200 Subject: [PATCH 3/4] Patch navbar-website-redesign.pug instead of sharelatex/navbar.pug in docker file --- ldap-overleaf-sl/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ldap-overleaf-sl/Dockerfile b/ldap-overleaf-sl/Dockerfile index 4b79aad..ab30c96 100644 --- a/ldap-overleaf-sl/Dockerfile +++ b/ldap-overleaf-sl/Dockerfile @@ -40,7 +40,7 @@ COPY sharelatex/router.js /overleaf/services/web/app/src/route # Too much changes to do inline (>10 Lines). COPY sharelatex/settings.pug /overleaf/services/web/app/views/user/ COPY sharelatex/login.pug /overleaf/services/web/app/views/user/ -COPY sharelatex/navbar.pug /overleaf/services/web/app/views/layout/ +COPY sharelatex/navbar-website-redesign.pug /overleaf/services/web/app/views/layout/ COPY sharelatex/navbar-marketing.pug /overleaf/services/web/app/views/layout/ # Non LDAP User Registration for Admins From 72219c539292ce39c74adacebb0d043069c01b9c Mon Sep 17 00:00:00 2001 From: armanddidierjean <95971503+armanddidierjean@users.noreply.github.com> Date: Sun, 29 Sep 2024 17:26:20 +0200 Subject: [PATCH 4/4] Use Overleaf 5.1.1 base image --- ldap-overleaf-sl/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ldap-overleaf-sl/Dockerfile b/ldap-overleaf-sl/Dockerfile index ab30c96..17a0189 100644 --- a/ldap-overleaf-sl/Dockerfile +++ b/ldap-overleaf-sl/Dockerfile @@ -1,4 +1,4 @@ -FROM sharelatex/sharelatex:4.2.0 +FROM sharelatex/sharelatex:5.1.1 # FROM sharelatex/sharelatex:latest # latest might not be tested # e.g. the AuthenticationManager.js script had to be adapted after versions 2.3.1