Unverified Commit 989a1bb2 authored by sym's avatar sym Committed by GitHub
Browse files

Merge pull request #33 from yzx9/feature/oauth2

Add OAuth2 support
parents 15a3a609 0dafa573
Loading
Loading
Loading
Loading
+117 −18
Original line number Diff line number Diff line
@@ -8,8 +8,8 @@ The inital idea for this implementation was taken from
[worksasintended](https://github.com/worksasintended).

## BREAKING CHANGE
be careful if you try to migrate from 3.3.2! Backup your machines and data.
The migration paths should be:

Be careful if you try to migrate from 3.3.2! Backup your machines and data. The migration paths should be:
- Backup your machines and data
- Run latest 3.5 sharelatex image, make sure that you have enough free space and run the migration scripts:
   - (Details see https://github.com/overleaf/overleaf/wiki/Full-Project-History-Migration)
@@ -21,23 +21,31 @@ The migration paths should be:
    ```
- run this sharelatex image (4.1.1)

Be careful if you try to migrate from 3.3.2! Backup your machines and data. The migration paths should be:

- Backup Your machines and data
- Run latest 3.5 sharelatex image and run the migration scripts
- Run this sharelatex image (4.1.1) and run the migrations scripts

## Limitations

## Limitations:
NEW: This version provides the possibility to use a separate ldap bind user. It does this just to find the proper BIND DN and record for the provided email, so it is possible that users from different groups / OUs can login.
Afterwards it tries to bind to the ldap (using ldapts) with the user DN and credentials of the user which tries to login. No hassle of password hashing for LDAP pwds!

If you upgrade from an older commit:

**Note**:

 - you have to add: uid=%u to your BIND_DN 
 - LDAP_GROUP_FILTER is now named LDAP_USER_FILTER
 - Import of contacts from LDAP is now controlled by LDAP_CONTACT_FILTER


Only valid LDAP users or email users registered by an admin can login. 
This module authenticates against the local DB if `ALLOW_EMAIL_LOGIN` is set to `true` if this fails
it tries to authenticate against the specified LDAP server. 

*Note:*
**Note**:

- LDAP Users can not change their password for the ldap username login. They have to change it at the ldap server.
- LDAP Users can reset their local db password. Then they can decide if they login with either their ldap user and password or with their email and local db password.
- Users can not change their email. The email address is taken from the ldap server (mail) field. (or by invitation through an admin).
@@ -46,8 +54,7 @@ it tries to authenticate against the specified LDAP server.
- Admins can invite non ldap users directly (via email). Additionally (``link sharing`` of projects is possible).

*Important:*
Sharelatex/Overleaf uses the email address to identify users: If you change the email in the LDAP you have to update the corresponding field 
in the mongo db.
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.

```
docker exec -it mongo /bin/bash
@@ -88,13 +95,10 @@ ADMIN_IS_SYSADMIN=false
*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.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
LDAP_BASE: dc=DOMAIN,dc=TLD
@@ -128,17 +132,90 @@ LDAP_CONTACTS: 'false'
If you enable LDAP_CONTACTS, then all users in LDAP_CONTACT_FILTER are loaded from the ldap server into the contacts.
At the moment this happens every time you click on "Share" within a project.
if you want to enable this function set:

```
LDAP_CONTACT_FILTER: (objectClass=person)
LDAP_CONTACTS: 'true'
```

### OAuth2 Configuration

```
# Enable OAuth2
OAUTH2_ENABLED: "true"

# Provider name, optional
OAUTH2_PROVIDER: YOUR_OAUTH2_PROVIDER

# OAuth2 client configuration, 
OAUTH2_CLIENT_ID: YOUR_OAUTH2_CLIENT_ID
OAUTH2_CLIENT_SECRET: YOUR_OAUTH2_CLIENT_SECRET
# Scope should at least include email
OAUTH2_SCOPE: YOUR_OAUTH2_SCOPE

# OAuth2 APIs
# Redirect to OAuth 2.0 url
OAUTH2_AUTHORIZATION_URL: YOUR_OAUTH2_AUTHORIZATION_URL
# Fetch access token api endpoint
OAUTH2_TOKEN_URL: YOUR_OAUTH2_TOKEN_URL
# Content type of token request
# One of ["application/x-www-form-urlencoded", "application/json"]
# Default "application/x-www-form-urlencoded"
OAUTH2_TOKEN_CONTENT_TYPE: "application/x-www-form-urlencoded"
# Fetch user profile api endpoint
OAUTH2_PROFILE_URL: YOUR_OAUTH2_PROFILE_URL

# OAuth2 user attributes
# User identity
OAUTH2_USER_ATTR_EMAIL: email
# User attributes, only used on the first login
OAUTH2_USER_ATTR_UID: id
OAUTH2_USER_ATTR_FIRSTNAME: name
OAUTH2_USER_ATTR_LASTNAME:
OAUTH2_USER_ATTR_IS_ADMIN: site_admin
```

Example configuration for GitHub:

```
OAUTH2_ENABLED: "true"
OAUTH2_PROVIDER: GitHub
OAUTH2_CLIENT_ID: YOUR_CLIENT_ID
OAUTH2_CLIENT_SECRET: YOUR_CLIENT_SECRET
OAUTH2_SCOPE: # the 'public' scope is sufficient for our needs, so we do not request any more 
OAUTH2_AUTHORIZATION_URL: https://github.com/login/oauth/authorize
OAUTH2_TOKEN_URL: https://github.com/login/oauth/access_token
OAUTH2_PROFILE_URL: https://api.github.com/user
OAUTH2_USER_ATTR_EMAIL: email
OAUTH2_USER_ATTR_UID: id
OAUTH2_USER_ATTR_FIRSTNAME: name
OAUTH2_USER_ATTR_LASTNAME:
OAUTH2_USER_ATTR_IS_ADMIN: site_admin
```

Example configuration for Authentik:

```
OAUTH2_ENABLED: "true"
OAUTH2_PROVIDER: GitHub
OAUTH2_CLIENT_ID: "redacted"
OAUTH2_CLIENT_SECRET: "redacted"
OAUTH2_AUTHORIZATION_URL: https://auth.redacted.domain/application/o/authorize/
OAUTH2_TOKEN_URL: https://auth.redacted.domain/application/o/token/
OAUTH2_PROFILE_URL: https://auth.redacted.domain/application/o/userinfo/
OAUTH2_USER_ATTR_EMAIL: email
OAUTH2_USER_ATTR_UID: "email"
OAUTH2_USER_ATTR_FIRSTNAME: name
# To make it work one should create a custom scope first
OAUTH2_USER_ATTR_IS_ADMIN: is_admin
```

### Sharelatex Configuration

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
## Installation, Usage and Initial startup

Install the docker engine: https://docs.docker.com/engine/install/

@@ -150,40 +227,62 @@ Install docker-compose:
pip install docker-compose
```

use the command:

use the command 
```
make
```

to generate the ldap-overleaf-sl docker image.

use the command
use the command:

```
docker network create web
```

to create a network for the docker instances.

### Startup

#### 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.

## Startup 
Start docker containers:

``` 
docker-compose up -d
```

#### Using proxy

There are 2 different ways of starting either using Traefik or using Certbot. Adapt the one you want to use.

### Using Traefik
##### Using Traefik

Then start docker containers (with loadbalancer):

``` 
export NUMINSTANCES=1
docker-compose -f docker-compose.traefik.yml up -d --scale sharelatex=$NUMINSTANCES
```

### Using Certbot 
##### 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 
```

## Debug

1. Set the env variable `LOG_LEVEL` to `debug` (default is info - you can do this in the docker-compose file)
2. Check the logs in ShareLaTeX, particularly at `/var/log/sharelatex/web.log`. You can do this by using the command: `docker exec ldap-overleaf-sl cat /var/log/sharelatex/web.log`.

## Upgrading

*Be aware:* if you upgrade from a previous installation check your docker image version

E.g.: Mongodb: You cannot upgrade directly from mongo 4.2 to 5.0. You must first upgrade from 4.2 to 4.4.
+16 −0
Original line number Diff line number Diff line
@@ -81,6 +81,22 @@ services:
      LDAP_CONTACT_FILTER: "(memberof=cn=GROUPNAME,ou=groups,dc=DOMAIN,dc=TLD)"
      LDAP_CONTACTS: "false"

      ## OAuth2 Settings
      # OAUTH2_ENABLED: "true"
      # OAUTH2_PROVIDER: YOUR_OAUTH2_PROVIDER
      # OAUTH2_CLIENT_ID: YOUR_OAUTH2_CLIENT_ID
      # OAUTH2_CLIENT_SECRET: YOUR_OAUTH2_CLIENT_SECRET
      # OAUTH2_SCOPE: YOUR_OAUTH2_SCOPE
      # 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_PROFILE_URL: YOUR_OAUTH2_PROFILE_URL
      # OAUTH2_USER_ATTR_EMAIL: email
      # OAUTH2_USER_ATTR_UID: id
      # OAUTH2_USER_ATTR_FIRSTNAME: name
      # OAUTH2_USER_ATTR_LASTNAME:
      # OAUTH2_USER_ATTR_IS_ADMIN: site_admin

      # Same property, unfortunately with different names in
      # different locations
      SHARELATEX_REDIS_HOST: redis
+16 −0
Original line number Diff line number Diff line
@@ -162,6 +162,22 @@ services:
      LDAP_CONTACT_FILTER: "(memberof=cn=GROUPNAME,ou=groups,dc=DOMAIN,dc=TLD)"
      LDAP_CONTACTS: "false"

      ## OAuth2 Settings
      # OAUTH2_ENABLED: "true"
      # OAUTH2_PROVIDER: YOUR_OAUTH2_PROVIDER
      # OAUTH2_CLIENT_ID: YOUR_OAUTH2_CLIENT_ID
      # OAUTH2_CLIENT_SECRET: YOUR_OAUTH2_CLIENT_SECRET
      # OAUTH2_SCOPE: YOUR_OAUTH2_SCOPE
      # 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_PROFILE_URL: YOUR_OAUTH2_PROFILE_URL
      # OAUTH2_USER_ATTR_EMAIL: email
      # OAUTH2_USER_ATTR_UID: id
      # OAUTH2_USER_ATTR_FIRSTNAME: name
      # OAUTH2_USER_ATTR_LASTNAME:
      # OAUTH2_USER_ATTR_IS_ADMIN: site_admin

      # Same property, unfortunately with different names in
      # different locations
      SHARELATEX_REDIS_HOST: redis

docker-compose.yml

0 → 100644
+153 −0
Original line number Diff line number Diff line
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
    privileged: false
    ports:
      - 80:80
    links:
      - mongo
      - redis
    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"

      # Uncomment the following line to enable secure cookies if you are using SSL
      # SHARELATEX_SECURE_COOKIE: "true"
      # SHARELATEX_BEHIND_PROXY: "true"

      LDAP_SERVER: ldaps://LDAPSERVER:636
      LDAP_BASE: ou=people,dc=DOMAIN,dc=TLD

      ### There are to ways get users from the ldap server

      ## NO LDAP BIND USER:
      # Tries directly to bind with the login user (as uid)
      # LDAP_BINDDN: uid=%u,ou=someunit,ou=people,dc=DOMAIN,dc=TLD

      ## Or you can use ai global LDAP_BIND_USER
      # LDAP_BIND_USER:
      # LDAP_BIND_PW:

      # Only allow users matching LDAP_USER_FILTER
      LDAP_USER_FILTER: "(memberof=cn=GROUPNAME,ou=groups,dc=DOMAIN,dc=TLD)"

      # If user is in ADMIN_GROUP on user creation (first login) isAdmin is set to true.
      # Admin Users can invite external (non ldap) users. This feature makes only sense
      # when ALLOW_EMAIL_LOGIN is set to 'true'. Additionally admins can send
      # system wide messages.
      LDAP_ADMIN_GROUP_FILTER: "(memberof=cn=ADMINGROUPNAME,ou=groups,dc=DOMAIN,dc=TLD)"
      ALLOW_EMAIL_LOGIN: "true"

      # All users in the LDAP_CONTACT_FILTER are loaded from the ldap server into contacts.
      LDAP_CONTACT_FILTER: "(memberof=cn=GROUPNAME,ou=groups,dc=DOMAIN,dc=TLD)"
      LDAP_CONTACTS: "false"

      ## OAuth2 Settings
      # OAUTH2_ENABLED: "true"
      # OAUTH2_PROVIDER: YOUR_OAUTH2_PROVIDER
      # OAUTH2_CLIENT_ID: YOUR_OAUTH2_CLIENT_ID
      # OAUTH2_CLIENT_SECRET: YOUR_OAUTH2_CLIENT_SECRET
      # OAUTH2_SCOPE: YOUR_OAUTH2_SCOPE
      # 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_PROFILE_URL: YOUR_OAUTH2_PROFILE_URL
      # OAUTH2_USER_ATTR_EMAIL: email
      # OAUTH2_USER_ATTR_UID: id
      # OAUTH2_USER_ATTR_FIRSTNAME: name
      # OAUTH2_USER_ATTR_LASTNAME:
      # OAUTH2_USER_ATTR_IS_ADMIN: site_admin

      # 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:4.4
    container_name: mongo
    expose:
      - 27017
    volumes:
      - ${MYDATA}/mongo_data:/data/db
    healthcheck:
      test: echo 'db.stats().ok' | mongo localhost:27017/test --quiet
      interval: 10s
      timeout: 10s
      retries: 5
    command: "--replSet overleaf"

  # See also: https://github.com/overleaf/overleaf/issues/1120
  mongoinit:
    image: mongo:4.4
    # this container will exit after executing the command
    restart: "no"
    depends_on:
      mongo:
        condition: service_healthy
    entrypoint:
      [
        "mongo",
        "--host",
        "mongo:27017",
        "--eval",
        'rs.initiate({ _id: "overleaf", members: [ { _id: 0, host: "mongo:27017" } ] })',
      ]

  redis:
    restart: always
    image: redis:6.2
    container_name: redis
    expose:
      - 6379
    volumes:
      - ${MYDATA}/redis_data:/data
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 10s
      timeout: 5s
      retries: 5
+21 −19
Original line number Diff line number Diff line
@@ -13,18 +13,6 @@ ARG admin_is_sysadmin
# set workdir (might solve issue #2 - see https://stackoverflow.com/questions/57534295/)
WORKDIR /overleaf/services/web

# overwrite some files
COPY sharelatex/AuthenticationManager.js    /overleaf/services/web/app/src/Features/Authentication/
COPY sharelatex/ContactController.js        /overleaf/services/web/app/src/Features/Contacts/

# Too much changes to do inline (>10 Lines).
COPY sharelatex/settings.pug    /overleaf/services/web/app/views/user/
COPY sharelatex/navbar.pug      /overleaf/services/web/app/views/layout/

# 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

    # install latest npm
RUN npm install -g npm && \
    ## clean cache (might solve issue #2)
@@ -37,12 +25,27 @@ RUN npm install -g npm && \
    apt-get update && \
    apt-get -y install python-pygments && \
    apt-get -y install texlive texlive-lang-german texlive-latex-extra texlive-full texlive-science && \
    ## instead of copying the login.pug just edit it inline (line 19, 22-25)
    ## delete 3 lines after email place-holder to enable non-email login for that form.
    sed -iE '/type=.*email.*/d' /overleaf/services/web/app/views/user/login.pug && \
    apt-get clean && \
    rm -rf /var/lib/apt/lists/*

# overwrite some files
COPY sharelatex/AuthenticationManager.js    /overleaf/services/web/app/src/Features/Authentication/
COPY sharelatex/AuthenticationController.js /overleaf/services/web/app/src/Features/Authentication/
COPY sharelatex/ContactController.js        /overleaf/services/web/app/src/Features/Contacts/
COPY sharelatex/router.js                   /overleaf/services/web/app/src/router.js

# Too much changes to do inline (>10 Lines).
COPY sharelatex/settings.pug    /overleaf/services/web/app/views/user/
COPY sharelatex/login.pug       /overleaf/services/web/app/views/user/
COPY sharelatex/navbar.pug      /overleaf/services/web/app/views/layout/

# 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

    ## comment out this line to prevent sed accidently remove the brackets of the email(username) field
    # sed -iE '/email@example.com/{n;N;N;d}' /overleaf/services/web/app/views/user/login.pug && \
    sed -iE "s/email@example.com/${login_text:-user}/g" /overleaf/services/web/app/views/user/login.pug && \
RUN sed -iE "s/email@example.com/${login_text:-user}/g" /overleaf/services/web/app/views/user/login.pug && \
    ## Collaboration settings display (share project placeholder) | edit line 146
    ## share.pug file was removed in later versions
    # sed -iE "s%placeholder=.*$%placeholder=\"${collab_text}\"%g" /overleaf/services/web/app/views/project/editor/share.pug && \
@@ -57,9 +60,8 @@ RUN npm install -g npm && \
    rm /overleaf/services/web/modules/user-activate/app/views/user/register.pug && \
    ### To remove comments entirly (bug https://github.com/overleaf/overleaf/issues/678)
    rm /overleaf/services/web/app/views/project/editor/review-panel.pug && \
    touch /overleaf/services/web/app/views/project/editor/review-panel.pug && \
    apt-get clean && \
    rm -rf /var/lib/apt/lists/*
    touch /overleaf/services/web/app/views/project/editor/review-panel.pug


### Nginx and Certificates
# enable https via letsencrypt
Loading