mirror of
https://git.unistra.fr/aius/root/ldap-overleaf-sl.git
synced 2025-05-04 19:55:26 +02:00
Merge remote-tracking branch 'shasler/ldap-replace-uid' into fix-uid
This commit is contained in:
commit
f25cb05c8b
8 changed files with 271 additions and 45 deletions
1
.env
1
.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
|
||||
|
|
1
Makefile
1
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
|
||||
|
|
30
README.md
30
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) <br/>
|
||||
*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 <br/>
|
||||
*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) <br/>
|
||||
*COLLAB_TEXT* : displayed for email invitation (share.pug)
|
||||
*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)
|
||||
|
||||
|
||||
### 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
|
||||
```
|
||||
|
||||
|
|
142
docker-compose.certbot.yml
Normal file
142
docker-compose.certbot.yml
Normal file
|
@ -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 <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."
|
||||
|
||||
# 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;
|
||||
|
||||
|
|
@ -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
|
|
@ -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
|
||||
|
@ -8,6 +8,7 @@ LABEL version="0.1"
|
|||
# passed from .env (via make)
|
||||
ARG collab_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/
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
@ -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,
|
||||
|
|
79
ldap-overleaf-sl/sharelatex/admin-sysadmin.pug
Normal file
79
ldap-overleaf-sl/sharelatex/admin-sysadmin.pug
Normal file
|
@ -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.
|
||||
|
Loading…
Add table
Reference in a new issue