mirror of
https://github.com/smhaller/ldap-overleaf-sl.git
synced 2025-01-25 06:04:48 +01:00
Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d5633ea430 | ||
|
|
8eaaf5e6c0 | ||
|
|
b6eb20b71d | ||
|
|
63e2ee30e9 | ||
|
|
5d31ca8ec3 | ||
|
|
cc29b6f89e | ||
|
|
b3ef70bcd4 | ||
|
|
0a70d7c1e7 | ||
|
|
ddd0a14b3e |
52
README.md
52
README.md
@@ -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 Initial idea for this implementation was taken from
|
||||
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:
|
||||
@@ -50,7 +66,7 @@ it tries to authenticate against the specified LDAP server.
|
||||
*Important:*
|
||||
Sharelatex/Overleaf uses the email address to identify users: If you change the email in the LDAP/OAuth you have to update the corresponding field in the mongo db.
|
||||
|
||||
```bash
|
||||
```
|
||||
docker exec -it mongo /bin/bash
|
||||
mongo
|
||||
use sharelatex
|
||||
@@ -86,7 +102,7 @@ COLLAB_TEXT=Direct share with collaborators is enabled only for activated users!
|
||||
ADMIN_IS_SYSADMIN=false
|
||||
```
|
||||
|
||||
*LOGIN_TEXT* : displayed instead of email-address field (login.pug) <br/>
|
||||
*LOGIN_TEXT* : displayed instead of email-adress field (login.pug) <br/>
|
||||
*COLLAB_TEXT* : displayed for email invitation (share.pug)<br/>
|
||||
*ADMIN_IS_SYSADMIN* : false or true (if ``false`` isAdmin group is allowed to add users to sharelatex and post messages. if ``true`` isAdmin group is allowed to logout other users / set maintenance mode)
|
||||
|
||||
@@ -158,8 +174,6 @@ OAUTH2_TOKEN_URL: YOUR_OAUTH2_TOKEN_URL
|
||||
# One of ["application/x-www-form-urlencoded", "application/json"]
|
||||
# Default "application/x-www-form-urlencoded"
|
||||
OAUTH2_TOKEN_CONTENT_TYPE: "application/x-www-form-urlencoded"
|
||||
# OAUTH2_STATE_LENGTH: Length of the OAuth2 state
|
||||
OAUTH2_STATE_LENGTH: 6
|
||||
# Fetch user profile api endpoint
|
||||
OAUTH2_PROFILE_URL: YOUR_OAUTH2_PROFILE_URL
|
||||
|
||||
@@ -219,39 +233,43 @@ Install the docker engine: https://docs.docker.com/engine/install/
|
||||
|
||||
Install docker-compose:
|
||||
|
||||
(If you need pip: apt install python3-pip)
|
||||
(if you need pip: apt install python3-pip)
|
||||
|
||||
```bash
|
||||
```
|
||||
pip install docker-compose
|
||||
```
|
||||
|
||||
Use the following commands to generate the ldap-overleaf-sl docker image:
|
||||
use the following commands:
|
||||
|
||||
```bash
|
||||
```
|
||||
bash scripts/extract_files.sh 4.2.0
|
||||
bash scripts/apply_diffs.sh
|
||||
make
|
||||
```
|
||||
|
||||
Use the following command to create a network for the docker instances:
|
||||
to generate the ldap-overleaf-sl docker image.
|
||||
|
||||
```bash
|
||||
use the command:
|
||||
|
||||
```
|
||||
docker network create web
|
||||
```
|
||||
|
||||
to create a network for the docker instances.
|
||||
|
||||
### Startup
|
||||
|
||||
#### Using without reverse proxy
|
||||
#### Using without proxy
|
||||
|
||||
In most cases, you should use a gateway reverse proxy for your requests (see the next section), as they can offer many benefits such as enhanced security and easier SSL certificate updates. This simple startup method is used for 1. Development 2. When you know what you're doing, for example, when there is an additional gateway layer outside your server.
|
||||
|
||||
Start docker containers:
|
||||
|
||||
```bash
|
||||
```
|
||||
docker-compose up -d
|
||||
```
|
||||
|
||||
#### Using reverse proxy
|
||||
#### Using proxy
|
||||
|
||||
There are 2 different ways of starting either using Traefik or using Certbot. Adapt the one you want to use.
|
||||
|
||||
@@ -259,7 +277,7 @@ There are 2 different ways of starting either using Traefik or using Certbot. Ad
|
||||
|
||||
Then start docker containers (with loadbalancer):
|
||||
|
||||
```bash
|
||||
```
|
||||
export NUMINSTANCES=1
|
||||
docker-compose -f docker-compose.traefik.yml up -d --scale sharelatex=$NUMINSTANCES
|
||||
```
|
||||
@@ -268,7 +286,7 @@ docker-compose -f docker-compose.traefik.yml up -d --scale sharelatex=$NUMINSTAN
|
||||
|
||||
Enable line 65/66 and 69/70 in ldapoverleaf-sl/Dockerfile and ``make`` again.
|
||||
|
||||
```bash
|
||||
```
|
||||
docker-compose -f docker-compose.certbot.yml up -d
|
||||
```
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -91,7 +91,6 @@ services:
|
||||
# OAUTH2_AUTHORIZATION_URL: YOUR_OAUTH2_AUTHORIZATION_URL
|
||||
# OAUTH2_TOKEN_URL: YOUR_OAUTH2_TOKEN_URL
|
||||
# OAUTH2_TOKEN_CONTENT_TYPE: # One of ['application/x-www-form-urlencoded', 'application/json']
|
||||
# OAUTH2_STATE_LENGTH: Length of the OAuth2 state (default: 6)
|
||||
# OAUTH2_PROFILE_URL: YOUR_OAUTH2_PROFILE_URL
|
||||
# OAUTH2_USER_ATTR_EMAIL: email
|
||||
# OAUTH2_USER_ATTR_UID: id
|
||||
@@ -101,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
|
||||
|
||||
@@ -112,7 +111,7 @@ services:
|
||||
|
||||
mongo:
|
||||
restart: always
|
||||
image: mongo:4.4
|
||||
image: mongo:5.0
|
||||
container_name: mongo
|
||||
expose:
|
||||
- 27017
|
||||
@@ -127,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:
|
||||
|
||||
@@ -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
|
||||
@@ -172,7 +172,6 @@ services:
|
||||
# OAUTH2_AUTHORIZATION_URL: YOUR_OAUTH2_AUTHORIZATION_URL
|
||||
# OAUTH2_TOKEN_URL: YOUR_OAUTH2_TOKEN_URL
|
||||
# OAUTH2_TOKEN_CONTENT_TYPE: # One of ['application/x-www-form-urlencoded', 'application/json']
|
||||
# OAUTH2_STATE_LENGTH: Length of the OAuth2 state (default: 6)
|
||||
# OAUTH2_PROFILE_URL: YOUR_OAUTH2_PROFILE_URL
|
||||
# OAUTH2_USER_ATTR_EMAIL: email
|
||||
# OAUTH2_USER_ATTR_UID: id
|
||||
@@ -182,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
|
||||
|
||||
@@ -193,7 +192,7 @@ services:
|
||||
|
||||
mongo:
|
||||
restart: always
|
||||
image: mongo:4.4
|
||||
image: mongo:5.0
|
||||
container_name: mongo
|
||||
expose:
|
||||
- 27017
|
||||
@@ -216,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:
|
||||
|
||||
@@ -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 <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"
|
||||
|
||||
# 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
|
||||
@@ -89,7 +89,6 @@ services:
|
||||
# OAUTH2_AUTHORIZATION_URL: YOUR_OAUTH2_AUTHORIZATION_URL
|
||||
# OAUTH2_TOKEN_URL: YOUR_OAUTH2_TOKEN_URL
|
||||
# OAUTH2_TOKEN_CONTENT_TYPE: # One of ['application/x-www-form-urlencoded', 'application/json']
|
||||
# OAUTH2_STATE_LENGTH: Length of the OAuth2 state (default: 6)
|
||||
# OAUTH2_PROFILE_URL: YOUR_OAUTH2_PROFILE_URL
|
||||
# OAUTH2_USER_ATTR_EMAIL: email
|
||||
# OAUTH2_USER_ATTR_UID: id
|
||||
@@ -99,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
|
||||
|
||||
@@ -110,7 +109,7 @@ services:
|
||||
|
||||
mongo:
|
||||
restart: always
|
||||
image: mongo:4.4
|
||||
image: mongo:5.0
|
||||
container_name: mongo
|
||||
expose:
|
||||
- 27017
|
||||
@@ -125,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:
|
||||
|
||||
@@ -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
|
||||
@@ -20,9 +20,11 @@ RUN npm install -g npm && \
|
||||
npm install ldap-escape ldapts-search ldapts@3.2.4 && \
|
||||
# npm install bcrypt@5.0.0 && \
|
||||
apt-get update && \
|
||||
apt-get -y install libxml-libxslt-perl cpanminus libbtparse2 python-pygments && \
|
||||
apt-get -y install libxml-libxslt-perl cpanminus libbtparse2 python3-pygments
|
||||
# now install latest texlive2023 from tlmgr
|
||||
tlmgr update --self --all && \
|
||||
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/*
|
||||
@@ -35,14 +37,30 @@ ENV PATH="/usr/local/texlive/2023/bin/x86_64-linux:${PATH};"
|
||||
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
|
||||
|
||||
@@ -1,14 +1,11 @@
|
||||
268a269,366
|
||||
>
|
||||
> // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
|
||||
300a301,394
|
||||
> oauth2Redirect(req, res, next) {
|
||||
> // random state
|
||||
> const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
|
||||
> const stateLength = Number(process.env.OAUTH2_STATE_LENGTH || 6)
|
||||
> const state = new Array(stateLength).fill(0).map(() => characters.charAt(Math.floor(Math.random() * characters.length))).join("")
|
||||
> 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`
|
||||
@@ -35,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)
|
||||
@@ -96,4 +93,3 @@
|
||||
> console.error("Fails to access by OAuth2: " + String(e))
|
||||
> }
|
||||
> },
|
||||
> // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|
||||
|
||||
@@ -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 @@
|
||||
> }
|
||||
> })
|
||||
> },
|
||||
> // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
11c11
|
||||
< trackChangesAvailable: false,
|
||||
---
|
||||
> trackChangesAvailable: true,
|
||||
60c60
|
||||
< trackChanges: false,
|
||||
---
|
||||
> trackChanges: true,
|
||||
308
ldap-overleaf-sl/sharelatex_diff/TrackChangesController.js
Normal file
308
ldap-overleaf-sl/sharelatex_diff/TrackChangesController.js
Normal 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
|
||||
@@ -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')}
|
||||
@@ -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
|
||||
|
||||
@@ -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") I have left, purged or imported my projects on Overleaf v1 (if any)
|
||||
>
|
||||
> div.confirmation-checkbox-wrapper
|
||||
> input(
|
||||
> type="checkbox"
|
||||
> ng-model="state.confirmSharelatexDelete"
|
||||
> ng-change="checkValidation()"
|
||||
> ).pull-left
|
||||
> label(style="display: inline") 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 || {})}
|
||||
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user