diff --git a/.env b/.env index 66183361..d48abfce 100644 --- a/.env +++ b/.env @@ -6,3 +6,6 @@ BE_VERSION=v4 ## Enable v4 ELASTIC feature (disable if required or set in command line). To later disable, either unset or set to an empty value # ELASTIC_ENABLED=true + +## Enable LDAP authentication backend (disable if required or set in command line). To later disable, either unset or set to an empty value +# LDAP_ENABLED=true diff --git a/.github/workflows/compose_test.yaml b/.github/workflows/compose_test.yaml index d559755e..2054d3c4 100644 --- a/.github/workflows/compose_test.yaml +++ b/.github/workflows/compose_test.yaml @@ -54,12 +54,14 @@ jobs: strategy: matrix: BE_VERSION: [v3, v4] - JOBS_AND_ELASTIC_ENABLED: ['', true] + ELASTIC_OR_JOBS_ENABLED: ['', true] + LDAP_ENABLED: ['', true] steps: - uses: actions/checkout@v4 - name: Test compose.yaml run: |- - export JOBS_ENABLED=${{ matrix.JOBS_AND_ELASTIC_ENABLED }} - export ELASTIC_ENABLED=${{ matrix.JOBS_AND_ELASTIC_ENABLED }} + export JOBS_ENABLED=${{ matrix.ELASTIC_OR_JOBS_ENABLED }} + export ELASTIC_ENABLED=${{ matrix.ELASTIC_OR_JOBS_ENABLED }} + export LDAP_ENABLED=${{ matrix.LDAP_ENABLED }} export BE_VERSION=${{ matrix.BE_VERSION }} docker compose --profile '*' up --wait --wait-timeout 300 diff --git a/README.md b/README.md index 285e7d85..39cef84e 100644 --- a/README.md +++ b/README.md @@ -41,6 +41,7 @@ They are used when adding new services or grouping services together (and do not | env | `BE_VERSION` |
  • `v3`: backend/v3
  • `v4`: backend/v4 | `v4` | as set | Sets the be version to use in (2) of [default setup](#default-setup) to v3 | mongodb,frontend | | env | `JOBS_ENABLED` | `true`: rabbitmq,archivemock,jobs feature | `''` | v3 | Creates a rabbitmq message broker which the be posts to and the archivemock listens to. It emulates the data long-term archive/retrieve workflow | | | env | `ELASTIC_ENABLED` | `true`: elastic,elastic feature | `''` | v4 | Creates an elastic search service and sets the be to use it for full-text searches | | +| env | `LDAP_ENABLED` | `true`: ldap auth | `''` | as set | Creates an LDAP service and sets the be to use it as authentication backend | | After optionally setting any configuration option, one can still select the services to run as described [here](README.md#select-the-services). diff --git a/services/backend/README.md b/services/backend/README.md index b9c21663..c4f19e69 100644 --- a/services/backend/README.md +++ b/services/backend/README.md @@ -5,3 +5,12 @@ The SciCat backend HTTP service. ## Dependency on `BE_VERSION` The `BE_VERSION` value controls which version of the backend should be started, either [v3](./services/v3) or [v4](./services/v4) (default). + +## Dependencies + +Here below we show the internal dependencies of the service, which are not already covered [here](../../../../README.md) (if `B` depends on `A`, then we visualize as `A --> B`). The same subdomain to service convention applies. + +```mermaid +graph TD + ldap --> backend +``` diff --git a/services/backend/compose.yaml b/services/backend/compose.yaml index 9265b39b..fa7ae295 100644 --- a/services/backend/compose.yaml +++ b/services/backend/compose.yaml @@ -1,2 +1,4 @@ include: - - ./services/${BE_VERSION:-v4}/compose.yaml + - path: + - ./services/${BE_VERSION:-v4}/compose.yaml + - ./services/ldap/.${LDAP_ENABLED:+./${BE_VERSION:-v4}/}compose${LDAP_ENABLED:+.ldap}.yaml diff --git a/services/backend/services/ldap/.compose.yaml b/services/backend/services/ldap/.compose.yaml new file mode 100644 index 00000000..1d826e5c --- /dev/null +++ b/services/backend/services/ldap/.compose.yaml @@ -0,0 +1 @@ +## empty file used when override configs are not enabled diff --git a/services/backend/services/ldap/README.md b/services/backend/services/ldap/README.md new file mode 100644 index 00000000..2c7fdc24 --- /dev/null +++ b/services/backend/services/ldap/README.md @@ -0,0 +1,20 @@ +# LDAP (OpenLDAP) + +LDAP (Lightweight Directory Access Protocol) is a protocol used to access and manage directory information such as user credentials. +SciCat can use LDAP as third-party authentication provider. + +## Configuration options + +The OpenLDAP configuration is set by the [.env file](./config/.env). + +For an extensive list of available options see [here](https://hub.docker.com/r/bitnami/openldap). + +You can add other users by editing the [ldif file](./config/ldifs/02-users.ldif). +:warning: User creation is only done once, when the container is created. + +## Default configuration +The default configuration [.env file](./config/.env) creates the `dc=facility` domain with the following user: + +| Username | Password | +| --------- | -------- | +| ldap-user | password | diff --git a/services/backend/services/ldap/compose.yaml b/services/backend/services/ldap/compose.yaml new file mode 100644 index 00000000..2e617840 --- /dev/null +++ b/services/backend/services/ldap/compose.yaml @@ -0,0 +1,13 @@ +services: + ldap: + image: bitnami/openldap:2.6 + volumes: + - ./config/ldifs:/ldifs:ro + env_file: + - ./config/.env + healthcheck: + test: ldapwhoami -H ldap://ldap:389 -D 'cn=admin,dc=facility' -w 'admin' + start_period: 5s + interval: 10s + timeout: 10s + retries: 5 diff --git a/services/backend/services/ldap/config/.env b/services/backend/services/ldap/config/.env new file mode 100644 index 00000000..85b5fce1 --- /dev/null +++ b/services/backend/services/ldap/config/.env @@ -0,0 +1,4 @@ +LDAP_ADMIN_USERNAME=admin +LDAP_ADMIN_PASSWORD=admin +LDAP_PORT_NUMBER=389 +LDAP_ROOT=dc=facility diff --git a/services/backend/services/ldap/config/ldifs/01-bootstrap.ldif b/services/backend/services/ldap/config/ldifs/01-bootstrap.ldif new file mode 100644 index 00000000..c728ee63 --- /dev/null +++ b/services/backend/services/ldap/config/ldifs/01-bootstrap.ldif @@ -0,0 +1,11 @@ +dn: dc=facility +objectClass: top +objectClass: dcObject +objectClass: organization +o: My Organization +dc: facility + +dn: ou=users,dc=facility +objectClass: top +objectClass: organizationalUnit +ou: users diff --git a/services/backend/services/ldap/config/ldifs/02-users.ldif b/services/backend/services/ldap/config/ldifs/02-users.ldif new file mode 100644 index 00000000..050ec8f1 --- /dev/null +++ b/services/backend/services/ldap/config/ldifs/02-users.ldif @@ -0,0 +1,12 @@ +dn: uid=ldap-user,ou=users,dc=facility +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: inetOrgPerson +uid: ldap-user +cn: LDAP user +displayName: ldap-user +sn: LDAP +givenName: User +mail: ldap-user@facility.com +userPassword: password diff --git a/services/backend/services/v3/README.md b/services/backend/services/v3/README.md index 591fb88f..8fd6a101 100644 --- a/services/backend/services/v3/README.md +++ b/services/backend/services/v3/README.md @@ -69,12 +69,14 @@ In the default configuration folder [config](./config), the backend is set to us Additionally, by setting the env variable [ENABLE_JOBS](../../.env#L5), the [archive mock](./services/archivemock/) and [rabbitmq](./services/rabbitmq/) services are started and the backend is configured to connect to them. +If `LDAP_ENABLED` is toggled, you can use LDAP to log in with a [LDAP user](../ldap/README.md#default-configuration). + ## Dependencies -Here below we show the internal dependencies of the service, which are not already covered [here](../../../../README.md) (if `B` depends on `A`, then we visualize it as `A --> B`). The same subdomain to service convention applies. +Here below we show the internal dependencies of the service, which are not already covered [here](../../../../README.md) and [here](../../README.md) (if `B` depends on `A`, then we visualize as `A --> B`). The same subdomain to service convention applies. ```mermaid -graph TD +graph TD rabbitmq --> archivemock rabbitmq --> backend backend --> archivemock diff --git a/services/backend/services/v3/compose.ldap.yaml b/services/backend/services/v3/compose.ldap.yaml new file mode 100644 index 00000000..0e866aac --- /dev/null +++ b/services/backend/services/v3/compose.ldap.yaml @@ -0,0 +1,10 @@ +include: + - ../ldap/compose.yaml + +services: + backend: + depends_on: + ldap: + condition: service_healthy + volumes: + - ./config/providers.ldap.json:/config/providers.ldap.json diff --git a/services/backend/services/v3/config/providers.ldap.json b/services/backend/services/v3/config/providers.ldap.json new file mode 100644 index 00000000..a0a1a4c6 --- /dev/null +++ b/services/backend/services/v3/config/providers.ldap.json @@ -0,0 +1,24 @@ +{ + "ldap": { + "provider": "ldap", + "authScheme": "ldap", + "module": "passport-ldapauth", + "authPath": "/auth/msad", + "successRedirect": "/auth/account", + "failureRedirect": "/msad", + "session": true, + "json": true, + "failureFlash": true, + "profileAttributesFromLDAP": { + "displayName": "displayName", + "email": "mail" + }, + "server": { + "url": "ldap://ldap:389", + "bindDn": "cn=admin,dc=facility", + "bindCredentials": "admin", + "searchBase": "ou=users,dc=facility", + "searchFilter": "(uid={{username}})" + } + } +} diff --git a/services/backend/services/v4/README.md b/services/backend/services/v4/README.md index a80ae90a..fdc8a33a 100644 --- a/services/backend/services/v4/README.md +++ b/services/backend/services/v4/README.md @@ -23,11 +23,13 @@ In the default configuration folder [config](./config), the backend is set to us ## Enable additional features -Additionally, by setting the env variable `ELASTIC_ENABLED`, the [elastic search](./services/elastic/) service is started and the backend is configured to connect to them. +Additionally, by setting the env variable `ELASTIC_ENABLED`, the [elastic search](./services/elastic/) service is started and the backend is configured to connect to them. + +If `LDAP_ENABLED` is toggled, you can use LDAP to log in with a [LDAP user](../ldap/README.md#default-configuration). ## Dependencies -Here below we show the internal dependencies of the service, which are not already covered [here](../../../../README.md) (if `B` depends on `A`, then we visualize as `A --> B`). The same subdomain to service convention applies. +Here below we show the internal dependencies of the service, which are not already covered [here](../../../../README.md) and [here](../../README.md) (if `B` depends on `A`, then we visualize as `A --> B`). The same subdomain to service convention applies. ```mermaid graph TD diff --git a/services/backend/services/v4/compose.ldap.yaml b/services/backend/services/v4/compose.ldap.yaml new file mode 100644 index 00000000..9b8fd1c3 --- /dev/null +++ b/services/backend/services/v4/compose.ldap.yaml @@ -0,0 +1,10 @@ +include: + - ../ldap/compose.yaml + +services: + backend: + depends_on: + ldap: + condition: service_healthy + env_file: + - ./config/.ldap.env diff --git a/services/backend/services/v4/config/.ldap.env b/services/backend/services/v4/config/.ldap.env new file mode 100644 index 00000000..2b7755a0 --- /dev/null +++ b/services/backend/services/v4/config/.ldap.env @@ -0,0 +1,3 @@ +LDAP_URL=ldap://ldap:389 +LDAP_SEARCH_BASE=ou=users,dc=facility +LDAP_SEARCH_FILTER=(uid={{username}}) diff --git a/services/frontend/compose.yaml b/services/frontend/compose.yaml index ea5f7ae2..d78bbabb 100644 --- a/services/frontend/compose.yaml +++ b/services/frontend/compose.yaml @@ -13,5 +13,6 @@ services: - /config/init.sh && nginx -g "daemon off;" environment: BE_VERSION: ${BE_VERSION:-v4} + LDAP_ENABLED: ${LDAP_ENABLED:-} labels: - traefik.http.routers.frontend.rule=Host(`localhost`) diff --git a/services/frontend/config/config.base.json b/services/frontend/config/config.base.json index 143d9268..de384ae6 100644 --- a/services/frontend/config/config.base.json +++ b/services/frontend/config/config.base.json @@ -7,7 +7,7 @@ "editMetadataEnabled":true, "editPublishedData":true, "editSampleEnabled":true, - "externalAuthEndpoint":"/auth/msad", + "externalAuthEndpoint":"/api/v3/auth/ldap", "facility":"SAMPLE-SITE", "fileColorEnabled":true, "fileDownloadEnabled":true, diff --git a/services/frontend/config/config.ldap.json b/services/frontend/config/config.ldap.json new file mode 100644 index 00000000..d25fb2e7 --- /dev/null +++ b/services/frontend/config/config.ldap.json @@ -0,0 +1,4 @@ +{ + "loginLdapEnabled": true, + "loginLdapLabel": "Connect with LDAP" +} diff --git a/services/frontend/config/config.v3.json b/services/frontend/config/config.v3.json index b01c5d09..9322b40e 100644 --- a/services/frontend/config/config.v3.json +++ b/services/frontend/config/config.v3.json @@ -1,3 +1,4 @@ { - "accessTokenPrefix": "" + "accessTokenPrefix": "", + "externalAuthEndpoint":"/auth/msad" } diff --git a/services/frontend/config/init.sh b/services/frontend/config/init.sh index 4c986a8f..14c8a4d1 100755 --- a/services/frontend/config/init.sh +++ b/services/frontend/config/init.sh @@ -10,6 +10,7 @@ exclude_config () { } [ "$BE_VERSION" = "v4" ] && exclude_config "v3.json" +[ -z "$LDAP_ENABLED" ] && exclude_config "ldap.json" # shellcheck disable=SC2086 jq -s 'reduce .[] as $item ({}; . * $item)' $FILES > /usr/share/nginx/html/assets/config.json