mirror of
https://github.com/nitnelave/lldap.git
synced 2023-04-12 14:25:13 +00:00
Compare commits
34 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
dcca768b6c | ||
![]() |
ea69b4bead | ||
![]() |
7b4188a376 | ||
![]() |
252132430c | ||
![]() |
7f9bc95c5c | ||
![]() |
69fca82a86 | ||
![]() |
9a30cac7b0 | ||
![]() |
558bb37354 | ||
![]() |
5b74852193 | ||
![]() |
d18cf1ac37 | ||
![]() |
96f55ff28e | ||
![]() |
825f37d360 | ||
![]() |
8eb27c5267 | ||
![]() |
18d9dd6ff9 | ||
![]() |
308521c632 | ||
![]() |
86b2b5148d | ||
![]() |
b9e0e4a6dc | ||
![]() |
1b8849ead1 | ||
![]() |
1fe635384f | ||
![]() |
df16d66753 | ||
![]() |
65e2c24928 | ||
![]() |
c4b8621e2a | ||
![]() |
88a9f8a97b | ||
![]() |
fc91d59b99 | ||
![]() |
aad4711056 | ||
![]() |
c7c6d95334 | ||
![]() |
84b4c66309 | ||
![]() |
923d77072b | ||
![]() |
758aa7f7f7 | ||
![]() |
866a74fa29 | ||
![]() |
36a51070b3 | ||
![]() |
585b65e11d | ||
![]() |
2c8fe2a481 | ||
![]() |
1b67bad270 |
@ -2,6 +2,7 @@
|
||||
.git/*
|
||||
.github/*
|
||||
.gitignore
|
||||
.gitattributes
|
||||
|
||||
# Don't track cargo generated files
|
||||
target/*
|
||||
@ -17,6 +18,7 @@ Dockerfile
|
||||
*.md
|
||||
LICENSE
|
||||
CHANGELOG.md
|
||||
README.md
|
||||
docs/*
|
||||
example_configs/*
|
||||
|
||||
|
10
.gitattributes
vendored
Normal file
10
.gitattributes
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
example-configs/** linguist-documentation
|
||||
docs/** linguist-documentation
|
||||
*.md linguist-documentation
|
||||
lldap_config.docker_template.toml linguist-documentation
|
||||
|
||||
schema.graphql linguist-generated
|
||||
|
||||
.github/** -linguist-detectable
|
||||
.devcontainer/** -linguist-detectable
|
||||
.config/** -linguist-detectable
|
1
.github/CODEOWNERS
vendored
Normal file
1
.github/CODEOWNERS
vendored
Normal file
@ -0,0 +1 @@
|
||||
* @nitnelave
|
2
.github/codecov.yml
vendored
2
.github/codecov.yml
vendored
@ -10,3 +10,5 @@ ignore:
|
||||
- "docs"
|
||||
- "example_configs"
|
||||
- "migration-tool"
|
||||
- "scripts"
|
||||
- "set-password"
|
||||
|
383
.github/workflows/docker-build-static.yml
vendored
383
.github/workflows/docker-build-static.yml
vendored
@ -4,12 +4,18 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- 'main'
|
||||
paths-ignore:
|
||||
- 'docs/**'
|
||||
- 'example_configs/**'
|
||||
release:
|
||||
types:
|
||||
- 'published'
|
||||
pull_request:
|
||||
branches:
|
||||
- 'main'
|
||||
paths-ignore:
|
||||
- 'docs/**'
|
||||
- 'example_configs/**'
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
msg:
|
||||
@ -60,8 +66,25 @@ env:
|
||||
# cache based on Cargo.lock per cargo target
|
||||
|
||||
jobs:
|
||||
pre_job:
|
||||
continue-on-error: true
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
should_skip: ${{ steps.skip_check.outputs.should_skip }}
|
||||
steps:
|
||||
- id: skip_check
|
||||
uses: fkirc/skip-duplicate-actions@master
|
||||
with:
|
||||
concurrent_skipping: 'outdated_runs'
|
||||
skip_after_successful_duplicate: ${{ github.ref != 'refs/heads/main' }}
|
||||
paths_ignore: '["**/*.md", "**/docs/**", "example_configs/**", "*.sh", ".gitignore", "lldap_config.docker_template.toml"]'
|
||||
do_not_skip: '["workflow_dispatch", "schedule"]'
|
||||
cancel_others: true
|
||||
|
||||
build-ui:
|
||||
runs-on: ubuntu-latest
|
||||
needs: pre_job
|
||||
if: ${{ needs.pre_job.outputs.should_skip != 'true' || github.event_name == 'release' }}
|
||||
container:
|
||||
image: nitnelave/rust-dev:latest
|
||||
steps:
|
||||
@ -99,6 +122,8 @@ jobs:
|
||||
|
||||
build-bin:
|
||||
runs-on: ubuntu-latest
|
||||
needs: pre_job
|
||||
if: ${{ needs.pre_job.outputs.should_skip != 'true' || github.event_name == 'release' }}
|
||||
strategy:
|
||||
matrix:
|
||||
target: [armv7-unknown-linux-gnueabihf, aarch64-unknown-linux-musl, x86_64-unknown-linux-musl]
|
||||
@ -145,9 +170,9 @@ jobs:
|
||||
name: ${{ matrix.target }}-lldap_set_password-bin
|
||||
path: target/${{ matrix.target }}/release/lldap_set_password
|
||||
|
||||
lldap-database-integration-test:
|
||||
lldap-database-init-test:
|
||||
needs: [build-ui,build-bin]
|
||||
name: LLDAP test
|
||||
name: LLDAP database init test
|
||||
runs-on: ubuntu-latest
|
||||
services:
|
||||
mariadb:
|
||||
@ -155,10 +180,13 @@ jobs:
|
||||
ports:
|
||||
- 3306:3306
|
||||
env:
|
||||
MYSQL_USER: lldapuser
|
||||
MYSQL_PASSWORD: lldappass
|
||||
MYSQL_DATABASE: lldap
|
||||
MYSQL_ROOT_PASSWORD: rootpass
|
||||
MARIADB_USER: lldapuser
|
||||
MARIADB_PASSWORD: lldappass
|
||||
MARIADB_DATABASE: lldap
|
||||
MARIADB_ALLOW_EMPTY_ROOT_PASSWORD: 1
|
||||
options: >-
|
||||
--name mariadb
|
||||
--health-cmd="mysqladmin ping" --health-interval=5s --health-timeout=2s --health-retries=3
|
||||
|
||||
postgresql:
|
||||
image: postgres:latest
|
||||
@ -168,6 +196,12 @@ jobs:
|
||||
POSTGRES_USER: lldapuser
|
||||
POSTGRES_PASSWORD: lldappass
|
||||
POSTGRES_DB: lldap
|
||||
options: >-
|
||||
--health-cmd pg_isready
|
||||
--health-interval 10s
|
||||
--health-timeout 5s
|
||||
--health-retries 5
|
||||
--name postgresql
|
||||
|
||||
steps:
|
||||
- name: Download artifacts
|
||||
@ -175,8 +209,7 @@ jobs:
|
||||
with:
|
||||
name: x86_64-unknown-linux-musl-lldap-bin
|
||||
path: bin/
|
||||
- name: Where is the bin?
|
||||
run: ls -alR bin
|
||||
|
||||
- name: Set executables to LLDAP
|
||||
run: chmod +x bin/lldap
|
||||
|
||||
@ -212,11 +245,233 @@ jobs:
|
||||
LLDAP_ldap_port: 3892
|
||||
LLDAP_http_port: 17172
|
||||
|
||||
- name: Check DB container logs
|
||||
run: |
|
||||
docker logs -n 20 mariadb
|
||||
docker logs -n 20 postgresql
|
||||
|
||||
lldap-database-migration-test:
|
||||
needs: [build-ui,build-bin]
|
||||
name: LLDAP database migration test
|
||||
runs-on: ubuntu-latest
|
||||
services:
|
||||
postgresql:
|
||||
image: postgres:latest
|
||||
ports:
|
||||
- 5432:5432
|
||||
env:
|
||||
POSTGRES_USER: lldapuser
|
||||
POSTGRES_PASSWORD: lldappass
|
||||
POSTGRES_DB: lldap
|
||||
options: >-
|
||||
--health-cmd pg_isready
|
||||
--health-interval 10s
|
||||
--health-timeout 5s
|
||||
--health-retries 5
|
||||
--name postgresql
|
||||
|
||||
mariadb:
|
||||
image: mariadb:latest
|
||||
ports:
|
||||
- 3306:3306
|
||||
env:
|
||||
MARIADB_USER: lldapuser
|
||||
MARIADB_PASSWORD: lldappass
|
||||
MARIADB_DATABASE: lldap
|
||||
MARIADB_ALLOW_EMPTY_ROOT_PASSWORD: 1
|
||||
options: >-
|
||||
--name mariadb
|
||||
--health-cmd="mysqladmin ping" --health-interval=5s --health-timeout=2s --health-retries=3
|
||||
|
||||
|
||||
mysql:
|
||||
image: mysql:latest
|
||||
ports:
|
||||
- 3307:3306
|
||||
env:
|
||||
MYSQL_USER: lldapuser
|
||||
MYSQL_PASSWORD: lldappass
|
||||
MYSQL_DATABASE: lldap
|
||||
MYSQL_ALLOW_EMPTY_PASSWORD: 1
|
||||
options: >-
|
||||
--name mysql
|
||||
--health-cmd="mysqladmin ping" --health-interval=5s --health-timeout=2s --health-retries=3
|
||||
|
||||
|
||||
steps:
|
||||
- name: Download LLDAP artifacts
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: x86_64-unknown-linux-musl-lldap-bin
|
||||
path: bin/
|
||||
|
||||
- name: Download LLDAP set password
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: x86_64-unknown-linux-musl-lldap_set_password-bin
|
||||
path: bin/
|
||||
|
||||
- name: Set executables to LLDAP and LLDAP set password
|
||||
run: |
|
||||
chmod +x bin/lldap
|
||||
chmod +x bin/lldap_set_password
|
||||
|
||||
- name: Install sqlite3 and ldap-utils for exporting and searching dummy user
|
||||
run: sudo apt update && sudo apt install -y sqlite3 ldap-utils
|
||||
|
||||
- name: Run lldap with sqlite DB and healthcheck
|
||||
run: |
|
||||
bin/lldap run &
|
||||
sleep 10s
|
||||
bin/lldap healthcheck
|
||||
env:
|
||||
LLDAP_database_url: sqlite://users.db?mode=rwc
|
||||
LLDAP_ldap_port: 3890
|
||||
LLDAP_http_port: 17170
|
||||
LLDAP_LDAP_USER_PASS: ldappass
|
||||
LLDAP_JWT_SECRET: somejwtsecret
|
||||
|
||||
- name: Create dummy user
|
||||
run: |
|
||||
TOKEN=$(curl -X POST -H "Content-Type: application/json" -d '{"username": "admin", "password": "ldappass"}' http://localhost:17170/auth/simple/login | jq -r .token)
|
||||
echo "$TOKEN"
|
||||
curl 'http://localhost:17170/api/graphql' -H 'Content-Type: application/json' -H "Authorization: Bearer ${TOKEN//[$'\t\r\n ']}" --data-binary '{"query":"mutation{\n createUser(user:\n {\n id: \"dummyuser\",\n email: \"dummyuser@example.com\"\n }\n )\n {\n id\n email\n }\n}\n\n\n"}' --compressed
|
||||
bin/lldap_set_password --base-url http://localhost:17170 --admin-username admin --admin-password ldappass --token $TOKEN --username dummyuser --password dummypassword
|
||||
|
||||
- name: Test Dummy User, This will be checked again after importing
|
||||
run: |
|
||||
ldapsearch -H ldap://localhost:3890 -LLL -D "uid=dummyuser,ou=people,dc=example,dc=com" -w 'dummypassword' -s "One" -b "ou=people,dc=example,dc=com"
|
||||
|
||||
- name: Stop LLDAP sqlite
|
||||
run: pkill lldap
|
||||
|
||||
- name: Export and Converting to Postgress
|
||||
run: |
|
||||
curl -L https://raw.githubusercontent.com/lldap/lldap/main/scripts/sqlite_dump_commands.sh -o helper.sh
|
||||
chmod +x ./helper.sh
|
||||
./helper.sh | sqlite3 ./users.db > ./dump.sql
|
||||
sed -i -r -e "s/X'([[:xdigit:]]+'[^'])/'\\\x\\1/g" -e '1s/^/BEGIN;\n/' -e '$aCOMMIT;' ./dump.sql
|
||||
|
||||
- name: Create schema on postgres
|
||||
run: |
|
||||
bin/lldap create_schema -d postgres://lldapuser:lldappass@localhost:5432/lldap
|
||||
|
||||
- name: Copy converted db to postgress and import
|
||||
run: |
|
||||
docker ps -a
|
||||
docker cp ./dump.sql postgresql:/tmp/dump.sql
|
||||
docker exec postgresql bash -c "psql -U lldapuser -d lldap < /tmp/dump.sql"
|
||||
rm ./dump.sql
|
||||
|
||||
- name: Export and Converting to mariadb
|
||||
run: |
|
||||
curl -L https://raw.githubusercontent.com/lldap/lldap/main/scripts/sqlite_dump_commands.sh -o helper.sh
|
||||
chmod +x ./helper.sh
|
||||
./helper.sh | sqlite3 ./users.db > ./dump.sql
|
||||
cp ./dump.sql ./dump-no-sed.sql
|
||||
sed -i -r -e "s/([^']'[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]{9})\+00:00'([^'])/\1'\2/g" \-e 's/^INSERT INTO "?([a-zA-Z0-9_]+)"?/INSERT INTO `\1`/' -e '1s/^/START TRANSACTION;\n/' -e '$aCOMMIT;' ./dump.sql
|
||||
sed -i '1 i\SET FOREIGN_KEY_CHECKS = 0;' ./dump.sql
|
||||
|
||||
- name: Create schema on mariadb
|
||||
run: bin/lldap create_schema -d mysql://lldapuser:lldappass@localhost:3306/lldap
|
||||
|
||||
- name: Copy converted db to mariadb and import
|
||||
run: |
|
||||
docker ps -a
|
||||
docker cp ./dump.sql mariadb:/tmp/dump.sql
|
||||
docker exec mariadb bash -c "mariadb -ulldapuser -plldappass -f lldap < /tmp/dump.sql"
|
||||
rm ./dump.sql
|
||||
|
||||
- name: Export and Converting to mysql
|
||||
run: |
|
||||
curl -L https://raw.githubusercontent.com/lldap/lldap/main/scripts/sqlite_dump_commands.sh -o helper.sh
|
||||
chmod +x ./helper.sh
|
||||
./helper.sh | sqlite3 ./users.db > ./dump.sql
|
||||
sed -i -r -e 's/^INSERT INTO "?([a-zA-Z0-9_]+)"?/INSERT INTO `\1`/' -e '1s/^/START TRANSACTION;\n/' -e '$aCOMMIT;' ./dump.sql
|
||||
sed -i '1 i\SET FOREIGN_KEY_CHECKS = 0;' ./dump.sql
|
||||
|
||||
- name: Create schema on mysql
|
||||
run: bin/lldap create_schema -d mysql://lldapuser:lldappass@localhost:3307/lldap
|
||||
|
||||
- name: Copy converted db to mysql and import
|
||||
run: |
|
||||
docker ps -a
|
||||
docker cp ./dump.sql mysql:/tmp/dump.sql
|
||||
docker exec mysql bash -c "mysql -ulldapuser -plldappass -f lldap < /tmp/dump.sql"
|
||||
rm ./dump.sql
|
||||
|
||||
- name: Run lldap with postgres DB and healthcheck again
|
||||
run: |
|
||||
bin/lldap run &
|
||||
sleep 10s
|
||||
bin/lldap healthcheck
|
||||
env:
|
||||
LLDAP_database_url: postgres://lldapuser:lldappass@localhost:5432/lldap
|
||||
LLDAP_ldap_port: 3891
|
||||
LLDAP_http_port: 17171
|
||||
LLDAP_LDAP_USER_PASS: ldappass
|
||||
LLDAP_JWT_SECRET: somejwtsecret
|
||||
|
||||
- name: Run lldap with mariaDB and healthcheck again
|
||||
run: |
|
||||
bin/lldap run &
|
||||
sleep 10s
|
||||
bin/lldap healthcheck
|
||||
env:
|
||||
LLDAP_database_url: mysql://lldapuser:lldappass@localhost:3306/lldap
|
||||
LLDAP_ldap_port: 3892
|
||||
LLDAP_http_port: 17172
|
||||
LLDAP_JWT_SECRET: somejwtsecret
|
||||
|
||||
- name: Run lldap with mysql and healthcheck again
|
||||
run: |
|
||||
bin/lldap run &
|
||||
sleep 10s
|
||||
bin/lldap healthcheck
|
||||
env:
|
||||
LLDAP_database_url: mysql://lldapuser:lldappass@localhost:3307/lldap
|
||||
LLDAP_ldap_port: 3893
|
||||
LLDAP_http_port: 17173
|
||||
LLDAP_JWT_SECRET: somejwtsecret
|
||||
|
||||
- name: Test Dummy User
|
||||
run: |
|
||||
ldapsearch -H ldap://localhost:3891 -LLL -D "uid=dummyuser,ou=people,dc=example,dc=com" -w 'dummypassword' -s "One" -b "ou=people,dc=example,dc=com"
|
||||
ldapsearch -H ldap://localhost:3892 -LLL -D "uid=dummyuser,ou=people,dc=example,dc=com" -w 'dummypassword' -s "One" -b "ou=people,dc=example,dc=com"
|
||||
ldapsearch -H ldap://localhost:3893 -LLL -D "uid=dummyuser,ou=people,dc=example,dc=com" -w 'dummypassword' -s "One" -b "ou=people,dc=example,dc=com"
|
||||
|
||||
build-docker-image:
|
||||
needs: [build-ui, build-bin]
|
||||
name: Build Docker image
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
container: ["debian","alpine"]
|
||||
include:
|
||||
- container: alpine
|
||||
platforms: linux/amd64,linux/arm64
|
||||
tags: |
|
||||
type=ref,event=pr
|
||||
type=semver,pattern=v{{version}}
|
||||
type=semver,pattern=v{{major}}
|
||||
type=semver,pattern=v{{major}}.{{minor}}
|
||||
type=semver,pattern=v{{version}},suffix=
|
||||
type=semver,pattern=v{{major}},suffix=
|
||||
type=semver,pattern=v{{major}}.{{minor}},suffix=
|
||||
type=raw,value=latest,enable={{ is_default_branch }}
|
||||
type=raw,value=stable,enable=${{ startsWith(github.ref, 'refs/tags/v') }}
|
||||
type=raw,value=stable,enable=${{ startsWith(github.ref, 'refs/tags/v') }},suffix=
|
||||
type=raw,value=latest,enable={{ is_default_branch }},suffix=
|
||||
- container: debian
|
||||
platforms: linux/amd64,linux/arm64,linux/arm/v7
|
||||
tags: |
|
||||
type=ref,event=pr
|
||||
type=semver,pattern=v{{version}}
|
||||
type=semver,pattern=v{{major}}
|
||||
type=semver,pattern=v{{major}}.{{minor}}
|
||||
type=raw,value=latest,enable={{ is_default_branch }}
|
||||
type=raw,value=stable,enable=${{ startsWith(github.ref, 'refs/tags/v') }}
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
@ -238,86 +493,66 @@ jobs:
|
||||
uses: docker/setup-qemu-action@v2
|
||||
- uses: docker/setup-buildx-action@v2
|
||||
|
||||
- name: Docker meta
|
||||
- name: Docker ${{ matrix.container }} meta
|
||||
id: meta
|
||||
uses: docker/metadata-action@v4
|
||||
with:
|
||||
# list of Docker images to use as base name for tags
|
||||
images: |
|
||||
nitnelave/lldap
|
||||
# generate Docker tags based on the following events/attributes
|
||||
tags: |
|
||||
type=ref,event=branch
|
||||
type=ref,event=pr
|
||||
type=semver,pattern={{version}}
|
||||
type=semver,pattern={{major}}.{{minor}}
|
||||
type=semver,pattern={{major}}
|
||||
type=sha
|
||||
lldap/lldap
|
||||
ghcr.io/lldap/lldap
|
||||
# Wanted Docker tags
|
||||
# vX-alpine
|
||||
# vX.Y-alpine
|
||||
# vX.Y.Z-alpine
|
||||
# latest
|
||||
# latest-alpine
|
||||
# stable
|
||||
# stable-alpine
|
||||
#################
|
||||
# vX-debian
|
||||
# vX.Y-debian
|
||||
# vX.Y.Z-debian
|
||||
# latest-debian
|
||||
# stable-debian
|
||||
#################
|
||||
# Check matrix for tag list definition
|
||||
flavor: |
|
||||
latest=false
|
||||
suffix=-${{ matrix.container }}
|
||||
tags: ${{ matrix.tags }}
|
||||
|
||||
- name: parse tag
|
||||
uses: gacts/github-slug@v1
|
||||
id: slug
|
||||
|
||||
- name: Login to Docker Hub
|
||||
# Docker login to nitnelave/lldap and lldap/lldap
|
||||
- name: Login to Nitnelave/LLDAP Docker Hub
|
||||
if: github.event_name != 'pull_request'
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: Login to GitHub Container Registry
|
||||
if: github.event_name != 'pull_request'
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: nitnelave
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
|
||||
########################################
|
||||
#### docker image :latest tag build ####
|
||||
#### docker image build ####
|
||||
########################################
|
||||
- name: Build and push latest alpine
|
||||
if: github.event_name != 'release'
|
||||
- name: Build ${{ matrix.container }} Docker Image
|
||||
uses: docker/build-push-action@v4
|
||||
with:
|
||||
context: .
|
||||
push: ${{ github.event_name != 'pull_request' }}
|
||||
platforms: linux/amd64,linux/arm64
|
||||
file: ./.github/workflows/Dockerfile.ci.alpine
|
||||
tags: nitnelave/lldap:latest, nitnelave/lldap:latest-alpine
|
||||
cache-from: type=gha,mode=max
|
||||
cache-to: type=gha,mode=max
|
||||
|
||||
- name: Build and push latest debian
|
||||
if: github.event_name != 'release'
|
||||
uses: docker/build-push-action@v4
|
||||
with:
|
||||
context: .
|
||||
push: ${{ github.event_name != 'pull_request' }}
|
||||
platforms: linux/amd64,linux/arm64,linux/arm/v7
|
||||
file: ./.github/workflows/Dockerfile.ci.debian
|
||||
tags: nitnelave/lldap:latest-debian
|
||||
cache-from: type=gha,mode=max
|
||||
cache-to: type=gha,mode=max
|
||||
|
||||
########################################
|
||||
#### docker image :semver tag build ####
|
||||
########################################
|
||||
- name: Build and push release alpine
|
||||
if: github.event_name == 'release'
|
||||
uses: docker/build-push-action@v4
|
||||
with:
|
||||
context: .
|
||||
platforms: linux/amd64,linux/arm64
|
||||
push: true
|
||||
# Tag as latest, stable, semver, major, major.minor and major.minor.patch.
|
||||
file: ./.github/workflows/Dockerfile.ci.alpine
|
||||
tags: nitnelave/lldap:stable, nitnelave/lldap:stable-alpine, nitnelave/lldap:v${{ steps.slug.outputs.version-semantic }}, nitnelave/lldap:v${{ steps.slug.outputs.version-major }}, nitnelave/lldap:v${{ steps.slug.outputs.version-major }}.${{ steps.slug.outputs.version-minor }}, nitnelave/lldap:v${{ steps.slug.outputs.version-major }}.${{ steps.slug.outputs.version-minor }}.${{ steps.slug.outputs.version-patch }}, nitnelave/lldap:v${{ steps.slug.outputs.version-semantic }}-alpine, nitnelave/lldap:v${{ steps.slug.outputs.version-major }}-alpine, nitnelave/lldap:v${{ steps.slug.outputs.version-major }}-alpine.${{ steps.slug.outputs.version-minor }}-alpine, nitnelave/lldap:v${{ steps.slug.outputs.version-major }}.${{ steps.slug.outputs.version-minor }}.${{ steps.slug.outputs.version-patch }}-alpine
|
||||
cache-from: type=gha,mode=max
|
||||
cache-to: type=gha,mode=max
|
||||
|
||||
- name: Build and push release debian
|
||||
if: github.event_name == 'release'
|
||||
uses: docker/build-push-action@v4
|
||||
with:
|
||||
context: .
|
||||
platforms: linux/amd64,linux/arm64,linux/arm/v7
|
||||
push: true
|
||||
# Tag as latest, stable, semver, major, major.minor and major.minor.patch.
|
||||
file: ./.github/workflows/Dockerfile.ci.debian
|
||||
tags: nitnelave/lldap:stable-debian, nitnelave/lldap:v${{ steps.slug.outputs.version-semantic }}-debian, nitnelave/lldap:v${{ steps.slug.outputs.version-major }}-debian, nitnelave/lldap:v${{ steps.slug.outputs.version-major }}.${{ steps.slug.outputs.version-minor }}-debian, nitnelave/lldap:v${{ steps.slug.outputs.version-major }}.${{ steps.slug.outputs.version-minor }}.${{ steps.slug.outputs.version-patch }}-debian
|
||||
platforms: ${{ matrix.platforms }}
|
||||
file: ./.github/workflows/Dockerfile.ci.${{ matrix.container }}
|
||||
tags: |
|
||||
${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
cache-from: type=gha,mode=max
|
||||
cache-to: type=gha,mode=max
|
||||
|
||||
@ -329,6 +564,14 @@ jobs:
|
||||
password: ${{ secrets.DOCKERHUB_PASSWORD }}
|
||||
repository: nitnelave/lldap
|
||||
|
||||
- name: Update lldap repo description
|
||||
if: github.event_name != 'pull_request'
|
||||
uses: peter-evans/dockerhub-description@v3
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_PASSWORD }}
|
||||
repository: lldap/lldap
|
||||
|
||||
###############################################################
|
||||
### Download artifacts, clean up ui, upload to release page ###
|
||||
###############################################################
|
||||
@ -337,6 +580,8 @@ jobs:
|
||||
name: Create release artifacts
|
||||
if: github.event_name == 'release'
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
steps:
|
||||
- name: Download all artifacts
|
||||
uses: actions/download-artifact@v3
|
||||
|
10
.github/workflows/rust.yml
vendored
10
.github/workflows/rust.yml
vendored
@ -13,7 +13,6 @@ jobs:
|
||||
pre_job:
|
||||
continue-on-error: true
|
||||
runs-on: ubuntu-latest
|
||||
# Map a step output to a job output
|
||||
outputs:
|
||||
should_skip: ${{ steps.skip_check.outputs.should_skip }}
|
||||
steps:
|
||||
@ -22,7 +21,7 @@ jobs:
|
||||
with:
|
||||
concurrent_skipping: 'outdated_runs'
|
||||
skip_after_successful_duplicate: 'true'
|
||||
paths_ignore: '["**/*.md", "**/docs/**", "example_configs/**", "*.sh"]'
|
||||
paths_ignore: '["**/*.md", "**/docs/**", "example_configs/**", "*.sh", ".dockerignore", ".gitignore", "lldap_config.docker_template.toml", "Dockerfile"]'
|
||||
do_not_skip: '["workflow_dispatch", "schedule"]'
|
||||
cancel_others: true
|
||||
|
||||
@ -102,6 +101,13 @@ jobs:
|
||||
run: cargo llvm-cov --no-run --lcov --output-path lcov.info
|
||||
- name: Upload coverage to Codecov
|
||||
uses: codecov/codecov-action@v3
|
||||
if: github.ref != 'refs/heads/main' || github.event_name != 'push'
|
||||
with:
|
||||
files: lcov.info
|
||||
fail_ci_if_error: true
|
||||
- name: Upload coverage to Codecov (main)
|
||||
uses: codecov/codecov-action@v3
|
||||
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
|
||||
with:
|
||||
files: lcov.info
|
||||
fail_ci_if_error: true
|
||||
|
29
CHANGELOG.md
29
CHANGELOG.md
@ -5,6 +5,35 @@ All notable changes to this project will be documented in this file.
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [0.4.3] 2023-04-11
|
||||
|
||||
The repository has changed from `nitnelave/lldap` to `lldap/lldap`, both on GitHub
|
||||
and on DockerHub (although we will keep publishing the images to
|
||||
`nitnelave/lldap` for the foreseeable future). All data on GitHub has been
|
||||
migrated, and the new docker images are available both on DockerHub and on the
|
||||
GHCR under `lldap/lldap`.
|
||||
|
||||
### Added
|
||||
|
||||
- EC private keys are not supported for LDAPS.
|
||||
|
||||
### Changed
|
||||
|
||||
- SMTP user no longer has a default value (and instead defaults to unauthenticated).
|
||||
|
||||
### Fixed
|
||||
|
||||
- WASM payload is now delivered uncompressed to Safari due to a Safari bug.
|
||||
- Password reset no longer redirects to login page.
|
||||
- NextCloud config should add the "mail" attribute.
|
||||
- GraphQL parameters are now urldecoded, to support special characters in usernames.
|
||||
- Healthcheck correctly checks the server certificate.
|
||||
|
||||
### New services
|
||||
|
||||
- Home Assistant
|
||||
- Shaarli
|
||||
|
||||
## [0.4.2] - 2023-03-27
|
||||
|
||||
### Added
|
||||
|
15
Cargo.lock
generated
15
Cargo.lock
generated
@ -2345,7 +2345,7 @@ checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4"
|
||||
|
||||
[[package]]
|
||||
name = "lldap"
|
||||
version = "0.4.2-alpha"
|
||||
version = "0.4.4-alpha"
|
||||
dependencies = [
|
||||
"actix",
|
||||
"actix-files",
|
||||
@ -2404,13 +2404,14 @@ dependencies = [
|
||||
"tracing-forest",
|
||||
"tracing-log",
|
||||
"tracing-subscriber",
|
||||
"urlencoding",
|
||||
"uuid 0.8.2",
|
||||
"webpki-roots",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lldap_app"
|
||||
version = "0.4.2-alpha"
|
||||
version = "0.4.4-alpha"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"base64 0.13.1",
|
||||
@ -2441,7 +2442,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "lldap_auth"
|
||||
version = "0.3.0-alpha.1"
|
||||
version = "0.3.0"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"curve25519-dalek",
|
||||
@ -2544,7 +2545,7 @@ checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
|
||||
|
||||
[[package]]
|
||||
name = "migration-tool"
|
||||
version = "0.4.2-alpha"
|
||||
version = "0.4.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"base64 0.13.1",
|
||||
@ -4399,6 +4400,12 @@ dependencies = [
|
||||
"percent-encoding",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "urlencoding"
|
||||
version = "2.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e8db7427f936968176eaa7cdf81b7f98b980b18495ec28f1b5791ac3bfe3eea9"
|
||||
|
||||
[[package]]
|
||||
name = "uuid"
|
||||
version = "0.8.2"
|
||||
|
14
README.md
14
README.md
@ -23,8 +23,8 @@
|
||||
src="https://img.shields.io/badge/unsafe-forbidden-success.svg"
|
||||
alt="Unsafe forbidden"/>
|
||||
</a>
|
||||
<a href="https://app.codecov.io/gh/nitnelave/lldap">
|
||||
<img alt="Codecov" src="https://img.shields.io/codecov/c/github/nitnelave/lldap" />
|
||||
<a href="https://app.codecov.io/gh/lldap/lldap">
|
||||
<img alt="Codecov" src="https://img.shields.io/codecov/c/github/lldap/lldap" />
|
||||
</a>
|
||||
</p>
|
||||
|
||||
@ -77,6 +77,9 @@ For more features (OAuth/OpenID support, reverse proxy, ...) you can install
|
||||
other components (KeyCloak, Authelia, ...) using this server as the source of
|
||||
truth for users, via LDAP.
|
||||
|
||||
By default, the data is stored in SQLite, but you can swap the backend with
|
||||
MySQL/MariaDB or PostgreSQL.
|
||||
|
||||
## Installation
|
||||
|
||||
### With Docker
|
||||
@ -268,6 +271,7 @@ folder for help with:
|
||||
- [Portainer](example_configs/portainer.md)
|
||||
- [Rancher](example_configs/rancher.md)
|
||||
- [Seafile](example_configs/seafile.md)
|
||||
- [Shaarli](example_configs/shaarli.md)
|
||||
- [Syncthing](example_configs/syncthing.md)
|
||||
- [Vaultwarden](example_configs/vaultwarden.md)
|
||||
- [WeKan](example_configs/wekan.md)
|
||||
@ -276,6 +280,12 @@ folder for help with:
|
||||
- [XBackBone](example_configs/xbackbone_config.php)
|
||||
- [Zendto](example_configs/zendto.md)
|
||||
|
||||
## Migrating from SQLite
|
||||
|
||||
If you started with an SQLite database and would like to migrate to
|
||||
MySQL/MariaDB or PostgreSQL, check out the [DB
|
||||
migration docs](/docs/database_migration.md).
|
||||
|
||||
## Comparisons with other services
|
||||
|
||||
### vs OpenLDAP
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "lldap_app"
|
||||
version = "0.4.2"
|
||||
version = "0.4.4-alpha"
|
||||
authors = ["Valentin Tolmer <valentin@tolmer.fr>"]
|
||||
edition = "2021"
|
||||
include = ["src/**/*", "queries/**/*", "Cargo.toml", "../schema.graphql"]
|
||||
|
@ -14,4 +14,4 @@ fi
|
||||
|
||||
wasm-pack build --target web --release
|
||||
|
||||
gzip -9 -f pkg/lldap_app_bg.wasm
|
||||
gzip -9 -k -f pkg/lldap_app_bg.wasm
|
||||
|
@ -177,7 +177,13 @@ impl App {
|
||||
Some(AppRoute::StartResetPassword | AppRoute::FinishResetPassword { token: _ }),
|
||||
_,
|
||||
_,
|
||||
) if self.password_reset_enabled == Some(false) => Some(AppRoute::Login),
|
||||
) => {
|
||||
if self.password_reset_enabled == Some(false) {
|
||||
Some(AppRoute::Login)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
(None, _, _) | (_, None, _) => Some(AppRoute::Login),
|
||||
// User is logged in, a URL was given, don't redirect.
|
||||
(_, Some(_), Some(_)) => None,
|
||||
|
@ -1,6 +1,10 @@
|
||||
import init, { run_app } from '/pkg/lldap_app.js';
|
||||
async function main() {
|
||||
await init('/pkg/lldap_app_bg.wasm');
|
||||
run_app();
|
||||
if(navigator.userAgent.indexOf('AppleWebKit') != -1) {
|
||||
await init('/pkg/lldap_app_bg.wasm');
|
||||
} else {
|
||||
await init('/pkg/lldap_app_bg.wasm.gz');
|
||||
}
|
||||
run_app();
|
||||
}
|
||||
main()
|
||||
|
@ -26,9 +26,9 @@ Frontend:
|
||||
|
||||
Data storage:
|
||||
* The data (users, groups, memberships, active JWTs, ...) is stored in SQL.
|
||||
* Currently only SQLite is supported (see
|
||||
https://github.com/launchbadge/sqlx/issues/1225 for what blocks us from
|
||||
supporting more SQL backends).
|
||||
* The main SQL DBs are supported: SQLite by default, MySQL, MariaDB, PostgreSQL
|
||||
(see [DB Migration](/database_migration.md) for how to migrate off of
|
||||
SQLite).
|
||||
|
||||
### Code organization
|
||||
|
||||
|
@ -65,7 +65,8 @@ a transaction:
|
||||
```
|
||||
sed -i -r -e 's/^INSERT INTO "?([a-zA-Z0-9_]+)"?/INSERT INTO `\1`/' \
|
||||
-e '1s/^/START TRANSACTION;\n/' \
|
||||
-e '$aCOMMIT;' /path/to/dump.sql
|
||||
-e '$aCOMMIT;' \
|
||||
-e '1 i\SET FOREIGN_KEY_CHECKS = 0;' /path/to/dump.sql
|
||||
```
|
||||
|
||||
### To MariaDB
|
||||
@ -77,7 +78,8 @@ strings. Use the following command to remove those and perform the additional My
|
||||
sed -i -r -e "s/([^']'[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]{9})\+00:00'([^'])/\1'\2/g" \
|
||||
-e 's/^INSERT INTO "?([a-zA-Z0-9_]+)"?/INSERT INTO `\1`/' \
|
||||
-e '1s/^/START TRANSACTION;\n/' \
|
||||
-e '$aCOMMIT;' /path/to/dump.sql
|
||||
-e '$aCOMMIT;' \
|
||||
-e '1 i\SET FOREIGN_KEY_CHECKS = 0;' /path/to/dump.sql
|
||||
```
|
||||
|
||||
## Insert data
|
||||
@ -102,4 +104,6 @@ or
|
||||
|
||||
Modify your `database_url` in `lldap_config.toml` (or `LLDAP_DATABASE_URL` in the env)
|
||||
to point to your new database (the same value used when generating schema). Restart
|
||||
LLDAP and check the logs to ensure there were no errors.
|
||||
LLDAP and check the logs to ensure there were no errors.
|
||||
|
||||
#### More details/examples can be seen in the CI process [here](https://raw.githubusercontent.com/nitnelave/lldap/main/.github/workflows/docker-build-static.yml), look for the job `lldap-database-migration-test`
|
||||
|
23
example_configs/home-assistant.md
Normal file
23
example_configs/home-assistant.md
Normal file
@ -0,0 +1,23 @@
|
||||
# Home Assistant Configuration
|
||||
|
||||
Home Assistant configures ldap auth via the [Command Line Auth Provider](https://www.home-assistant.io/docs/authentication/providers/#command-line). The wiki mentions a script that can be used for LDAP authentication, but it doesn't work in the container version (it is lacking both `ldapsearch` and `curl` ldap protocol support). Thankfully LLDAP has a graphql API to save the day!
|
||||
|
||||
## Graphql-based Auth Script
|
||||
|
||||
The [auth script](lldap-ha-auth.sh) attempts to authenticate a user against an LLDAP server, using credentials provided via `username` and `password` environment variables. The first argument must be the URL of your LLDAP server, accessible from Home Assistant. You can provide an additional optional argument to confine allowed logins to a single group. The script will output the user's display name as the `name` variable, if not empty.
|
||||
|
||||
1. Copy the [auth script](lldap-ha-auth.sh) to your home assistant instance. In this example, we use `/config/lldap-auth.sh`.
|
||||
2. Add the following to your configuration.yaml in Home assistant:
|
||||
```yaml
|
||||
homeassistant:
|
||||
auth_providers:
|
||||
# Ensure you have the homeassistant provider enabled if you want to continue using your existing accounts
|
||||
- type: homeassistant
|
||||
- type: command_line
|
||||
command: /config/lldap-auth.sh
|
||||
# Only allow users in the 'homeassistant_user' group to login.
|
||||
# Change to ["https://lldap.example.com"] to allow all users
|
||||
args: ["https://lldap.example.com", "homeassistant_user"]
|
||||
meta: true
|
||||
```
|
||||
3. Reload your config or restart Home Assistant
|
70
example_configs/lldap-ha-auth.sh
Normal file
70
example_configs/lldap-ha-auth.sh
Normal file
@ -0,0 +1,70 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Usernames should be validated using a regular expression to be of
|
||||
# a known format. Special characters will be escaped anyway, but it is
|
||||
# generally not recommended to allow more than necessary.
|
||||
# This pattern is set by default. In your config file, you can either
|
||||
# overwrite it with a different one or use "unset USERNAME_PATTERN" to
|
||||
# disable validation completely.
|
||||
USERNAME_PATTERN='^[a-z|A-Z|0-9|_|-|.]+$'
|
||||
|
||||
# When the timeout (in seconds) is exceeded (e.g. due to slow networking),
|
||||
# authentication fails.
|
||||
TIMEOUT=3
|
||||
|
||||
# Log messages to stderr.
|
||||
log() {
|
||||
echo "$1" >&2
|
||||
}
|
||||
|
||||
# Get server address
|
||||
if [ -z "$1" ]; then
|
||||
log "Usage: lldap-auth.sh <LLDAP server address> <Optional group to filter>"
|
||||
exit 2
|
||||
fi
|
||||
SERVER_URL="${1%/}"
|
||||
|
||||
# Check username and password are present and not malformed.
|
||||
if [ -z "$username" ] || [ -z "$password" ]; then
|
||||
log "Need username and password environment variables."
|
||||
exit 2
|
||||
elif [ ! -z "$USERNAME_PATTERN" ]; then
|
||||
username_match=$(echo "$username" | sed -r "s/$USERNAME_PATTERN/x/")
|
||||
if [ "$username_match" != "x" ]; then
|
||||
log "Username '$username' has an invalid format."
|
||||
exit 2
|
||||
fi
|
||||
fi
|
||||
|
||||
RESPONSE=$(curl -f -s -X POST -m "$TIMEOUT" -H "Content-type: application/json" -d '{"username":"'"$username"'","password":"'"$password"'"}' "$SERVER_URL/auth/simple/login")
|
||||
if [[ $? -ne 0 ]]; then
|
||||
log "Auth failed"
|
||||
exit 1
|
||||
fi
|
||||
TOKEN=$(jq -e -r .token <<< $RESPONSE)
|
||||
if [[ $? -ne 0 ]]; then
|
||||
log "Failed to parse token"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
RESPONSE=$(curl -f -s -m "$TIMEOUT" -H "Content-type: application/json" -H "Authorization: Bearer ${TOKEN}" -d '{"variables":{"id":"'"$username"'"},"query":"query($id:String!){user(userId:$id){displayName groups{displayName}}}"}' "$SERVER_URL/api/graphql")
|
||||
if [[ $? -ne 0 ]]; then
|
||||
log "Failed to get user"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
USER_JSON=$(jq -e .data.user <<< $RESPONSE)
|
||||
if [[ $? -ne 0 ]]; then
|
||||
log "Failed to parse user json"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ ! -z "$2" ]] && ! jq -e '.groups|map(.displayName)|index("'"$2"'")' <<< $USER_JSON > /dev/null 2>&1; then
|
||||
log "User is not in group '$2'"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
DISPLAY_NAME=$(jq -r .displayName <<< $USER_JSON)
|
||||
|
||||
[[ ! -z "$DISPLAY_NAME" ]] && echo "name = $DISPLAY_NAME"
|
||||
|
@ -62,6 +62,7 @@ occ ldap:set-config s01 ldapGroupFilterMode 0
|
||||
occ ldap:set-config s01 ldapGroupDisplayName cn
|
||||
occ ldap:set-config s01 ldapGroupFilterObjectclass groupOfUniqueNames
|
||||
occ ldap:set-config s01 ldapGroupMemberAssocAttr uniqueMember
|
||||
occ ldap:set-config s01 ldapEmailAttribute "mail"
|
||||
occ ldap:set-config s01 ldapLoginFilterEmail 0
|
||||
occ ldap:set-config s01 ldapLoginFilterUsername 1
|
||||
occ ldap:set-config s01 ldapMatchingRuleInChainState unknown
|
||||
|
11
example_configs/shaarli.md
Normal file
11
example_configs/shaarli.md
Normal file
@ -0,0 +1,11 @@
|
||||
# Configuration for shaarli
|
||||
|
||||
LDAP configuration is in ```/data/config.json.php```
|
||||
|
||||
Just add the following lines:
|
||||
```
|
||||
"ldap": {
|
||||
"host": "ldap://lldap_server:3890",
|
||||
"dn": "uid=%s,ou=people,dc=example,dc=com"
|
||||
}
|
||||
```
|
@ -2,7 +2,7 @@
|
||||
authors = ["Valentin Tolmer <valentin@tolmer.fr>"]
|
||||
edition = "2021"
|
||||
name = "lldap"
|
||||
version = "0.4.2"
|
||||
version = "0.4.4-alpha"
|
||||
|
||||
[dependencies]
|
||||
actix = "0.13"
|
||||
@ -31,8 +31,9 @@ lber = "0.4.1"
|
||||
ldap3_proto = ">=0.3.1"
|
||||
log = "*"
|
||||
orion = "0.17"
|
||||
rustls = "0.20"
|
||||
rustls-pemfile = "1"
|
||||
serde = "*"
|
||||
serde_bytes = "0.11"
|
||||
serde_json = "1"
|
||||
sha2 = "0.10"
|
||||
thiserror = "*"
|
||||
@ -44,8 +45,7 @@ tracing = "*"
|
||||
tracing-actix-web = "0.7"
|
||||
tracing-attributes = "^0.1.21"
|
||||
tracing-log = "*"
|
||||
rustls-pemfile = "1"
|
||||
serde_bytes = "0.11"
|
||||
urlencoding = "2"
|
||||
webpki-roots = "*"
|
||||
|
||||
[dependencies.chrono]
|
||||
@ -114,5 +114,9 @@ version = "0.11"
|
||||
default-features = false
|
||||
features = ["rustls-tls-webpki-roots"]
|
||||
|
||||
[dependencies.rustls]
|
||||
version = "0.20"
|
||||
features = ["dangerous_configuration"]
|
||||
|
||||
[dev-dependencies]
|
||||
mockall = "0.11"
|
||||
|
@ -18,7 +18,7 @@ use hmac::Hmac;
|
||||
use jwt::{SignWithKey, VerifyWithKey};
|
||||
use sha2::Sha512;
|
||||
use time::ext::NumericalDuration;
|
||||
use tracing::{debug, instrument, warn};
|
||||
use tracing::{debug, info, instrument, warn};
|
||||
|
||||
use lldap_auth::{login, password_reset, registration, JWTClaims};
|
||||
|
||||
@ -183,6 +183,7 @@ where
|
||||
.await
|
||||
{
|
||||
warn!("Error sending email: {:#?}", e);
|
||||
info!("Reset token: {}", token);
|
||||
return Err(TcpError::InternalServerError(format!(
|
||||
"Could not send email: {}",
|
||||
e
|
||||
|
@ -132,6 +132,10 @@ pub enum SmtpEncryption {
|
||||
#[derive(Debug, Parser, Clone)]
|
||||
#[clap(next_help_heading = Some("SMTP"))]
|
||||
pub struct SmtpOpts {
|
||||
/// Enable password reset.
|
||||
#[clap(long, env = "LLDAP_SMTP_OPTIONS__ENABLE_PASSWORD_RESET")]
|
||||
pub smtp_enable_password_reset: Option<bool>,
|
||||
|
||||
/// Sender email address.
|
||||
#[clap(long, env = "LLDAP_SMTP_OPTIONS__FROM")]
|
||||
pub smtp_from: Option<Mailbox>,
|
||||
|
@ -25,7 +25,7 @@ pub struct MailOptions {
|
||||
pub server: String,
|
||||
#[builder(default = "587")]
|
||||
pub port: u16,
|
||||
#[builder(default = r#""admin".to_string()"#)]
|
||||
#[builder(default = r#"String::default()"#)]
|
||||
pub user: String,
|
||||
#[builder(default = r#"SecUtf8::from("")"#)]
|
||||
pub password: SecUtf8,
|
||||
@ -276,6 +276,9 @@ impl ConfigOverrider for SmtpOpts {
|
||||
if let Some(tls_required) = self.smtp_tls_required {
|
||||
config.smtp_options.tls_required = Some(tls_required);
|
||||
}
|
||||
if let Some(enable_password_reset) = self.smtp_enable_password_reset {
|
||||
config.smtp_options.enable_password_reset = enable_password_reset;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -124,10 +124,12 @@ impl<Handler: BackendHandler> Query<Handler> {
|
||||
}
|
||||
|
||||
pub async fn user(context: &Context<Handler>, user_id: String) -> FieldResult<User<Handler>> {
|
||||
use anyhow::Context;
|
||||
let span = debug_span!("[GraphQL query] user");
|
||||
span.in_scope(|| {
|
||||
debug!(?user_id);
|
||||
});
|
||||
let user_id = urlencoding::decode(&user_id).context("Invalid user parameter")?;
|
||||
let user_id = UserId::new(&user_id);
|
||||
let handler = context
|
||||
.get_readable_handler(&user_id)
|
||||
|
@ -1,4 +1,4 @@
|
||||
use crate::infra::configuration::LdapsOptions;
|
||||
use crate::infra::{configuration::LdapsOptions, ldap_server::read_certificates};
|
||||
use anyhow::{anyhow, bail, ensure, Context, Result};
|
||||
use futures_util::SinkExt;
|
||||
use ldap3_proto::{
|
||||
@ -65,6 +65,7 @@ where
|
||||
invalid_answer
|
||||
);
|
||||
info!("Success");
|
||||
resp.close().await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -85,15 +86,44 @@ fn get_root_certificates() -> rustls::RootCertStore {
|
||||
root_store
|
||||
}
|
||||
|
||||
fn get_tls_connector() -> Result<RustlsTlsConnector> {
|
||||
use rustls::ClientConfig;
|
||||
let client_config = std::sync::Arc::new(
|
||||
ClientConfig::builder()
|
||||
.with_safe_defaults()
|
||||
.with_root_certificates(get_root_certificates())
|
||||
.with_no_client_auth(),
|
||||
);
|
||||
Ok(client_config.into())
|
||||
fn get_tls_connector(ldaps_options: &LdapsOptions) -> Result<RustlsTlsConnector> {
|
||||
let mut client_config = rustls::ClientConfig::builder()
|
||||
.with_safe_defaults()
|
||||
.with_root_certificates(get_root_certificates())
|
||||
.with_no_client_auth();
|
||||
let (certs, _private_key) = read_certificates(ldaps_options)?;
|
||||
// Check that the server cert is the one in the config file.
|
||||
struct CertificateVerifier {
|
||||
certificate: rustls::Certificate,
|
||||
certificate_path: String,
|
||||
}
|
||||
impl rustls::client::ServerCertVerifier for CertificateVerifier {
|
||||
fn verify_server_cert(
|
||||
&self,
|
||||
end_entity: &rustls::Certificate,
|
||||
_intermediates: &[rustls::Certificate],
|
||||
_server_name: &rustls::ServerName,
|
||||
_scts: &mut dyn Iterator<Item = &[u8]>,
|
||||
_ocsp_response: &[u8],
|
||||
_now: std::time::SystemTime,
|
||||
) -> std::result::Result<rustls::client::ServerCertVerified, rustls::Error> {
|
||||
if end_entity != &self.certificate {
|
||||
return Err(rustls::Error::InvalidCertificateData(format!(
|
||||
"Server certificate doesn't match the one in the config file {}",
|
||||
&self.certificate_path
|
||||
)));
|
||||
}
|
||||
Ok(rustls::client::ServerCertVerified::assertion())
|
||||
}
|
||||
}
|
||||
let mut dangerous_config = rustls::client::DangerousClientConfig {
|
||||
cfg: &mut client_config,
|
||||
};
|
||||
dangerous_config.set_certificate_verifier(std::sync::Arc::new(CertificateVerifier {
|
||||
certificate: certs.first().expect("empty certificate chain").clone(),
|
||||
certificate_path: ldaps_options.cert_file.clone(),
|
||||
}));
|
||||
Ok(std::sync::Arc::new(client_config).into())
|
||||
}
|
||||
|
||||
#[instrument(skip_all, level = "info", err)]
|
||||
@ -102,15 +132,20 @@ pub async fn check_ldaps(ldaps_options: &LdapsOptions) -> Result<()> {
|
||||
info!("LDAPS not enabled");
|
||||
return Ok(());
|
||||
};
|
||||
let tls_connector = get_tls_connector()?;
|
||||
let tls_connector =
|
||||
get_tls_connector(ldaps_options).context("while preparing the tls connection")?;
|
||||
let url = format!("localhost:{}", ldaps_options.port);
|
||||
check_ldap_endpoint(
|
||||
tls_connector
|
||||
.connect(
|
||||
rustls::ServerName::try_from(url.as_str())?,
|
||||
TcpStream::connect(&url).await?,
|
||||
rustls::ServerName::try_from("localhost")
|
||||
.context("while parsing the server name")?,
|
||||
TcpStream::connect(&url)
|
||||
.await
|
||||
.context("while connecting TCP")?,
|
||||
)
|
||||
.await?,
|
||||
.await
|
||||
.context("while connecting TLS")?,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
@ -4,7 +4,8 @@ use crate::{
|
||||
opaque_handler::OpaqueHandler,
|
||||
},
|
||||
infra::{
|
||||
access_control::AccessControlledBackendHandler, configuration::Configuration,
|
||||
access_control::AccessControlledBackendHandler,
|
||||
configuration::{Configuration, LdapsOptions},
|
||||
ldap_handler::LdapHandler,
|
||||
},
|
||||
};
|
||||
@ -94,7 +95,7 @@ where
|
||||
}
|
||||
|
||||
fn read_private_key(key_file: &str) -> Result<PrivateKey> {
|
||||
use rustls_pemfile::{pkcs8_private_keys, rsa_private_keys};
|
||||
use rustls_pemfile::{ec_private_keys, pkcs8_private_keys, rsa_private_keys};
|
||||
use std::{fs::File, io::BufReader};
|
||||
pkcs8_private_keys(&mut BufReader::new(File::open(key_file)?))
|
||||
.map_err(anyhow::Error::from)
|
||||
@ -112,29 +113,36 @@ fn read_private_key(key_file: &str) -> Result<PrivateKey> {
|
||||
.ok_or_else(|| anyhow!("No PKCS1 key"))
|
||||
})
|
||||
})
|
||||
.or_else(|_| {
|
||||
ec_private_keys(&mut BufReader::new(File::open(key_file)?))
|
||||
.map_err(anyhow::Error::from)
|
||||
.and_then(|keys| keys.into_iter().next().ok_or_else(|| anyhow!("No EC key")))
|
||||
})
|
||||
.with_context(|| {
|
||||
format!(
|
||||
"Cannot read either PKCS1 or PKCS8 private key from {}",
|
||||
"Cannot read either PKCS1, PKCS8 or EC private key from {}",
|
||||
key_file
|
||||
)
|
||||
})
|
||||
.map(rustls::PrivateKey)
|
||||
}
|
||||
|
||||
fn get_tls_acceptor(config: &Configuration) -> Result<RustlsTlsAcceptor> {
|
||||
use rustls::{Certificate, ServerConfig};
|
||||
use rustls_pemfile::certs;
|
||||
pub fn read_certificates(
|
||||
ldaps_options: &LdapsOptions,
|
||||
) -> Result<(Vec<rustls::Certificate>, rustls::PrivateKey)> {
|
||||
use std::{fs::File, io::BufReader};
|
||||
// Load TLS key and cert files
|
||||
let certs = certs(&mut BufReader::new(File::open(
|
||||
&config.ldaps_options.cert_file,
|
||||
)?))?
|
||||
.into_iter()
|
||||
.map(Certificate)
|
||||
.collect::<Vec<_>>();
|
||||
let private_key = read_private_key(&config.ldaps_options.key_file)?;
|
||||
let certs = rustls_pemfile::certs(&mut BufReader::new(File::open(&ldaps_options.cert_file)?))?
|
||||
.into_iter()
|
||||
.map(rustls::Certificate)
|
||||
.collect::<Vec<_>>();
|
||||
let private_key = read_private_key(&ldaps_options.key_file)?;
|
||||
Ok((certs, private_key))
|
||||
}
|
||||
|
||||
fn get_tls_acceptor(ldaps_options: &LdapsOptions) -> Result<RustlsTlsAcceptor> {
|
||||
let (certs, private_key) = read_certificates(ldaps_options)?;
|
||||
let server_config = std::sync::Arc::new(
|
||||
ServerConfig::builder()
|
||||
rustls::ServerConfig::builder()
|
||||
.with_safe_defaults()
|
||||
.with_no_client_auth()
|
||||
.with_single_cert(certs, private_key)?,
|
||||
@ -185,7 +193,8 @@ where
|
||||
if config.ldaps_options.enabled {
|
||||
let tls_context = (
|
||||
context_for_tls,
|
||||
get_tls_acceptor(config).context("while setting up the SSL certificate")?,
|
||||
get_tls_acceptor(&config.ldaps_options)
|
||||
.context("while setting up the SSL certificate")?,
|
||||
);
|
||||
let tls_binder = move || {
|
||||
let tls_context = tls_context.clone();
|
||||
|
@ -16,7 +16,7 @@ use actix_files::{Files, NamedFile};
|
||||
use actix_http::{header, HttpServiceBuilder};
|
||||
use actix_server::ServerBuilder;
|
||||
use actix_service::map_config;
|
||||
use actix_web::{dev::AppConfig, guard, middleware, web, App, HttpResponse, Responder};
|
||||
use actix_web::{dev::AppConfig, guard, web, App, HttpResponse, Responder};
|
||||
use anyhow::{Context, Result};
|
||||
use hmac::Hmac;
|
||||
use sha2::Sha512;
|
||||
@ -68,6 +68,10 @@ pub(crate) fn error_to_http_response(error: TcpError) -> HttpResponse {
|
||||
}
|
||||
|
||||
async fn wasm_handler() -> actix_web::Result<impl Responder> {
|
||||
Ok(actix_files::NamedFile::open_async("./app/pkg/lldap_app_bg.wasm").await?)
|
||||
}
|
||||
|
||||
async fn wasm_handler_compressed() -> actix_web::Result<impl Responder> {
|
||||
Ok(
|
||||
actix_files::NamedFile::open_async("./app/pkg/lldap_app_bg.wasm.gz")
|
||||
.await?
|
||||
@ -110,12 +114,9 @@ fn http_config<Backend>(
|
||||
.configure(super::graphql::api::configure_endpoint::<Backend>),
|
||||
)
|
||||
.service(
|
||||
web::resource("/pkg/lldap_app_bg.wasm").route(
|
||||
web::route()
|
||||
.wrap(middleware::Compress::default())
|
||||
.to(wasm_handler),
|
||||
),
|
||||
web::resource("/pkg/lldap_app_bg.wasm.gz").route(web::route().to(wasm_handler_compressed)),
|
||||
)
|
||||
.service(web::resource("/pkg/lldap_app_bg.wasm").route(web::route().to(wasm_handler)))
|
||||
// Serve the /pkg path with the compiled WASM app.
|
||||
.service(Files::new("/pkg", "./app/pkg"))
|
||||
// Serve static files
|
||||
|
Loading…
Reference in New Issue
Block a user