11 Commits

Author SHA1 Message Date
Simon M. Haller-Seeber
8eaaf5e6c0 Addressed #42, if one can not login via email (local user), and one does not use ldap but oauth - then there is no login page but a redirect to the oauth provider. Closes issue #42 and pull request #43. 2024-06-26 12:31:44 +02:00
Simon M. Haller-Seeber
b6eb20b71d Added missing function; Adapted Dockerfile that we can use tlmgr and LaTex 2024 2024-06-25 14:34:11 +02:00
Simon M. Haller-Seeber
63e2ee30e9 Merge branch '5.0.6' of github.com:smhaller/ldap-overleaf-sl 2024-06-24 19:17:04 +02:00
Simon M. Haller-Seeber
5d31ca8ec3 Added missing routes for TrackChanges 2024-06-24 19:15:51 +02:00
sym
cc29b6f89e Update README.md 2024-06-24 16:48:27 +02:00
Simon M. Haller-Seeber
b3ef70bcd4 Added changes for Sharelatex/Overleaf 5.0.6; Added Track Changes. 2024-06-24 16:45:36 +02:00
Simon M. Haller-Seeber
0a70d7c1e7 Added changes for Sharelatex/Overleaf 5.0.6; Added Track Changes. 2024-06-24 16:37:44 +02:00
Simon M. Haller-Seeber
ddd0a14b3e Added changes for Sharelatex/Overleaf 5.0.6; Added Track Changes. 2024-06-24 16:36:53 +02:00
sym
09a38afa57 Merge pull request #48 from maurerle/patch-2
add texlive path - use latest texlive
2024-03-05 19:37:23 +01:00
yzx9
217e61d9ad Reduce RUN instruction 2024-03-05 22:55:25 +08:00
Florian Maurer
6a761aca54 fix path - use latest texlive
This commit moves to tlmgr to install the latest texlive version.
Somehow the bin of the latex install dir is not on the PATH so we add it.

An image installed from this has a size of about 6.8GB and includes all packages of a typical latex distribution
2024-02-29 14:02:09 +01:00
18 changed files with 565 additions and 654 deletions

4
.env
View File

@@ -1,6 +1,6 @@
# do not use quotes (")
MYDOMAIN=overleaf.mildstone.org
MYMAIL=andrea.rgn@gmail.com
MYDOMAIN=MYDOMAIN.TLD
MYMAIL=MYEMAIL@MYDOMAIN.TLD
MYDATA=/data
LOGIN_TEXT=username
COLLAB_TEXT=Direct share with collaborators is enabled only for activated users!

View File

@@ -4,7 +4,7 @@ build:
docker build --build-arg login_text="${LOGIN_TEXT}" \
--build-arg collab_text="${COLLAB_TEXT}" \
--build-arg admin_is_sysadmin="${ADMIN_IS_SYSADMIN}" \
-t "ldap-overleaf-sl:240728" ldap-overleaf-sl
-t "ldap-overleaf-sl" ldap-overleaf-sl
clean: check_clean
docker-compose down

View File

