diff --git a/.env b/.env
index 8124bba..4a9fe4f 100644
--- a/.env
+++ b/.env
@@ -4,3 +4,4 @@ MYMAIL=MYEMAIL@MYDOMAIN.TLD
MYDATA=/data
LOGIN_TEXT=username
COLLAB_TEXT=Direct share with collaborators is enabled only for activated users!
+ADMIN_IS_SYSADMIN=false
diff --git a/Makefile b/Makefile
index 437b00c..182ccf7 100644
--- a/Makefile
+++ b/Makefile
@@ -3,6 +3,7 @@ include .env
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" ldap-overleaf-sl
clean: check_clean
diff --git a/README.md b/README.md
index a6fea7b..7cc5240 100644
--- a/README.md
+++ b/README.md
@@ -55,21 +55,25 @@ MYDATA=/data
- sharelatex: all projects, tmp files, user files templates and ...
- letsencrypt: https certificates
-*MYDOMAIN* is the FQDN for sharelatex and traefik (letsencrypt)
-*MYDOMAIN*:8443 Traefik Dashboard - Login uses traefik/user.htpasswd : user:admin pass:adminPass change this (e.g. generate a password with htpasswd)
+*MYDOMAIN* is the FQDN for sharelatex and traefik (letsencrypt) or certbot
+*MYDOMAIN*:8443 Traefik Dashboard (docker-compose-traefik.yml) - Login uses traefik/user.htpasswd : user:admin pass:adminPass change this (e.g. generate a password with htpasswd)
*MYMAIL* is the admin mailaddress
```
LOGIN_TEXT=username
COLLAB_TEXT=Direct share with collaborators is enabled only for activated users!
+ADMIN_IS_SYSADMIN=false
```
*LOGIN_TEXT* : displayed instead of email-adress field (login.pug)
-*COLLAB_TEXT* : displayed for email invitation (share.pug)
+*COLLAB_TEXT* : displayed for email invitation (share.pug)
+*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)
### LDAP Configuration
-Edit [docker-compose.yml](docker-compose.yml) to fit your local setup.
+Edit [docker-compose.treafik.yml](docker-compose.traefik.yml) or [docker-compose.certbot.yml](docker-compose.certbot.yml) to fit your local setup.
+
+
```
LDAP_SERVER: ldaps://LDAPSERVER:636
@@ -104,7 +108,7 @@ LDAP_CONTACTS: 'true'
### Sharelatex Configuration
-Edit SHARELATEX_ environment variables in [docker-compose.yml](docker-compose.yml) to fit your local setup
+Edit SHARELATEX_ environment variables in [docker-compose.traefik.yml](docker-compose.traefik.yml) or [docker-compose.certbot.yml](docker-compose.certbot.yml) to fit your local setup
(e.g. proper SMTP server, Header, Footer, App Name,...). See https://github.com/overleaf/overleaf/wiki/Quick-Start-Guide for more details.
## Installation, Usage and Inital startup
@@ -132,9 +136,23 @@ docker network create web
```
to create a network for the docker instances.
+
+## Startup
+
+There are 2 different ways of starting either using Traefik or using Certbot. Adapt the one you want to use.
+
+### Using Traefik
+
Then start docker containers (with loadbalancer):
```
export NUMINSTANCES=1
-docker-compose up -d --scale sharelatex=$NUMINSTANCES
+docker-compose -f docker-compose.traefik.yml up -d --scale sharelatex=$NUMINSTANCES
+```
+
+### Using Certbot
+Enable line 65/66 and 69/70 in ldapoverleaf-sl/Dockerfile and ``make`` again.
+
+```
+docker-compose -f docker-compose.certbot.yml up -d
```
diff --git a/docker-compose.certbot.yml b/docker-compose.certbot.yml
new file mode 100644
index 0000000..ecb2f32
--- /dev/null
+++ b/docker-compose.certbot.yml
@@ -0,0 +1,142 @@
+version: '2.2'
+services:
+ sharelatex:
+ restart: always
+ image: ldap-overleaf-sl
+ container_name: ldap-overleaf-sl
+ depends_on:
+ mongo:
+ condition: service_healthy
+ redis:
+ condition: service_healthy
+ simple-certbot:
+ condition: service_started
+ privileged: false
+ ports:
+ - 443:443
+ links:
+ - mongo
+ - redis
+ - simple-certbot
+ volumes:
+ - ${MYDATA}/sharelatex:/var/lib/sharelatex
+ - ${MYDATA}/letsencrypt:/etc/letsencrypt
+ - ${MYDATA}/letsencrypt/live/${MYDOMAIN}/:/etc/letsencrypt/certs/domain
+ environment:
+ SHARELATEX_APP_NAME: Overleaf
+ SHARELATEX_MONGO_URL: mongodb://mongo/sharelatex
+ SHARELATEX_SITE_URL: https://${MYDOMAIN}
+ SHARELATEX_NAV_TITLE: Overleaf - run by ${MYDOMAIN}
+ #SHARELATEX_HEADER_IMAGE_URL: https://${MYDOMAIN}/logo.svg
+ SHARELATEX_ADMIN_EMAIL: ${MYMAIL}
+ SHARELATEX_LEFT_FOOTER: '[{"text": "Powered by ShareLaTeX 2016"} ]'
+ SHARELATEX_RIGHT_FOOTER: '[{"text": "LDAP Overleaf (beta)"} ]'
+ SHARELATEX_EMAIL_FROM_ADDRESS: "noreply@${MYDOMAIN}"
+ # SHARELATEX_EMAIL_AWS_SES_ACCESS_KEY_ID:
+ # SHARELATEX_EMAIL_AWS_SES_SECRET_KEY:
+ SHARELATEX_EMAIL_SMTP_HOST: smtp.${MYDOMAIN}
+ SHARELATEX_EMAIL_SMTP_PORT: 587
+ SHARELATEX_EMAIL_SMTP_SECURE: 'false'
+ # SHARELATEX_EMAIL_SMTP_USER:
+ # SHARELATEX_EMAIL_SMTP_PASS:
+ # SHARELATEX_EMAIL_SMTP_TLS_REJECT_UNAUTH: true
+ # SHARELATEX_EMAIL_SMTP_IGNORE_TLS: false
+ SHARELATEX_CUSTOM_EMAIL_FOOTER: "This system is run by ${MYDOMAIN} - please contact ${MYMAIL} if you experience any issues."
+
+ # 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'
+
+ SHARELATEX_SECURE_COOKIE: 'true'
+ SHARELATEX_BEHIND_PROXY: 'true'
+
+ LDAP_SERVER: ldaps://LDAPSERVER:636
+ LDAP_SERVER: ldaps://LDAPSERVER:636
+ LDAP_BASE: ou=people,dc=DOMAIN,dc=TLD
+ LDAP_BINDDN: ou=someunit,ou=people,dc=DOMAIN,dc=TLS
+ # By default tries to bind directly with the ldap user - this user has to be in the LDAP GROUP
+ LDAP_GROUP_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 adminsy can send
+ # system wide messages.
+ #LDAP_ADMIN_GROUP_FILTER: '(memberof=cn=ADMINGROUPNAME,ou=groups,dc=DOMAIN,dc=TLD)'
+ ALLOW_EMAIL_LOGIN: 'false'
+
+ # All users in the LDAP_GROUP_FILTER are loaded from the ldap server into contacts.
+ # This LDAP search happens without bind. If you want this and your LDAP needs a bind you can
+ # adapt this in the function getLdapContacts() in ContactsController.js (lines 82 - 107)
+ LDAP_CONTACTS: 'false'
+
+ # Same property, unfortunately with different names in
+ # different locations
+ SHARELATEX_REDIS_HOST: redis
+ REDIS_HOST: redis
+ REDIS_PORT: 6379
+
+ ENABLED_LINKED_FILE_TYPES: 'url,project_file'
+
+ # Enables Thumbnail generation using ImageMagick
+ ENABLE_CONVERSIONS: 'true'
+
+ mongo:
+ restart: always
+ image: mongo
+ container_name: mongo
+ ports:
+ - 27017
+ volumes:
+ - ${MYDATA}/mongo_data:/data/db
+ healthcheck:
+ test: echo 'db.stats().ok' | mongo localhost:27017/test --quiet
+ interval: 10s
+ timeout: 10s
+ retries: 5
+
+ redis:
+ restart: always
+ image: redis:5.0.0
+ container_name: redis
+ # modify to get rid of the redis issue #35 and #19 with a better solution
+ # WARNING: /proc/sys/net/core/somaxconn is set to the lower value of 128.
+ # for vm overcommit: enable first on host system
+ # sysctl vm.overcommit_memory=1 (and add it to rc.local)
+ # then you do not need it in the redis container
+ sysctls:
+ - net.core.somaxconn=65535
+ # - vm.overcommit_memory=1
+ ports:
+ - 6379
+ volumes:
+ - ${MYDATA}/redis_data:/data
+ healthcheck:
+ test: ["CMD", "redis-cli", "ping"]
+ interval: 10s
+ timeout: 5s
+ retries: 5
+
+
+ simple-certbot:
+ restart: always
+ image: certbot/certbot
+ container_name: simple-certbot
+ ports:
+ - 80:80
+ volumes:
+ - ${MYDATA}/letsencrypt:/etc/letsencrypt
+ # a bit hacky but this docker image uses very little disk-space
+ # best practices for ssl and nginx are set in the ldap-overleaf-sl Dockerfile
+ entrypoint:
+ - "/bin/sh"
+ - -c
+ - |
+ trap exit TERM;\
+ certbot certonly --standalone -d ${MYDOMAIN} --agree-tos -m ${MYMAIL} -n ; \
+ while :; do certbot renew; sleep 240h & wait $${!}; done;
+
+
diff --git a/docker-compose.yml b/docker-compose.traefik.yml
similarity index 93%
rename from docker-compose.yml
rename to docker-compose.traefik.yml
index 8cc272f..9c1546e 100644
--- a/docker-compose.yml
+++ b/docker-compose.traefik.yml
@@ -77,7 +77,6 @@ services:
links:
- mongo
- redis
- #- simple-certbot
volumes:
- ${MYDATA}/sharelatex:/var/lib/sharelatex
- ${MYDATA}/letsencrypt:/etc/letsencrypt:ro
@@ -211,26 +210,6 @@ services:
networks:
- web
-
-# simple-certbot:
-# restart: always
-# image: certbot/certbot
-# container_name: simple-certbot
-# ports:
-# - 80:80
-# volumes:
-# - ${MYDATA}/letsencrypt:/etc/letsencrypt
-# # a bit hacky but this docker image uses very little disk-space
-# # best practices for ssl and nginx are set in the ldap-overleaf-sl Dockerfile
-# entrypoint:
-# - "/bin/sh"
-# - -c
-# - |
-# trap exit TERM;\
-# certbot certonly --standalone -d ${MYDOMAIN} --agree-tos -m ${MYMAIL} -n ; \
-# while :; do certbot renew; sleep 240h & wait $${!}; done;
-#
-
networks:
web:
external: true
diff --git a/ldap-overleaf-sl/Dockerfile b/ldap-overleaf-sl/Dockerfile
index f1231ef..a499091 100644
--- a/ldap-overleaf-sl/Dockerfile
+++ b/ldap-overleaf-sl/Dockerfile
@@ -1,4 +1,4 @@
-FROM sharelatex/sharelatex:2.5.2
+FROM sharelatex/sharelatex:2.6.1
# FROM sharelatex/sharelatex:latest
# latest might not be tested
# e.g. the AuthenticationManager.js script had to be adapted after versions 2.3.1
@@ -7,7 +7,8 @@ LABEL version="0.1"
# passed from .env (via make)
ARG collab_text
-ARG login_text
+ARG login_text
+ARG admin_is_sysadmin
# set workdir (might solve issue #2 - see https://stackoverflow.com/questions/57534295/)
WORKDIR /var/www/sharelatex/web
@@ -16,6 +17,7 @@ WORKDIR /var/www/sharelatex/web
RUN npm install -g npm
# clean cache (might solve issue #2)
#RUN npm cache clean --force
+RUN npm install ldap-escape
RUN npm install ldapts-search
RUN npm install ldapts
RUN npm install ldap-escape
@@ -50,24 +52,27 @@ COPY sharelatex/navbar.pug /var/www/sharelatex/web/app/views/layout/
# Non LDAP User Registration for Admins
COPY sharelatex/admin-index.pug /var/www/sharelatex/web/app/views/admin/index.pug
+COPY sharelatex/admin-sysadmin.pug /tmp/admin-sysadmin.pug
+RUN if [ "${admin_is_sysadmin}" = "true" ] ; then cp /tmp/admin-sysadmin.pug /var/www/sharelatex/web/app/views/admin/index.pug ; else rm /tmp/admin-sysadmin.pug ; fi
+
RUN rm /var/www/sharelatex/web/app/views/admin/register.pug
### To remove comments entirly (bug https://github.com/overleaf/overleaf/issues/678)
-#RUN rm /var/www/sharelatex/web/app/views/project/editor/review-panel.pug
+RUN rm /var/www/sharelatex/web/app/views/project/editor/review-panel.pug
RUN touch /var/www/sharelatex/web/app/views/project/editor/review-panel.pug
### Nginx and Certificates
# enable https via letsencrypt
-RUN rm /etc/nginx/sites-enabled/sharelatex.conf
-COPY nginx/sharelatex.conf /etc/nginx/sites-enabled/sharelatex.conf
+#RUN rm /etc/nginx/sites-enabled/sharelatex.conf
+#COPY nginx/sharelatex.conf /etc/nginx/sites-enabled/sharelatex.conf
# get maintained best practice ssl from certbot
-RUN wget https://raw.githubusercontent.com/certbot/certbot/master/certbot-nginx/certbot_nginx/_internal/tls_configs/options-ssl-nginx.conf -O /etc/nginx/options-ssl-nginx.conf
-RUN wget https://raw.githubusercontent.com/certbot/certbot/master/certbot/certbot/ssl-dhparams.pem -O /etc/nginx/ssl-dhparams.pem
+#RUN wget https://raw.githubusercontent.com/certbot/certbot/master/certbot-nginx/certbot_nginx/_internal/tls_configs/options-ssl-nginx.conf -O /etc/nginx/options-ssl-nginx.conf
+#RUN wget https://raw.githubusercontent.com/certbot/certbot/master/certbot/certbot/ssl-dhparams.pem -O /etc/nginx/ssl-dhparams.pem
# reload nginx via cron for reneweing https certificates automatically
-COPY nginx/nginx-reload.sh /etc/cron.weekly/
-RUN chmod 0744 /etc/cron.weekly/nginx-reload.sh
+#COPY nginx/nginx-reload.sh /etc/cron.weekly/
+#RUN chmod 0744 /etc/cron.weekly/nginx-reload.sh
## extract certificates from acme.json?
# COPY nginx/nginx-cert.sh /etc/cron.weekly/
diff --git a/ldap-overleaf-sl/sharelatex/AuthenticationManager.js b/ldap-overleaf-sl/sharelatex/AuthenticationManager.js
index 53a18f9..fedbdb8 100644
--- a/ldap-overleaf-sl/sharelatex/AuthenticationManager.js
+++ b/ldap-overleaf-sl/sharelatex/AuthenticationManager.js
@@ -11,6 +11,7 @@ const util = require('util')
const { Client } = require('ldapts');
const ldapEscape = require('ldap-escape');
+
// https://www.npmjs.com/package/@overleaf/o-error
// have a look if we can do nice error messages.
@@ -110,7 +111,7 @@ const AuthenticationManager = {
},
validateEmail(email) {
- // we use the emailadress from the ldap
+ // we use the emailadress from the ldap
// therefore we do not enforce checks here
const parsed = EmailHelper.parseEmail(email)
//if (!parsed) {
@@ -203,7 +204,7 @@ const AuthenticationManager = {
//if (!user || !user.email || !user._id) {
// return callback(new Error('invalid user object'))
//}
-
+
console.log("Setting pass for user: " + JSON.stringify(user))
const validationError = this.validatePassword(password, user.email)
if (validationError) {
@@ -273,10 +274,10 @@ const AuthenticationManager = {
const ldap_reader = process.env.LDAP_BIND_USER
const ldap_reader_pass = process.env.LDAP_BIND_PW
const ldap_base = process.env.LDAP_BASE
- var mail = query.email
- var uid = query.email.split('@')[0]
- const filterstr = '(&' + process.env.LDAP_GROUP_FILTER + '(' + ldapEscape.filter`uid=${uid}` + '))'
- var userDn = "" //'uid=' + uid + ',' + ldap_bd;
+ var uid = query.email
+ const filterstr = process.env.LDAP_GROUP_FILTER.replaceAll('%u', ldapEscape.filter`${uid}`)
+ const userDn = ldapEscape.filter`uid=${uid}` + ',' + ldap_bd;
+ var mail = ""
var firstname = ""
var lastname = ""
var isAdmin = false
@@ -306,15 +307,15 @@ const AuthenticationManager = {
}
} catch (ex) {
console.log("An Error occured while getting user data during ldapsearch: " + String(ex))
- await client.unbind();
- return callback(null, null)
+ await client.unbind();
+ return callback(null, null)
}
try {
// if admin filter is set - only set admin for user in ldap group
// does not matter - admin is deactivated: managed through ldap
if (process.env.LDAP_ADMIN_GROUP_FILTER) {
- const adminfilter = '(&' + process.env.LDAP_ADMIN_GROUP_FILTER + '(' +ldapEscape.filter`uid=${uid}` + '))'
+ const adminfilter = process.env.LDAP_ADMIN_GROUP_FILTER.replaceAll('%u', ldapEscape.filter`${uid}`)
adminEntry = await client.search(ldap_base, {
scope: 'sub',
filter: adminfilter,
diff --git a/ldap-overleaf-sl/sharelatex/admin-sysadmin.pug b/ldap-overleaf-sl/sharelatex/admin-sysadmin.pug
new file mode 100644
index 0000000..c7131a3
--- /dev/null
+++ b/ldap-overleaf-sl/sharelatex/admin-sysadmin.pug
@@ -0,0 +1,79 @@
+extends ../layout
+
+block content
+ .content.content-alt
+ .container
+ .row
+ .col-xs-12
+ .card(ng-controller="RegisterUsersController")
+ .page-header
+ h1 Admin Panel
+ tabset(ng-cloak)
+ tab(heading="System Messages")
+ each message in systemMessages
+ .alert.alert-info.row-spaced(ng-non-bindable) #{message.content}
+ hr
+ form(method='post', action='/admin/messages')
+ input(name="_csrf", type="hidden", value=csrfToken)
+ .form-group
+ label(for="content")
+ input.form-control(name="content", type="text", placeholder="Message...", required)
+ button.btn.btn-primary(type="submit") Post Message
+ hr
+ form(method='post', action='/admin/messages/clear')
+ input(name="_csrf", type="hidden", value=csrfToken)
+ button.btn.btn-danger(type="submit") Clear all messages
+
+
+ tab(heading="Register non LDAP User")
+ form.form
+ .row
+ .col-md-4.col-xs-8
+ input.form-control(
+ name="email",
+ type="text",
+ placeholder="jane@example.com, joe@example.com",
+ ng-model="inputs.emails",
+ on-enter="registerUsers()"
+ )
+ .col-md-8.col-xs-4
+ button.btn.btn-primary(ng-click="registerUsers()") #{translate("register")}
+
+ .row-spaced(ng-show="error").ng-cloak.text-danger
+ p Sorry, an error occured
+
+ .row-spaced(ng-show="users.length > 0").ng-cloak.text-success
+ p We've sent out welcome emails to the registered users.
+ p You can also manually send them URLs below to allow them to reset their password and log in for the first time.
+ p (Password reset tokens will expire after one week and the user will need registering again).
+
+ hr(ng-show="users.length > 0").ng-cloak
+ table(ng-show="users.length > 0").table.table-striped.ng-cloak
+ tr
+ th #{translate("email")}
+ th Set Password Url
+ tr(ng-repeat="user in users")
+ td {{ user.email }}
+ td(style="word-break: break-all;") {{ user.setNewPasswordUrl }}
+ tab(heading="Open/Close Editor" bookmarkable-tab="open-close-editor")
+ if hasFeature('saas')
+ | The "Open/Close Editor" feature is not available in SAAS.
+ else
+ .row-spaced
+ form(method='post',action='/admin/closeEditor')
+ input(name="_csrf", type="hidden", value=csrfToken)
+ button.btn.btn-danger(type="submit") Close Editor
+ p.small Will stop anyone opening the editor. Will NOT disconnect already connected users.
+
+ .row-spaced
+ form(method='post',action='/admin/disconnectAllUsers')
+ input(name="_csrf", type="hidden", value=csrfToken)
+ button.btn.btn-danger(type="submit") Disconnect all users
+ p.small Will force disconnect all users with the editor open. Make sure to close the editor first to avoid them reconnecting.
+
+ .row-spaced
+ form(method='post',action='/admin/openEditor')
+ input(name="_csrf", type="hidden", value=csrfToken)
+ button.btn.btn-danger(type="submit") Reopen Editor
+ p.small Will reopen the editor after closing.
+