@@ -2,11 +2,27 @@
This repo contains an improved, free ldap authentication and authorisation
for sharelatex/[overleaf](https://github.com/overleaf/overleaf) community
edition. Currently this repo uses `sharelatex/sharelatex:4.2.0`.
edition. Currently this repo uses `sharelatex/sharelatex:5.0.6`.
The inital idea for this implementation was taken from
[worksasintended](https://github.com/worksasintended).
## Upgrading from 4.x to 5.0
- enter mongo database container and open a mongo shell
```mongo```
- execute
```db.adminCommand({ setFeatureCompatibilityVersion: "4.4" })```
in the MongoDB shell.
- Then upgrade:
```
bash scripts/extract_files.sh 5.0.6
bash scripts/apply_diffs.sh
make
```
**Note:** TrackChanges uses newer versions of Feature/Chat and Feature/DocumentUpdater - those are pulled during make directly from the Overleaf git repository.
## BREAKING CHANGE
Be careful if you try to migrate from 3.3.2! Backup your machines and data. The migration paths should be:

View File

@@ -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 <a href=\"https://www.sharelatex.com\">ShareLaTeX</a> 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 <a href=\"https://www.sharelatex.com\">ShareLaTeX</a> 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
@@ -99,7 +99,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
@@ -110,7 +110,7 @@ services:
mongo:
restart: always
image: mongo:4.4
image: mongo:5.0
container_name: mongo
expose:
- 27017
@@ -125,7 +125,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:

View File

@@ -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 <a href=\"https://www.sharelatex.com\">ShareLaTeX</a> 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 <a href=\"https://www.sharelatex.com\">ShareLaTeX</a> 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
@@ -180,7 +180,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
@@ -191,7 +191,7 @@ services:
mongo:
restart: always
image: mongo:4.4
image: mongo:5.0
container_name: mongo
expose:
- 27017
@@ -214,7 +214,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:

View File

@@ -2,7 +2,7 @@ version: "2.2"
services:
sharelatex:
restart: always
image: ldap-overleaf-sl:240728
image: ldap-overleaf-sl
container_name: ldap-overleaf-sl
depends_on:
mongo:
@@ -16,72 +16,68 @@ 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 <a href=\"https://www.sharelatex.com\">ShareLaTeX</a> 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: 192.168.1.99
SHARELATEX_EMAIL_SMTP_PORT: 25
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."
#LOG_LEVEL: "debug"
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 <a href=\"https://www.sharelatex.com\">ShareLaTeX</a> 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: ldap://ipa.mildstone.org:389
LDAP_BASE: cn=users,cn=accounts,dc=mildstone,dc=org
LDAP_SERVER: ldaps://LDAPSERVER:636
LDAP_BASE: ou=people,dc=DOMAIN,dc=TLD
### There are to ways get users from the ldap server
## NO LDAP BIND USER:
# Tries directly to bind with the login user (as uid)
# LDAP_BINDDN: uid=%u,ou=someunit,ou=people,dc=DOMAIN,dc=TLD
# LDAP_BINDDN: uid=%u,ou=people,dc=mildstone,dc=org
## Or you can use ai global LDAP_BIND_USER
LDAP_BIND_USER: uid=ldapsearch,cn=users,cn=accounts,dc=mildstone,dc=org
LDAP_BIND_PW: ldap_ha39it9
# LDAP_BIND_USER:
# LDAP_BIND_PW:
# Only allow users matching LDAP_USER_FILTER
LDAP_USER_FILTER: "(&(memberof=cn=latexusers,cn=groups,cn=accounts,dc=mildstone,dc=org)(uid=%u))"
LDAP_USER_FILTER: "(memberof=cn=GROUPNAME,ou=groups,dc=DOMAIN,dc=TLD)"
# If user is in ADMIN_GROUP on user creation (first login) isAdmin is set to true.
# Admin Users can invite external (non ldap) users. This feature makes only sense
# when ALLOW_EMAIL_LOGIN is set to 'true'. Additionally admins can send
# system wide messages.
LDAP_ADMIN_GROUP_FILTER: "(memberof=cn=admins,cn=groups,cn=accounts,dc=mildstone,dc=org)"
LDAP_ADMIN_GROUP_FILTER: "(memberof=cn=ADMINGROUPNAME,ou=groups,dc=DOMAIN,dc=TLD)"
ALLOW_EMAIL_LOGIN: "true"
# All users in the LDAP_CONTACT_FILTER are loaded from the ldap server into contacts.
LDAP_CONTACT_FILTER: "(memberof=cn=latexusers,cn=groups,cn=accounts,dc=mildstone,dc=org)"
LDAP_CONTACTS: "true"
LDAP_CONTACT_FILTER: "(memberof=cn=GROUPNAME,ou=groups,dc=DOMAIN,dc=TLD)"
LDAP_CONTACTS: "false"
## OAuth2 Settings
# OAUTH2_ENABLED: "true"
@@ -101,7 +97,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
@@ -112,7 +108,7 @@ services:
mongo:
restart: always
image: mongo:4.4
image: mongo:5.0
container_name: mongo
expose:
- 27017
@@ -127,7 +123,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:

View File

@@ -1,4 +1,4 @@
FROM sharelatex/sharelatex:4.2.0
FROM sharelatex/sharelatex:5.0.6
# FROM sharelatex/sharelatex:latest
# latest might not be tested
# e.g. the AuthenticationManager.js script had to be adapted after versions 2.3.1
@@ -13,40 +13,54 @@ ARG admin_is_sysadmin
# set workdir (might solve issue #2 - see https://stackoverflow.com/questions/57534295/)
WORKDIR /overleaf/services/web
RUN npm cache clean --force && \
npm install -g npm@10.5.0
# install latest npm
RUN \
RUN npm install -g npm && \
## clean cache (might solve issue #2)
##npm cache clean --force && \
##npm install -g npm@latest && \
# npm cache clean --force && \
npm install ldap-escape ldapts-search ldapts@3.2.4 && \
# npm install bcrypt@5.0.0 && \
## This variant of updateing texlive does not work
# bash -c tlmgr install scheme-full && \
## try this one:
apt-get update && \
apt-get -y install python-pygments && \
apt-get -y install texlive texlive-lang-german texlive-latex-extra texlive-full texlive-science && \
apt-get -y install libxml-libxslt-perl cpanminus libbtparse2 python3-pygments
# now install latest texlive2023 from tlmgr
RUN wget -O /tmp/update-tlmgr-latest.sh http://mirror.ctan.org/systems/texlive/tlnet/update-tlmgr-latest.sh
RUN bash /tmp/update-tlmgr-latest.sh
RUN tlmgr update --self --all && \
tlmgr install scheme-full --verify-repo=none && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*
# latex-bin must be on path to be found in compilation process
# needed for biber epstopdf and others
ENV PATH="/usr/local/texlive/2023/bin/x86_64-linux:${PATH};"
# overwrite some files
COPY sharelatex/AuthenticationManager.js /overleaf/services/web/app/src/Features/Authentication/
COPY sharelatex/AuthenticationController.js /overleaf/services/web/app/src/Features/Authentication/
COPY sharelatex/ContactController.js /overleaf/services/web/app/src/Features/Contacts/
COPY sharelatex/ProjectEditorHandler.js /overleaf/services/web/app/src/Features/Project/
COPY sharelatex_diff/TrackChangesController.js /overleaf/services/web/app/src/Features/TrackChanges/
COPY sharelatex/router.js /overleaf/services/web/app/src/router.js
## Copy some new files from the Docker git repo (because they are not in the current Docker release - to enable Track-Changes
RUN wget -O /overleaf/services/web/app/src/Features/DocumentUpdater/DocumentUpdaterHandler.js https://raw.githubusercontent.com/overleaf/overleaf/main/services/web/app/src/Features/DocumentUpdater/DocumentUpdaterHandler.js
RUN wget -O /overleaf/services/web/app/src/Features/Chat/ChatApiHandler.js https://raw.githubusercontent.com/overleaf/overleaf/main/services/web/app/src/Features/Chat/ChatApiHandler.js
RUN wget -O /overleaf/services/web/app/src/Features/Chat/ChatController.js https://raw.githubusercontent.com/overleaf/overleaf/main/services/web/app/src/Features/Chat/ChatController.js
RUN wget -O /overleaf/services/web/app/src/Features/Chat/ChatManager.js https://raw.githubusercontent.com/overleaf/overleaf/main/services/web/app/src/Features/Chat/ChatManager.js
# Too much changes to do inline (>10 Lines).
COPY sharelatex/settings.pug /overleaf/services/web/app/views/user/
#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.pug /overleaf/services/web/app/views/layout/
COPY sharelatex/navbar-marketing.pug /overleaf/services/web/app/views/layout/
# Copy TrackChanges Module
#COPY sharelatex-modules/track-changes /overleaf/services/web/modules/track-changes
# Non LDAP User Registration for Admins
COPY sharelatex/admin-index.pug /overleaf/services/web/app/views/admin/index.pug
COPY sharelatex/admin-sysadmin.pug /tmp/admin-sysadmin.pug
@@ -92,6 +106,3 @@ RUN sed -iE "s/email@example.com/${login_text:-user}/g" /overleaf/services/web/a
# echo "/usr/cron.weekly/nginx-cert.sh 2>&1 > /dev/null" > /etc/rc.local && \
# chmod 0744 /etc/rc.local
COPY update_texlive.sh /overleaf/services/web
RUN sh update_texlive.sh

View File

@@ -1,13 +1,11 @@
268a268,364
>
> // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
300a301,394
> oauth2Redirect(req, res, next) {
> // random state
> const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
> 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`
@@ -34,7 +32,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)
@@ -95,4 +93,3 @@
> console.error("Fails to access by OAuth2: " + String(e))
> }
> },
> // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

View File

@@ -1,12 +1,9 @@
19a20,25
> // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
19a20,22
> const fs = require("fs")
> const { Client } = require("ldapts")
> const ldapEscape = require("ldap-escape")
> // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
120a124,132
>
120a127,136
> // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
> _checkUserPassword2(query, password, callback) {
> // leave original _checkUserPassword untouched, because it will be called by
> // setUserPasswordInV2 (e.g. UserRegistrationHandler.js )
@@ -14,17 +11,16 @@
> AuthenticationManager.authUserObj(error, user, query, password, callback)
> })
> },
> // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
>
126c142,144
122c134,138
< AuthenticationManager._checkUserPassword(
---
> // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
> if (typeof callback === 'undefined') {
> callback = auditLog
> auditLog = null
> }
> AuthenticationManager._checkUserPassword2(
> // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
190a209,488
>
> // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
201a218,494
> /**
> * login with any password
> */
@@ -70,9 +66,10 @@
> isAdmin
> ) {
> if (!user) {
> //console.log('Creating User:' + JSON.stringify(query))
> //create random pass for local userdb, does not get checked for ldap users during login
> const pass = require("crypto").randomBytes(32).toString("hex")
> console.log('Creating User', { mail, uid, firstname, lastname, isAdmin, pass })
> let pass = require("crypto").randomBytes(32).toString("hex")
> //console.log('Creating User:' + JSON.stringify(query) + 'Random Pass' + pass)
>
> const userRegHand = require("../User/UserRegistrationHandler.js")
> userRegHand.registerNewUser(
@@ -102,7 +99,6 @@
> }
> ) // end register user
> } else {
> console.log('User exists', { mail })
> AuthenticationManager.login(user, "randomPass", callback)
> }
> },
@@ -302,4 +298,3 @@
> }
> })
> },
> // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

View File

@@ -0,0 +1,8 @@
11c11
< trackChangesAvailable: false,
---
> trackChangesAvailable: true,
60c60
< trackChanges: false,
---
> trackChanges: true,

View File

@@ -0,0 +1,308 @@
const ChatApiHandler = require('../Chat/ChatApiHandler')
const ChatManager = require('../Chat/ChatManager')
const EditorRealTimeController = require('../Editor/EditorRealTimeController')
const SessionManager = require('../Authentication/SessionManager')
const UserInfoManager = require('../User/UserInfoManager')
const DocstoreManager = require('../Docstore/DocstoreManager')
const DocumentUpdaterHandler = require('../DocumentUpdater/DocumentUpdaterHandler')
const CollaboratorsGetter = require('../Collaborators/CollaboratorsGetter')
const { Project } = require('../../models/Project')
const pLimit = require('p-limit')
async function _updateTCState (projectId, state, callback) {
await Project.updateOne({_id: projectId}, {track_changes: state}).exec()
callback()
}
function _transformId(doc) {
if (doc._id) {
doc.id = doc._id;
delete doc._id;
}
return doc;
}
const TrackChangesController = {
trackChanges(req, res, next) {
const { project_id } = req.params
let state = req.body.on || req.body.on_for
if ( req.body.on_for_guests && !req.body.on ) state.__guests__ = true
return _updateTCState(project_id, state,
function (err, message) {
if (err != null) {
return next(err)
}
EditorRealTimeController.emitToRoom(
project_id,
'toggle-track-changes',
state
)
return res.sendStatus(204)
}
)
},
acceptChanges(req, res, next) {
const { project_id, doc_id } = req.params
const change_ids = req.body.change_ids
return DocumentUpdaterHandler.acceptChanges(
project_id,
doc_id,
change_ids,
function (err, message) {
if (err != null) {
return next(err)
}
EditorRealTimeController.emitToRoom(
project_id,
'accept-changes',
doc_id,
change_ids,
)
return res.sendStatus(204)
}
)
},
async getAllRanges(req, res, next) {
const { project_id } = req.params
// FIXME: ranges are from mongodb, probably already outdated
const ranges = await DocstoreManager.promises.getAllRanges(project_id)
// frontend expects 'id', not '_id'
return res.json(ranges.map(_transformId))
},
async getChangesUsers(req, res, next) {
const { project_id } = req.params
const memberIds = await CollaboratorsGetter.promises.getMemberIds(project_id)
// FIXME: Does not work properly if the user is no longer a member of the project
// memberIds from DocstoreManager.getAllRanges(project_id) is not a remedy
// because ranges are not updated in real-time
const limit = pLimit(3)
const users = await Promise.all(
memberIds.map(memberId =>
limit(async () => {
const user = await UserInfoManager.promises.getPersonalInfo(memberId)
return user
})
)
)
users.push({_id: null}) // An anonymous user won't cause any harm
// frontend expects 'id', not '_id'
return res.json(users.map(_transformId))
},
getThreads(req, res, next) {
const { project_id } = req.params
return ChatApiHandler.getThreads(
project_id,
function (err, messages) {
if (err != null) {
return next(err)
}
return ChatManager.injectUserInfoIntoThreads(
messages,
function (err) {
if (err != null) {
return next(err)
}
return res.json(messages)
}
)
}
)
},
sendComment(req, res, next) {
const { project_id, thread_id } = req.params
const { content } = req.body
const user_id = SessionManager.getLoggedInUserId(req.session)
if (user_id == null) {
const err = new Error('no logged-in user')
return next(err)
}
return ChatApiHandler.sendComment(
project_id,
thread_id,
user_id,
content,
function (err, message) {
if (err != null) {
return next(err)
}
return UserInfoManager.getPersonalInfo(
user_id,
function (err, user) {
if (err != null) {
return next(err)
}
message.user = user
EditorRealTimeController.emitToRoom(
project_id,
'new-comment',
thread_id, message
)
return res.sendStatus(204)
}
)
}
)
},
editMessage(req, res, next) {
const { project_id, thread_id, message_id } = req.params
const { content } = req.body
const user_id = SessionManager.getLoggedInUserId(req.session)
if (user_id == null) {
const err = new Error('no logged-in user')
return next(err)
}
return ChatApiHandler.editMessage(
project_id,
thread_id,
message_id,
user_id,
content,
function (err, message) {
if (err != null) {
return next(err)
}
EditorRealTimeController.emitToRoom(
project_id,
'edit-message',
thread_id,
message_id,
content
)
return res.sendStatus(204)
}
)
},
deleteMessage(req, res, next) {
const { project_id, thread_id, message_id } = req.params
return ChatApiHandler.deleteMessage(
project_id,
thread_id,
message_id,
function (err, message) {
if (err != null) {
return next(err)
}
EditorRealTimeController.emitToRoom(
project_id,
'delete-message',
thread_id,
message_id
)
return res.sendStatus(204)
}
)
},
resolveThread(req, res, next) {
const { project_id, doc_id, thread_id } = req.params
const user_id = SessionManager.getLoggedInUserId(req.session)
if (user_id == null) {
const err = new Error('no logged-in user')
return next(err)
}
DocumentUpdaterHandler.resolveThread(
project_id,
doc_id,
thread_id,
user_id,
function (err, message) {
if (err != null) {
return next(err)
}
}
)
return ChatApiHandler.resolveThread(
project_id,
thread_id,
user_id,
function (err, message) {
if (err != null) {
return next(err)
}
return UserInfoManager.getPersonalInfo(
user_id,
function (err, user) {
if (err != null) {
return next(err)
}
EditorRealTimeController.emitToRoom(
project_id,
'resolve-thread',
thread_id,
user_id
)
return res.sendStatus(204)
}
)
}
)
},
reopenThread(req, res, next) {
const { project_id, doc_id, thread_id } = req.params
const user_id = SessionManager.getLoggedInUserId(req.session)
if (user_id == null) {
const err = new Error('no logged-in user')
return next(err)
}
DocumentUpdaterHandler.reopenThread(
project_id,
doc_id,
thread_id,
user_id,
function (err, message) {
if (err != null) {
return next(err)
}
}
)
return ChatApiHandler.reopenThread(
project_id,
thread_id,
function (err, message) {
if (err != null) {
return next(err)
}
EditorRealTimeController.emitToRoom(
project_id,
'reopen-thread',
thread_id
)
return res.sendStatus(204)
}
)
},
deleteThread(req, res, next) {
const { project_id, doc_id, thread_id } = req.params
const user_id = SessionManager.getLoggedInUserId(req.session)
if (user_id == null) {
const err = new Error('no logged-in user')
return next(err)
}
return DocumentUpdaterHandler.deleteThread(
project_id,
doc_id,
thread_id,
user_id,
function (err, message) {
if (err != null) {
return next(err)
}
ChatApiHandler.deleteThread(
project_id,
thread_id,
function (err, message) {
if (err != null) {
return next(err)
}
EditorRealTimeController.emitToRoom(
project_id,
'delete-thread',
thread_id
)
return res.sendStatus(204)
}
)
}
)
},
}
module.exports = TrackChangesController

View File

@@ -1,217 +0,0 @@
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
< - 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']
<
< if (typeof(suppressNavbarRight) == "undefined")
< .navbar-collapse.collapse(collapse="navCollapsed")
< ul.nav.navbar-nav.navbar-right
< if (canDisplayAdminMenu || canDisplayAdminRedirect || canDisplaySplitTestMenu)
< li.dropdown(class="subdued", dropdown)
< a.dropdown-toggle(href, dropdown-toggle)
< | Admin
< b.caret
< ul.dropdown-menu
< if canDisplayAdminMenu
< li
< a(href="/admin") Manage Site
< li
< 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
< if canDisplaySplitTestMenu
< li
< a(href="/admin/split-test") Manage Feature Flags
< 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
< }
<
< 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 if child.isContactUs
< li
< a(ng-controller="ContactModal" ng-click="contactUsModal()" 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)}
< else
< | !{translate(child.text)}
< else
< li(class=item.class)
< if item.url
< a(
< href=item.url,
< class=item.class,
< event-tracking=item.event
< event-tracking-mb="true"
< event-tracking-trigger="click"
< ) !{translate(item.text)}
< else
< | !{translate(item.text)}
<
< // logged out
< if !getSessionUser()
< // 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
< a(
< href="/register"
< event-tracking="menu-clicked-register"
< event-tracking-action="clicked"
< 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')}
<
< // 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 {{ usersEmail }}
< li.divider.hidden-xs.hidden-sm
---
> a(href="/user/settings") #{translate('Account Settings')}
> if nav.showSubscriptionLink
141,149c79,84
< 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")
< 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.divider.hidden-xs.hidden-sm
> li
> form(method="POST" action="/logout")
> input(name='_csrf', type='hidden', value=csrfToken)
> button.btn-link.text-left.dropdown-menu-button #{translate('log_out')}

View File

@@ -1,10 +1,91 @@
259a260,268
> // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
39a40
> const TrackChangesController = require('./Features/TrackChanges/TrackChangesController')
218c219,226
< webRouter.get('/login', UserPagesController.loginPage)
---
> // If no LDAP Server is in use and no local db login then we can redirect the login
> // and just use OAUTH
> if ( (typeof process.env.LDAP_SERVER === typeof undefined) && (process.env.ALLOW_EMAIL_LOGIN === 'false') && (process.env.OAUTH2_ENABLED === 'true') ) {
> webRouter.get('/login', function (req, res, next) { res.redirect('/oauth/redirect') })
> } else {
> webRouter.get('/login', UserPagesController.loginPage)
> }
>
259a268,274
> if (process.env.OAUTH2_ENABLED === 'true') {
> webRouter.get('/oauth/redirect', AuthenticationController.oauth2Redirect)
> webRouter.get('/oauth/callback', AuthenticationController.oauth2Callback)
> AuthenticationController.addEndpointToLoginWhitelist('/oauth/redirect')
> AuthenticationController.addEndpointToLoginWhitelist('/oauth/callback')
> }
> // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
>
1352a1368,1436
> )
>
> webRouter.post('/project/:project_id/track_changes',
> AuthorizationMiddleware.blockRestrictedUserFromProject,
> AuthorizationMiddleware.ensureUserCanReadProject,
> TrackChangesController.trackChanges
> )
> webRouter.post('/project/:project_id/doc/:doc_id/changes/accept',
> AuthorizationMiddleware.blockRestrictedUserFromProject,
> AuthorizationMiddleware.ensureUserCanReadProject,
> TrackChangesController.acceptChanges
> )
> webRouter.get('/project/:project_id/ranges',
> AuthorizationMiddleware.blockRestrictedUserFromProject,
> AuthorizationMiddleware.ensureUserCanReadProject,
> TrackChangesController.getAllRanges
> )
> webRouter.get('/project/:project_id/changes/users',
> AuthorizationMiddleware.blockRestrictedUserFromProject,
> AuthorizationMiddleware.ensureUserCanReadProject,
> TrackChangesController.getChangesUsers
> )
> webRouter.get(
> '/project/:project_id/threads',
> AuthorizationMiddleware.blockRestrictedUserFromProject,
> AuthorizationMiddleware.ensureUserCanReadProject,
> TrackChangesController.getThreads
> )
> webRouter.post(
> '/project/:project_id/thread/:thread_id/messages',
> AuthorizationMiddleware.blockRestrictedUserFromProject,
> AuthorizationMiddleware.ensureUserCanReadProject,
> TrackChangesController.sendComment
> )
> webRouter.post(
> '/project/:project_id/thread/:thread_id/messages/:message_id/edit',
> AuthorizationMiddleware.blockRestrictedUserFromProject,
> AuthorizationMiddleware.ensureUserCanReadProject,
> TrackChangesController.editMessage
> )
> webRouter.delete(
> '/project/:project_id/thread/:thread_id/messages/:message_id',
> AuthorizationMiddleware.blockRestrictedUserFromProject,
> AuthorizationMiddleware.ensureUserCanReadProject,
> TrackChangesController.deleteMessage
> )
> webRouter.post(
> '/project/:project_id/doc/:doc_id/thread/:thread_id/resolve',
> AuthorizationMiddleware.blockRestrictedUserFromProject,
> AuthorizationMiddleware.ensureUserCanReadProject,
> TrackChangesController.resolveThread
> )
> webRouter.post(
> '/project/:project_id/thread/:thread_id/resolve',
> AuthorizationMiddleware.blockRestrictedUserFromProject,
> AuthorizationMiddleware.ensureUserCanReadProject,
> TrackChangesController.resolveThread
> )
> webRouter.post(
> '/project/:project_id/doc/:doc_id/thread/:thread_id/reopen',
> AuthorizationMiddleware.blockRestrictedUserFromProject,
> AuthorizationMiddleware.ensureUserCanReadProject,
> TrackChangesController.reopenThread
> )
> webRouter.delete(
> '/project/:project_id/doc/:doc_id/thread/:thread_id',
> AuthorizationMiddleware.blockRestrictedUserFromProject,
> AuthorizationMiddleware.ensureUserCanReadProject,
> TrackChangesController.deleteThread

View File

@@ -1,212 +0,0 @@
1c1
< extends ../layout-marketing
---
> extends ../layout
3,4c3,14
< block entrypointVar
< - entrypoint = 'pages/user/settings'
---
> block content
> .content.content-alt
> .container
> .row
> .col-md-12.col-lg-10.col-lg-offset-1
> if ssoError
> .alert.alert-danger
> | #{translate('sso_link_error')}: #{translate(ssoError)}
> .card
> .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")}
> form(async-form="settings", name="settingsForm", method="POST", action="/user/settings", novalidate)
> input(type="hidden", name="_csrf", value=csrfToken)
> if !hasFeature('affiliations')
> // show the email, non-editable
> .form-group
> label.control-label #{translate("email")}
> div.form-control(
> readonly="true",
> ng-non-bindable
> ) #{user.email}
>
> if shouldAllowEditingDetails
> .form-group
> label(for='firstName').control-label #{translate("first_name")}
> input.form-control(
> id="firstName"
> type='text',
> name='first_name',
> value=user.first_name
> ng-non-bindable
> )
> .form-group
> label(for='lastName').control-label #{translate("last_name")}
> input.form-control(
> id="lastName"
> type='text',
> name='last_name',
> value=user.last_name
> ng-non-bindable
> )
> .form-group
> form-messages(aria-live="polite" for="settingsForm")
> .alert.alert-success(ng-show="settingsForm.response.success")
> | #{translate("thanks_settings_updated")}
> .actions
> button.btn.btn-primary(
> type='submit',
> ng-disabled="settingsForm.$invalid"
> ) #{translate("update")}
> else
> .form-group
> label.control-label #{translate("first_name")}
> div.form-control(
> readonly="true",
> ng-non-bindable
> ) #{user.first_name}
> .form-group
> label.control-label #{translate("last_name")}
> div.form-control(
> readonly="true",
> ng-non-bindable
> ) #{user.last_name}
>
> .col-md-5.col-md-offset-1
> h3
> | Set Password for Email login
> p
> | 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
> | If you need any help, please contact your sysadmins.
>
> p #{translate("need_to_leave")}
> a(href, ng-click="deleteAccount()") #{translate("delete_your_account")}
>
>
>
> script(type='text/ng-template', id='deleteAccountModalTemplate')
> .modal-header
> h3 #{translate("delete_account")}
> div.modal-body#delete-account-modal
> p !{translate("delete_account_warning_message_3")}
> if settings.createV1AccountOnLogin && settings.overleaf
> p
> strong
> | 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.
>
> if settings.overleaf && !hasPassword
> p
> b
> | #[a(href="/user/password/reset", target='_blank') #{translate("delete_acct_no_existing_pw")}].
> else
> form(novalidate, name="deleteAccountForm")
> label #{translate('email')}
> input.form-control(
> type="text",
> autocomplete="off",
> placeholder="",
> ng-model="state.deleteText",
> focus-on="open",
> ng-keyup="checkValidation()"
> )
>
> label #{translate('password')}
> input.form-control(
> type="password",
> autocomplete="off",
> placeholder="",
> ng-model="state.password",
> ng-keyup="checkValidation()"
> )
>
> div.confirmation-checkbox-wrapper
> input(
> type="checkbox"
> ng-model="state.confirmV1Purge"
> ng-change="checkValidation()"
> ).pull-left
> label(style="display: inline") &nbsp;I have left, purged or imported my projects on Overleaf v1 (if any) &nbsp;
>
> div.confirmation-checkbox-wrapper
> input(
> type="checkbox"
> ng-model="state.confirmSharelatexDelete"
> ng-change="checkValidation()"
> ).pull-left
> label(style="display: inline") &nbsp;I understand this will delete all projects in my Overleaf v2 account (and ShareLaTeX account, if any) with email address #[em {{ userDefaultEmail }}]
>
> div(ng-if="state.error")
> div.alert.alert-danger(ng-switch="state.error.code")
> span(ng-switch-when="InvalidCredentialsError")
> | #{translate('email_or_password_wrong_try_again')}
> span(ng-switch-when="SubscriptionAdminDeletionError")
> | #{translate('subscription_admins_cannot_be_deleted')}
> span(ng-switch-when="UserDeletionError")
> | #{translate('user_deletion_error')}
> span(ng-switch-default)
> | #{translate('generic_something_went_wrong')}
> if settings.createV1AccountOnLogin && settings.overleaf
> div(ng-if="state.error && state.error.code == 'InvalidCredentialsError'")
> div.alert.alert-info
> | If you can't remember your password, or if you are using Single-Sign-On with another provider
> | to sign in (such as Twitter or Google), please
> | #[a(href="/user/password/reset", target='_blank') reset your password],
> | and try again.
> .modal-footer
> button.btn.btn-default(
> ng-click="cancel()"
> ) #{translate("cancel")}
> button.btn.btn-danger(
> ng-disabled="!state.isValid || state.inflight"
> ng-click="delete()"
> )
> span(ng-hide="state.inflight") #{translate("delete")}
> span(ng-show="state.inflight") #{translate("deleting")}...
>
> script(type='text/javascript').
> window.passwordStrengthOptions = !{StringHelper.stringifyJsonForScript(settings.passwordStrengthOptions || {})}

View File

@@ -1,15 +0,0 @@
#!/bin/sh
cd /usr/local/texlive/
if [ -d 2023 ]
then
cp -a 2023 2024
rm -f 2024/tlpkg/backups/*
cd 2024
wget https://mirror.ctan.org/systems/texlive/tlnet/update-tlmgr-latest.sh
sh update-tlmgr-latest.sh --accept
fi
tlmgr update --self --all
tlmgr install scheme-full
luaotfload-tool -fu

View File

@@ -1,58 +0,0 @@
#!/bin/bash
set -e
CONTAINER_FILE_PATHS=(
"/overleaf/services/web/app/src/Features/Authentication/AuthenticationManager.js"
"/overleaf/services/web/app/src/Features/Authentication/AuthenticationController.js"
"/overleaf/services/web/app/src/Features/Contacts/ContactController.js"
"/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-marketing.pug"
"/overleaf/services/web/app/views/admin/index.pug"
"/overleaf/services/web/app/views/admin/index.pug"
)
FILENAMES=(
"AuthenticationManager.js"
"AuthenticationController.js"
"ContactController.js"
"router.js"
"settings.pug"
"login.pug"
"navbar.pug"
"navbar-marketing.pug"
"admin-index.pug"
"admin-sysadmin.pug"
)
if [ "${#CONTAINER_FILE_PATHS[@]}" -ne "${#FILENAMES[@]}" ]; then
echo "Error: The number of source files and target filenames does not match."
exit 1
fi
HOST_TARGET_PATH="ldap-overleaf-sl/sharelatex_ori"
if [ "$#" -ne 1 ]; then
echo "Usage: $0 [version]"
exit 1
else
VERSION=$1
fi
CONTAINER_NAME="tmp_sharelatex_for_extract_files"
IMAGE="sharelatex/sharelatex:$VERSION"
for i in "${!CONTAINER_FILE_PATHS[@]}"; do
file_path="${CONTAINER_FILE_PATHS[i]}"
new_filename="${FILENAMES[i]}"
new_target_path="$HOST_TARGET_PATH/$new_filename"
echo " copy: $new_target_path"
docker cp $CONTAINER_NAME:$file_path $new_target_path
done
#
#echo "Stopping and removing container..."
#docker stop $CONTAINER_NAME
#docker rm $CONTAINER_NAME

View File

@@ -6,6 +6,7 @@ CONTAINER_FILE_PATHS=(
"/overleaf/services/web/app/src/Features/Authentication/AuthenticationManager.js"
"/overleaf/services/web/app/src/Features/Authentication/AuthenticationController.js"
"/overleaf/services/web/app/src/Features/Contacts/ContactController.js"
"/overleaf/services/web/app/src/Features/Project/ProjectEditorHandler.js"
"/overleaf/services/web/app/src/router.js"
"/overleaf/services/web/app/views/user/settings.pug"
"/overleaf/services/web/app/views/user/login.pug"
@@ -19,6 +20,7 @@ FILENAMES=(
"AuthenticationManager.js"
"AuthenticationController.js"
"ContactController.js"
"ProjectEditorHandler.js"
"router.js"
"settings.pug"
"login.pug"
@@ -60,13 +62,13 @@ docker run -d --name $CONTAINER_NAME $IMAGE
echo "Waiting for container to start up..."
sleep 10
#for i in "${!CONTAINER_FILE_PATHS[@]}"; do
# file_path="${CONTAINER_FILE_PATHS[i]}"
# new_filename="${FILENAMES[i]}"
# new_target_path="$HOST_TARGET_PATH/$new_filename"
# docker cp $CONTAINER_NAME:$file_path $new_target_path
#done
#
#echo "Stopping and removing container..."
#docker stop $CONTAINER_NAME
#docker rm $CONTAINER_NAME
for i in "${!CONTAINER_FILE_PATHS[@]}"; do
file_path="${CONTAINER_FILE_PATHS[i]}"
new_filename="${FILENAMES[i]}"
new_target_path="$HOST_TARGET_PATH/$new_filename"
docker cp $CONTAINER_NAME:$file_path $new_target_path
done
echo "Stopping and removing container..."
docker stop $CONTAINER_NAME
docker rm $CONTAINER_NAME

View File

@@ -8,7 +8,6 @@ for filename in $(ls $MODIFIED_DIR); do
raw_file="$ORI_DIR/$filename"
if [ -f "$raw_file" ]; then
echo "working on $raw_file"
diff_output="$DIFFS_DIR/${filename}.diff"
diff "$raw_file" "$MODIFIED_DIR/$filename" > "$diff_output"
else