19 Commits
v0.6.0 ... main

Author SHA1 Message Date
Valentin Tolmer
f417427635 Prevent starting up if the JWT secret is not given
Similarly, don't create the admin if the password is not given
2024-12-24 19:40:26 +01:00
Dakota G
1f26262e13 example_configs: add Hashicorp 2024-12-10 07:34:50 +01:00
Zepmann
42fccf4713 readme: Updated Arch Linux install-from-repository section
Cleaned up the Arch Linux section. Added a link to the discussions support thread.
2024-12-07 18:49:58 +01:00
xeoneox
928faa4bcc example_configs: add search filter in onedev configuration 2024-12-07 07:17:52 +01:00
xeoneox
3895a5050d example_configs: Update OneDev example for latest release 2024-12-06 00:21:35 +01:00
Christian Medel
f92035b6fd example_configs: Add Kimai 2024-11-25 22:20:09 +01:00
Valentin Tolmer
37a10c871f github: Fix release bot clearing the release body 2024-11-22 23:12:36 +01:00
Valentin Tolmer
8397d536d9 chore: bump version to 0.6.2-alpha 2024-11-22 22:55:53 +01:00
Valentin Tolmer
acd39d20b1 release: 0.6.1 2024-11-22 22:47:49 +01:00
Valentin Tolmer
0ddeab8caa server: Fix schema migration from v8 for sqlite and postgres
Neither supports limits, but we can delete all the duplicate memberships and re-insert a single one
2024-11-21 23:34:37 +01:00
xeoneox
64514ddfc6 example_configs: expand url for OneDev config
fix capitalization and expound URL example
2024-11-21 10:01:24 +01:00
Valentin Tolmer
c47be779a3 docs: update architecture.md 2024-11-19 22:07:02 +01:00
xeoneox
fea2ed5b79 example_configs: Add onedev 2024-11-19 22:01:30 +01:00
Jan Düpmeier
e982908768 cargo,auth,server: update opaque-ke => 0.7 2024-11-17 13:34:01 +01:00
Valentin Tolmer
713dbde4cb server: Fix the instructions to silence the key_seed warning 2024-11-14 22:27:32 +01:00
Ansgar Tasler
579dd5e1b6 readme: add reference to terraform provider (#1035) 2024-11-13 16:04:41 +01:00
traverseda
3828ec7624 example_configs: Update pam example for release 0.6..0 2024-11-13 12:38:45 +01:00
Valentin Tolmer
b8c06ebd75 chore: bump version to 0.6.1-alpha 2024-11-09 22:25:13 +01:00
Valentin Tolmer
130d2552ac github: Remove release PR comment bot
It only runs for PRs that are mentioned in the release notes, but I only mention issues
2024-11-09 22:20:31 +01:00
22 changed files with 439 additions and 244 deletions

View File

@@ -216,6 +216,8 @@ jobs:
LLDAP_database_url: postgres://lldapuser:lldappass@localhost/lldap
LLDAP_ldap_port: 3890
LLDAP_http_port: 17170
LLDAP_JWT_SECRET: verysecret
LLDAP_LDAP_USER_PASS: password
- name: Run lldap with mariadb DB (MySQL Compatible) and healthcheck
@@ -227,6 +229,8 @@ jobs:
LLDAP_database_url: mysql://lldapuser:lldappass@localhost/lldap
LLDAP_ldap_port: 3891
LLDAP_http_port: 17171
LLDAP_JWT_SECRET: verysecret
LLDAP_LDAP_USER_PASS: password
- name: Run lldap with sqlite DB and healthcheck
@@ -238,6 +242,8 @@ jobs:
LLDAP_database_url: sqlite://users.db?mode=rwc
LLDAP_ldap_port: 3892
LLDAP_http_port: 17172
LLDAP_JWT_SECRET: verysecret
LLDAP_LDAP_USER_PASS: password
- name: Check DB container logs
run: |
@@ -324,9 +330,9 @@ jobs:
sleep 10s
bin/lldap healthcheck
env:
LLDAP_database_url: sqlite://users.db?mode=rwc
LLDAP_ldap_port: 3890
LLDAP_http_port: 17170
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
@@ -350,8 +356,11 @@ jobs:
sed -i -r -e "s/X'([[:xdigit:]]+'[^'])/'\\\x\\1/g" -e ":a; s/(INSERT INTO (user_attribute_schema|jwt_storage)\(.*\) VALUES\(.*),1([^']*\);)$/\1,true\3/; s/(INSERT INTO (user_attribute_schema|jwt_storage)\(.*\) VALUES\(.*),0([^']*\);)$/\1,false\3/; ta" -e '1s/^/BEGIN;\n/' -e '$aCOMMIT;' ./dump.sql
- name: Create schema on postgres
env:
LLDAP_DATABASE_URL: postgres://lldapuser:lldappass@localhost:5432/lldap
LLDAP_JWT_SECRET: somejwtsecret
run: |
bin/lldap create_schema -d postgres://lldapuser:lldappass@localhost:5432/lldap
bin/lldap create_schema
- name: Copy converted db to postgress and import
run: |
@@ -368,7 +377,10 @@ jobs:
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
env:
LLDAP_DATABASE_URL: mysql://lldapuser:lldappass@localhost:3306/lldap
LLDAP_JWT_SECRET: somejwtsecret
run: bin/lldap create_schema
- name: Copy converted db to mariadb and import
run: |
@@ -384,7 +396,10 @@ jobs:
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
env:
LLDAP_DATABASE_URL: mysql://lldapuser:lldappass@localhost:3307/lldap
LLDAP_JWT_SECRET: somejwtsecret
run: bin/lldap create_schema
- name: Copy converted db to mysql and import
run: |
@@ -399,10 +414,9 @@ jobs:
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_DATABASE_URL: postgres://lldapuser:lldappass@localhost:5432/lldap
LLDAP_LDAP_PORT: 3891
LLDAP_HTTP_PORT: 17171
LLDAP_JWT_SECRET: somejwtsecret
- name: Run lldap with mariaDB and healthcheck again
@@ -411,9 +425,9 @@ jobs:
sleep 10s
bin/lldap healthcheck
env:
LLDAP_database_url: mysql://lldapuser:lldappass@localhost:3306/lldap
LLDAP_ldap_port: 3892
LLDAP_http_port: 17172
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
@@ -422,9 +436,9 @@ jobs:
sleep 10s
bin/lldap healthcheck
env:
LLDAP_database_url: mysql://lldapuser:lldappass@localhost:3307/lldap
LLDAP_ldap_port: 3893
LLDAP_http_port: 17173
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 Postgres
@@ -738,5 +752,9 @@ jobs:
artifacts: aarch64-lldap.tar.gz,
amd64-lldap.tar.gz,
armhf-lldap.tar.gz
draft: true
omitBodyDuringUpdate: true
omitDraftDuringUpdate: true
omitNameDuringUpdate: true
env:
GITHUB_TOKEN: ${{ github.token }}

View File

@@ -1,20 +0,0 @@
name: Release Bot
on:
release:
types: [published]
jobs:
comment:
runs-on: ubuntu-latest
permissions:
issues: write
pull-requests: write
steps:
- uses: nflaig/release-comment-on-pr@master
with:
token: ${{ secrets.RELEASE_BOT_TOKEN }}
message: |
Thank you everyone for the contribution!
This feature is now available in the latest release, [${releaseTag}](${releaseUrl}).
You can support LLDAP by starring our repo, contributing some configuration examples and becoming a sponsor.

View File

@@ -5,6 +5,27 @@ 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.6.1] 2024-11-22
Small release, mainly to fix a migration issue with Sqlite and Postgresql.
### Added
- Added a link to a community terraform provider (#1035)
### Changed
- The opaque dependency now points to the official crate rather than a fork (#1040)
### Fixed
- Migration of the DB schema from 7 to 8 is now automatic for sqlite, and fixed for postgres (#1045)
- The startup warning about `key_seed` applying instead of `key_file` now has instructions on how to silence it (#1032)
### New services
- OneDev
## [0.6.0] 2024-11-09
### Breaking

190
Cargo.lock generated
View File

@@ -113,7 +113,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e01ed3140b2f8d422c68afa1ed2e85d996ea619c988ac834d255db32138655cb"
dependencies = [
"quote",
"syn 2.0.79",
"syn 2.0.86",
]
[[package]]
@@ -250,7 +250,7 @@ dependencies = [
"actix-router",
"proc-macro2",
"quote",
"syn 2.0.79",
"syn 2.0.86",
]
[[package]]
@@ -276,7 +276,7 @@ checksum = "b6ac1e58cded18cb28ddc17143c4dea5345b3ad575e14f32f66e4054a56eb271"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.79",
"syn 2.0.86",
]
[[package]]
@@ -360,9 +360,9 @@ dependencies = [
[[package]]
name = "anstream"
version = "0.6.15"
version = "0.6.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526"
checksum = "23a1e53f0f5d86382dafe1cf314783b2044280f406e7e1506368220ad11b1338"
dependencies = [
"anstyle",
"anstyle-parse",
@@ -375,43 +375,43 @@ dependencies = [
[[package]]
name = "anstyle"
version = "1.0.8"
version = "1.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1"
checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9"
[[package]]
name = "anstyle-parse"
version = "0.2.5"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb"
checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9"
dependencies = [
"utf8parse",
]
[[package]]
name = "anstyle-query"
version = "1.1.1"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a"
checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c"
dependencies = [
"windows-sys 0.52.0",
"windows-sys 0.59.0",
]
[[package]]
name = "anstyle-wincon"
version = "3.0.4"
version = "3.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8"
checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125"
dependencies = [
"anstyle",
"windows-sys 0.52.0",
"windows-sys 0.59.0",
]
[[package]]
name = "anyhow"
version = "1.0.89"
version = "1.0.92"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "86fdf8605db99b54d3cd748a44c6d04df638eb5dafb219b135d0149bd0db01f6"
checksum = "74f37166d7d48a0284b99dd824694c26119c700b53bf0d1540cdb147dbdaaf13"
[[package]]
name = "arrayref"
@@ -505,7 +505,7 @@ checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.79",
"syn 2.0.86",
]
[[package]]
@@ -516,7 +516,7 @@ checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.79",
"syn 2.0.86",
]
[[package]]
@@ -710,9 +710,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
[[package]]
name = "bytes"
version = "1.7.2"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3"
checksum = "9ac0150caa2ae65ca5bd83f25c7de183dea78d4d366469f148435e2acfbad0da"
[[package]]
name = "bytestring"
@@ -725,9 +725,9 @@ dependencies = [
[[package]]
name = "cc"
version = "1.1.30"
version = "1.1.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b16803a61b81d9eabb7eae2588776c4c1e584b738ede45fdbb4c972cec1e9945"
checksum = "e3788d6ac30243803df38a3e9991cf37e41210232916d41a8222ae378f912624"
dependencies = [
"jobserver",
"libc",
@@ -786,7 +786,7 @@ dependencies = [
"heck 0.5.0",
"proc-macro2",
"quote",
"syn 2.0.79",
"syn 2.0.86",
]
[[package]]
@@ -803,9 +803,9 @@ checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"
[[package]]
name = "colorchoice"
version = "1.0.2"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0"
checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990"
[[package]]
name = "combine"
@@ -1158,7 +1158,7 @@ dependencies = [
"proc-macro2",
"quote",
"rustc_version",
"syn 2.0.79",
"syn 2.0.86",
]
[[package]]
@@ -1178,7 +1178,7 @@ checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.79",
"syn 2.0.86",
"unicode-xid",
]
@@ -1245,7 +1245,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.79",
"syn 2.0.86",
]
[[package]]
@@ -1293,9 +1293,9 @@ checksum = "e079f19b08ca6239f47f8ba8509c11cf3ea30095831f7fed61441475edd8c449"
[[package]]
name = "encoding_rs"
version = "0.8.34"
version = "0.8.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59"
checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3"
dependencies = [
"cfg-if",
]
@@ -1422,9 +1422,9 @@ dependencies = [
[[package]]
name = "flume"
version = "0.11.0"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "55ac459de2512911e4b674ce33cf20befaba382d05b62b008afc1c8b57cbf181"
checksum = "da0e4dd2a88388a1f4ccc7c9ce104604dab68d9f408dc34cd45823d5a9069095"
dependencies = [
"futures-core",
"futures-sink",
@@ -1540,7 +1540,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.79",
"syn 2.0.86",
]
[[package]]
@@ -2233,7 +2233,7 @@ checksum = "0122b7114117e64a63ac49f752a5ca4624d534c7b1c7de796ac196381cd2d947"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.79",
"syn 2.0.86",
]
[[package]]
@@ -2384,7 +2384,7 @@ dependencies = [
[[package]]
name = "lber"
version = "0.4.3"
source = "git+https://github.com/inejge/ldap3/#5238c544aa0785b6999f20702131b9982e132e18"
source = "git+https://github.com/inejge/ldap3/#ba38bc573d63b109b92dca80898633512d5c47df"
dependencies = [
"bytes",
"nom",
@@ -2464,15 +2464,15 @@ dependencies = [
[[package]]
name = "libc"
version = "0.2.159"
version = "0.2.161"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5"
checksum = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1"
[[package]]
name = "libm"
version = "0.2.8"
version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058"
checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa"
[[package]]
name = "libsqlite3-sys"
@@ -2499,7 +2499,7 @@ checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89"
[[package]]
name = "lldap"
version = "0.6.0"
version = "0.6.2-alpha"
dependencies = [
"actix",
"actix-files",
@@ -2575,7 +2575,7 @@ dependencies = [
[[package]]
name = "lldap_app"
version = "0.6.0"
version = "0.6.2-alpha"
dependencies = [
"anyhow",
"base64 0.13.1",
@@ -2949,8 +2949,9 @@ checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381"
[[package]]
name = "opaque-ke"
version = "0.6.1"
source = "git+https://github.com/nitnelave/opaque-ke/?branch=zeroize_1.5#2f7f3a694516a7d1be4182945bed3d910cad777a"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aadda6db12e367812aea6e025158bf7f334d21aae853f7c46e0a3bdde70a26f5"
dependencies = [
"base64 0.13.1",
"curve25519-dalek",
@@ -3016,7 +3017,7 @@ dependencies = [
"proc-macro-error",
"proc-macro2",
"quote",
"syn 2.0.79",
"syn 2.0.86",
]
[[package]]
@@ -3074,7 +3075,7 @@ dependencies = [
"proc-macro2",
"proc-macro2-diagnostics",
"quote",
"syn 2.0.79",
"syn 2.0.86",
]
[[package]]
@@ -3121,29 +3122,29 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
[[package]]
name = "pin-project"
version = "1.1.6"
version = "1.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf123a161dde1e524adf36f90bc5d8d3462824a9c43553ad07a8183161189ec"
checksum = "be57f64e946e500c8ee36ef6331845d40a93055567ec57e8fae13efd33759b95"
dependencies = [
"pin-project-internal",
]
[[package]]
name = "pin-project-internal"
version = "1.1.6"
version = "1.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4502d8515ca9f32f1fb543d987f63d95a14934883db45bdb48060b6b69257f8"
checksum = "3c0f5fad0874fc7abcd4d750e76917eaebbecaa2c20bde22e1dbeeba8beb758c"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.79",
"syn 2.0.86",
]
[[package]]
name = "pin-project-lite"
version = "0.2.14"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02"
checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff"
[[package]]
name = "pin-utils"
@@ -3287,14 +3288,14 @@ dependencies = [
"proc-macro-error-attr2",
"proc-macro2",
"quote",
"syn 2.0.79",
"syn 2.0.86",
]
[[package]]
name = "proc-macro2"
version = "1.0.88"
version = "1.0.89"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7c3a7fc5db1e57d5a779a352c8cdb57b29aa4c40cc69c3a68a7fedc815fbf2f9"
checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e"
dependencies = [
"unicode-ident",
]
@@ -3307,7 +3308,7 @@ checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.79",
"syn 2.0.86",
"version_check",
"yansi",
]
@@ -3409,9 +3410,9 @@ dependencies = [
[[package]]
name = "regex"
version = "1.11.0"
version = "1.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38200e5ee88914975b69f657f0801b6f6dccafd44fd9326302a4aaeecfacb1d8"
checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
dependencies = [
"aho-corasick",
"memchr",
@@ -3616,9 +3617,9 @@ dependencies = [
[[package]]
name = "rustix"
version = "0.38.37"
version = "0.38.38"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811"
checksum = "aa260229e6538e52293eeb577aabd09945a09d6d9cc0fc550ed7529056c2e32a"
dependencies = [
"bitflags 2.6.0",
"errno",
@@ -3745,7 +3746,7 @@ dependencies = [
"proc-macro-error2",
"proc-macro2",
"quote",
"syn 2.0.79",
"syn 2.0.86",
]
[[package]]
@@ -3782,7 +3783,7 @@ dependencies = [
"proc-macro2",
"quote",
"sea-bae",
"syn 2.0.79",
"syn 2.0.86",
"unicode-ident",
]
@@ -3852,9 +3853,9 @@ checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b"
[[package]]
name = "serde"
version = "1.0.210"
version = "1.0.214"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a"
checksum = "f55c3193aca71c12ad7890f1785d2b73e1b9f63a0bbc353c08ef26fe03fc56b5"
dependencies = [
"serde_derive",
]
@@ -3882,20 +3883,20 @@ dependencies = [
[[package]]
name = "serde_derive"
version = "1.0.210"
version = "1.0.214"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f"
checksum = "de523f781f095e28fa605cdce0f8307e451cc0fd14e2eb4cd2e98a355b147766"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.79",
"syn 2.0.86",
]
[[package]]
name = "serde_json"
version = "1.0.128"
version = "1.0.132"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8"
checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03"
dependencies = [
"indexmap 2.6.0",
"itoa",
@@ -3946,7 +3947,7 @@ checksum = "91d129178576168c589c9ec973feedf7d3126c01ac2bf08795109aa35b69fb8f"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.79",
"syn 2.0.86",
]
[[package]]
@@ -4379,7 +4380,7 @@ dependencies = [
"proc-macro2",
"quote",
"rustversion",
"syn 2.0.79",
"syn 2.0.86",
]
[[package]]
@@ -4401,9 +4402,9 @@ dependencies = [
[[package]]
name = "syn"
version = "2.0.79"
version = "2.0.86"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590"
checksum = "e89275301d38033efb81a6e60e3497e734dfcc62571f2854bf4b16690398824c"
dependencies = [
"proc-macro2",
"quote",
@@ -4481,22 +4482,22 @@ dependencies = [
[[package]]
name = "thiserror"
version = "1.0.64"
version = "1.0.66"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84"
checksum = "5d171f59dbaa811dbbb1aee1e73db92ec2b122911a48e1390dfe327a821ddede"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.64"
version = "1.0.66"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3"
checksum = "b08be0f17bd307950653ce45db00cd31200d82b624b36e181337d9c7d92765b5"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.79",
"syn 2.0.86",
]
[[package]]
@@ -4557,9 +4558,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]]
name = "tokio"
version = "1.40.0"
version = "1.41.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2b070231665d27ad9ec9b8df639893f46727666c6767db40317fbe920a5d998"
checksum = "145f3413504347a2be84393cc8a7d2fb4d863b375909ea59f2158261aa258bbb"
dependencies = [
"backtrace",
"bytes",
@@ -4581,7 +4582,7 @@ checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.79",
"syn 2.0.86",
]
[[package]]
@@ -4683,9 +4684,9 @@ dependencies = [
[[package]]
name = "tracing-actix-web"
version = "0.7.13"
version = "0.7.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "15bc0cd5f72e837e310f4d978a90abf202a7f7d8ef3272246bae381d0086d3bf"
checksum = "6b87073920bcce23e9f5cb0d2671e9f01d6803bb5229c159b2f5ce6806d73ffc"
dependencies = [
"actix-web",
"mutually_exclusive_features",
@@ -4702,7 +4703,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.79",
"syn 2.0.86",
]
[[package]]
@@ -4781,12 +4782,9 @@ dependencies = [
[[package]]
name = "unicase"
version = "2.7.0"
version = "2.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89"
dependencies = [
"version_check",
]
checksum = "7e51b68083f157f853b6379db119d1c1be0e6e4dec98101079dec41f6f5cf6df"
[[package]]
name = "unicode-bidi"
@@ -5046,7 +5044,7 @@ dependencies = [
"once_cell",
"proc-macro2",
"quote",
"syn 2.0.79",
"syn 2.0.86",
"wasm-bindgen-shared",
]
@@ -5080,7 +5078,7 @@ checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.79",
"syn 2.0.86",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
@@ -5472,14 +5470,14 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.79",
"syn 2.0.86",
]
[[package]]
name = "zeroize"
version = "1.5.7"
version = "1.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c394b5bd0c6f669e7275d9c20aa90ae064cb22e75a1cad54e1b34088034b149f"
checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde"
dependencies = [
"zeroize_derive",
]
@@ -5492,7 +5490,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.79",
"syn 2.0.86",
]
[[package]]

View File

@@ -17,9 +17,5 @@ lto = true
[profile.release.package.lldap_app]
opt-level = 's'
[patch.crates-io.opaque-ke]
git = 'https://github.com/nitnelave/opaque-ke/'
branch = 'zeroize_1.5'
[patch.crates-io.lber]
git = 'https://github.com/inejge/ldap3/'

View File

@@ -156,6 +156,7 @@ services:
- LLDAP_JWT_SECRET=REPLACE_WITH_RANDOM
- LLDAP_KEY_SEED=REPLACE_WITH_RANDOM
- LLDAP_LDAP_BASE_DN=dc=example,dc=com
- LLDAP_LDAP_USER_PASS=adminPas$word
# If using LDAPS, set enabled true and configure cert and key path
# - LLDAP_LDAPS_OPTIONS__ENABLED=true
# - LLDAP_LDAPS_OPTIONS__CERT_FILE=/path/to/certfile.crt
@@ -198,32 +199,36 @@ Each package offers a [systemd service](https://wiki.archlinux.org/title/systemd
When using the distributed packages, the default login is `admin/password`. You can change that from the web UI after starting the service.
<details>
<summary><b>Arch</b></summary>
<summary><b>Arch Linux</b></summary>
<br>
Arch Linux offers unofficial support through the <a href="https://wiki.archlinux.org/title/Arch_User_Repository">Arch User Repository (AUR)</a>.<br>
The package descriptions can be used <a href="https://wiki.archlinux.org/title/Arch_User_Repository#Getting_started">to create and install packages</a>.<br><br>
Maintainer: <a href="https://github.com/Zepmann">@Zepmann</a><br>
Support: <a href="https://github.com/lldap/lldap/discussions">Discussions</a><br>
Package repository: <a href="https://aur.archlinux.org/packages">Arch user repository</a><br>
Support: <a href="https://github.com/lldap/lldap/discussions/1044">Discussions</a><br>
Package repository: <a href="https://aur.archlinux.org/packages">Arch User Repository</a><br><br>
<table>
<tr>
<td>Available packages:</td>
<td>Package name</td>
<td>Maintainer</td>
<td>Description</td>
</tr>
<tr>
<td><a href="https://aur.archlinux.org/packages/lldap">lldap</a></td>
<td><a href="https://github.com/Zepmann">@Zepmann</a></td>
<td>Builds the latest stable version.</td>
</tr>
<tr>
<td></td>
<td><a href="https://aur.archlinux.org/packages/lldap-bin">lldap-bin</a></td>
<td>Uses the latest pre-compiled binaries from the <a href="https://aur.archlinux.org/packages/lldap-bin">releases in this repository</a>.<br>
This package is recommended if you want to run lldap on a system with limited resources.</td>
<td><a href="https://github.com/Zepmann">@Zepmann</a></td>
<td>Uses the latest pre-compiled binaries from the <a href="https://github.com/lldap/lldap/releases">releases in this repository</a>.<br>
This package is recommended if you want to run LLDAP on a system with limited resources.</td>
</tr>
<tr>
<td></td>
<td><a href="https://aur.archlinux.org/packages/lldap-git">lldap-git</a></td>
<td></td>
<td>Builds the latest main branch code.</td>
</tr>
</table>
LLDPA configuration file: /etc/lldap.toml<br>
LLDAP configuration file: /etc/lldap.toml<br>
</details>
<details>
<summary><b>Debian</b></summary>
@@ -491,6 +496,9 @@ for some service integrations.
The [bootstrap.sh](scripts/bootstrap.sh) script can enforce a list of
users/groups/attributes from a given file, reflecting it on the server.
To manage the user, group and membership lifecycle in an infrastructure-as-code
scenario you can use the unofficial [LLDAP terraform provider in the terraform registry](https://registry.terraform.io/providers/tasansga/lldap/latest).
LLDAP is also very scriptable, through its GraphQL API. See the
[Scripting](docs/scripting.md) docs for more info.
@@ -584,6 +592,7 @@ folder for help with:
- [Grafana](example_configs/grafana_ldap_config.toml)
- [Grocy](example_configs/grocy.md)
- [Harbor](example_configs/harbor.md)
- [HashiCorp Vault](example_configs/hashicorp-vault.md)
- [Hedgedoc](example_configs/hedgedoc.md)
- [Home Assistant](example_configs/home-assistant.md)
- [Jellyfin](example_configs/jellyfin.md)
@@ -591,6 +600,7 @@ folder for help with:
- [Jitsi Meet](example_configs/jitsi_meet.conf)
- [Kasm](example_configs/kasm.md)
- [KeyCloak](example_configs/keycloak.md)
- [Kimai](example_configs/kimai.yaml)
- [LibreNMS](example_configs/librenms.md)
- [Maddy](example_configs/maddy.md)
- [Mastodon](example_configs/mastodon.env.example)
@@ -603,6 +613,7 @@ folder for help with:
- [Nextcloud](example_configs/nextcloud.md)
- [Nexus](example_configs/nexus.md)
- [OCIS (OwnCloud Infinite Scale)](example_configs/ocis.md)
- [OneDev](example_configs/onedev.md)
- [Organizr](example_configs/Organizr.md)
- [Portainer](example_configs/portainer.md)
- [PowerDNS Admin](example_configs/powerdns_admin.md)
@@ -703,6 +714,9 @@ modern identity protocols, check out Kanidm.
If you just set up the server, can get to the login page but the password you
set isn't working, try the following:
- If you have changed the admin password in the config after the first run, it
won't be used (unless you force its use with `force_ldap_user_pass_reset`).
The config password is only for the initial admin creation.
- (For docker): Make sure that the `/data` folder is persistent, either to a
docker volume or mounted from the host filesystem.
- Check if there is a `lldap_config.toml` file (either in `/data` for docker

View File

@@ -6,7 +6,7 @@ homepage = "https://github.com/lldap/lldap"
license = "GPL-3.0-only"
name = "lldap_app"
repository = "https://github.com/lldap/lldap"
version = "0.6.0"
version = "0.6.2-alpha"
include = ["src/**/*", "queries/**/*", "Cargo.toml", "../schema.graphql"]
[dependencies]

View File

@@ -31,14 +31,14 @@ default-features = false
version = "1"
[dependencies.opaque-ke]
version = "0.6"
version = "0.7"
[dependencies.chrono]
version = "*"
features = [ "serde" ]
features = ["serde"]
[dependencies.sea-orm]
version= "0.12"
version = "0.12"
default-features = false
features = ["macros"]
optional = true

View File

@@ -14,15 +14,14 @@ Backend:
is defined in `schema.graphql`.
* The static frontend files are served by this port too.
Note that secure protocols (LDAPS, HTTPS) are currently not supported. This can
be worked around by using a reverse proxy in front of the server (for the HTTP
API) that wraps/unwraps the HTTPS messages, or only open the service to
localhost or other trusted docker containers (for the LDAP API).
Note that HTTPS is currently not supported. This can be worked around by using
a reverse proxy in front of the server (for the HTTP API) that wraps/unwraps
the HTTPS messages. LDAPS is supported.
Frontend:
* User management UI.
* Written in Rust compiled to WASM as an SPA with the Yew library.
* Based on components, with a React-like organization.
* Based on components, with a React-like framework.
Data storage:
* The data (users, groups, memberships, active JWTs, ...) is stored in SQL.
@@ -50,19 +49,19 @@ Data storage:
Authentication is done via the OPAQUE protocol, meaning that the passwords are
never sent to the server, but instead the client proves that they know the
correct password (zero-knowledge proof). This is likely overkill, especially
considered that the LDAP interface requires sending the password to the server,
but it's one less potential flaw (especially since the LDAP interface can be
restricted to an internal docker-only network while the web app is exposed to
the Internet).
considered that the LDAP interface requires sending the password in cleartext
to the server, but it's one less potential flaw (especially since the LDAP
interface can be restricted to an internal docker-only network while the web
app is exposed to the Internet).
OPAQUE's "passwords" (user-specific blobs of data that can only be used in a
zero-knowledge proof that the password is correct) are hashed using Argon2, the
state of the art in terms of password storage. They are hashed using a secret
provided in the configuration (which can be given as environment variable or
command line argument as well): this should be kept secret and shouldn't change
(it would invalidate all passwords). Note that even if it was compromised, the
attacker wouldn't be able to decrypt the passwords without running an expensive
brute-force search independently for each password.
provided in the configuration (which can be given as environment variable,
command line argument or a file as well): this should be kept secret and
shouldn't change (it would invalidate all passwords). Note that even if it was
compromised, the attacker wouldn't be able to decrypt the passwords without
running an expensive brute-force search independently for each password.
### JWTs and refresh tokens

View File

@@ -0,0 +1,77 @@
# Configuration for HashiCorp Vault
Official LDAP configuration documentation is located [here](https://developer.hashicorp.com/vault/docs/auth/ldap).
**You'll need to authenticate using your root token or as a user who has permission to modify authentication methods!**
## User Interface
1. Navigate to `Access -> Authentication Methods`
2. Click `Enable new method +` in the top right and choose `LDAP` under `Infra`
3. Name the path whatever you want (preferably keep it default) and click `Enable method` at the bottom
* URL: `ldap://lldap.example.com:3890` or `ldaps://lldap.example.com:6360`
* LDAP Options
* If you're using LDAPS and your server does not have your LDAPS certificate installed check `Insecure TLS` otherwise leave this unchecked
* User Attribute: `uid`
* User Principal (UPN) Domain: **LEAVE THIS BLANK**
* Customize User Search
* Name of Object to bind (binddn): `cn=admin,ou=people,dc=example,dc=com`
* User DN: `ou=people,dc=example,dc=com`
* Bindpass: `ChangeMe!`
* User Search Filter: `(&(uid={{.Username}})(objectClass=person))`
* Customize Group Member Search
* Group Filter: `(&(member={{.UserDN}})(objectclass=groupOfUniqueNames))`
* Group Attribute: `cn`
* Group DN: `ou=groups,dc=example,dc=com`
4. Click `Save` at the bottom
5. Click into the auth menthod and then `Create group +` under the `Groups` tab
6. Set the name as the group you want users to have to authenticate to HashiCorp Vault
7. Set policy as `default` or whatever policy you want to tie to this group
8. Click `Save` at the bottom
As long as your user is in the group you specified, you should now be able to select `LDAP` from the dropdown on the login page and use your credentials.
## CLI
**This requires the vault CLI to be installed on your machine**
1. Set VAULT_ADDR environment variable
```bash
export VAULT_ADDR=https://vault.example.com
```
2. Login to vault and provide token when prompted
```bash
vault login
````
3. Enable the LDAP authentication method
```bash
vault auth enable ldap
```
4. Configure the LDAP authentication method
```bash
vault write auth/ldap/config \
url="ldaps://lldaps.example.com:6360" \
binddn="cn=admin,ou=people,dc=example,dc=com" \
bindpass="ChangeMe!" \
userdn="ou=people,dc=example,dc=com" \
userfilter="(&(uid={{.Username}})(objectClass=person))" \
groupdn="ou=groups,dc=example,dc=com" \
groupfilter="(&(member={{.UserDN}})(objectclass=groupOfUniqueNames))" \
userattr="uid" \
groupattr="cn" \
discoverdn=false
```
If you are using plain LDAP, change the URL accordingly. If you're using LDAPS and your server does not have your LDAPS certificate installed append `insecure_tls=true` to the bottom of the command.
5. Add your group to the LDAP configuration and set the policy
```bash
vault write auth/ldap/groups/vault_users policies=default
```
As long as your user is in the group you specified, you should now be able to select `LDAP` from the dropdown on the login page and use your credentials.

View File

@@ -0,0 +1,36 @@
# See https://www.kimai.org/documentation/local-yaml.html
# this file should be renamed local.yaml
kimai:
ldap:
activate: true
connection:
host: lldap
port: 3890 # 6360 for LDAPS
useSsl: false # true for LDAPS
useStartTls: false
username: uid=admin,ou=people,dc=example,dc=com
password: <PASSWORD_HERE>
accountFilterFormat: (&(objectClass=person)(uid=%s))
bindRequiresDn: true
optReferrals: false
user:
baseDn: ou=people, dc=example, dc=com
usernameAttribute: uid
filter: (&(objectClass=person)) # to filter by group, add (memberof=cn=kimai,ou=groups,dc=example,dc=com) [group 'kimai' must be created in web UI]
attributes:
- { ldap_attr: "uid", user_method: setUserIdentifier }
- { ldap_attr: "mail", user_method: setEmail }
- { ldap_attr: "cn", user_method: setAlias }
role:
baseDn: ou=groups, dc=example, dc=com
filter: (&(objectClass=groupOfUniqueNames))
usernameAttribute: cn
nameAttribute: cn
userDnAttribute: member
# Convert LDAP group name (nameAttribute) to Kimai role. Available roles are listed here: https://www.kimai.org/documentation/permissions.html
groups:
- { ldap_value: lldap_admin, role: ROLE_SUPER_ADMIN }
# add additional group mappings here

23
example_configs/onedev.md Normal file
View File

@@ -0,0 +1,23 @@
# Configuration for OneDev
In Onedev, go to `Administration > External Authentication Source > Authenticator` and Select `Generic LDAP`
* LDAP URL: ldap://lldap_ip_or_hostname:3890 or ldaps://lldap_ip_or_hostname:6360
* Authentication Required: On
* Manager DN: `uid=admin,ou=people,dc=example,dc=com`
* Manager Password: Your bind user's password
* User Search Base: `ou=people,dc=example,dc=com`
* User Search Filter: `(&(uid={0})(objectclass=person))`
* User Full Name Attribute: `displayName`
* Email Attribute: mail
* User SSH Key Attribute: (Leave Blank)
* Group Retrieval: "Search Groups Using Filter"
* Group Search Base: `ou=groups,dc=example,dc=com`
* Group Search Filter: `(&(uniqueMember={0})(objectclass=groupOfUniqueNames))`
* Group Name Attribute: cn
* Create User As Guest: Off
* Default Group: "No Default Group"
* Timeout: 300
Replace every instance of `dc=example,dc=com` with your configured domain.
After applying the above settings, users should be able to log in with their user name.

View File

@@ -1,7 +1,3 @@
> [!IMPORTANT]
> The integration requires custom ldap properties which are not supported on
> stable (as of 2024-09-19), please use nightly/latest tag.
# Configure lldap
You MUST use LDAPS. You MUST NOT use plain ldap. Even over a private network
@@ -46,11 +42,6 @@ The provided implementation uses custom attributes to mark users and groups
that should be included in the system (for instance, you don't want LDAP
accounts of other services to have a matching unix user).
> [!TIP]
> You can create custom attributes in the Web UI, but to provide values, you
> need to communicate with the API, see [scripting]. Example using lldap-cli:
> `./lldap-cli user update set example-user unix-uid 5000`
For users, you need to add an (integer) `unix-uid` attribute to the schema, and
manually set the value for the users you want to enable to login with PAM.

View File

@@ -8,7 +8,7 @@ keywords = ["cli", "ldap", "graphql", "server", "authentication"]
license = "GPL-3.0-only"
name = "lldap"
repository = "https://github.com/lldap/lldap"
version = "0.6.0"
version = "0.6.2-alpha"
[dependencies]
actix = "0.13"
@@ -86,7 +86,7 @@ path = "../auth"
features = ["opaque_server", "opaque_client", "sea_orm"]
[dependencies.opaque-ke]
version = "0.6"
version = "0.7"
[dependencies.rand]
features = ["small_rng", "getrandom"]
@@ -122,9 +122,15 @@ default-features = false
version = "0.24"
[dependencies.sea-orm]
version= "0.12"
version = "0.12"
default-features = false
features = ["macros", "with-chrono", "with-uuid", "sqlx-all", "runtime-actix-rustls"]
features = [
"macros",
"with-chrono",
"with-uuid",
"sqlx-all",
"runtime-actix-rustls",
]
[dependencies.reqwest]
version = "0.11"

View File

@@ -5,8 +5,8 @@ use crate::domain::{
use itertools::Itertools;
use sea_orm::{
sea_query::{
self, all, Alias, BinOper, BlobSize::Blob, ColumnDef, Expr, ForeignKey, ForeignKeyAction,
Func, Index, Query, SimpleExpr, Table, Value,
self, all, BinOper, BlobSize::Blob, ColumnDef, Expr, ForeignKey, ForeignKeyAction, Func,
Index, Query, SimpleExpr, Table, Value,
},
ConnectionTrait, DatabaseTransaction, DbErr, DeriveIden, FromQueryResult, Iden, Order,
Statement, TransactionTrait,
@@ -970,21 +970,15 @@ async fn migrate_to_v8(transaction: DatabaseTransaction) -> Result<DatabaseTrans
let builder = transaction.get_database_backend();
// Remove duplicate memberships.
#[derive(FromQueryResult)]
#[allow(dead_code)]
struct MembershipInfo {
user_id: UserId,
group_id: GroupId,
cnt: i64,
}
let mut delete_queries = MembershipInfo::find_by_statement(
for MembershipInfo { user_id, group_id } in MembershipInfo::find_by_statement(
builder.build(
Query::select()
.from(Memberships::Table)
.columns([Memberships::UserId, Memberships::GroupId])
.expr_as(
Expr::count(Expr::col((Memberships::Table, Memberships::UserId))),
Alias::new("cnt"),
)
.group_by_columns([Memberships::UserId, Memberships::GroupId])
.cond_having(all![SimpleExpr::Binary(
Box::new(Expr::col((Memberships::Table, Memberships::UserId)).count()),
@@ -996,38 +990,29 @@ async fn migrate_to_v8(transaction: DatabaseTransaction) -> Result<DatabaseTrans
.all(&transaction)
.await?
.into_iter()
.map(
|MembershipInfo {
user_id,
group_id,
cnt,
}| {
builder
.build(
{
transaction
.execute(
builder.build(
Query::delete()
.from_table(Memberships::Table)
.cond_where(all![
Expr::col(Memberships::UserId).eq(user_id),
Expr::col(Memberships::UserId).eq(&user_id),
Expr::col(Memberships::GroupId).eq(group_id)
])
.limit(cnt as u64 - 1),
)
.to_owned()
},
)
.peekable();
if delete_queries.peek().is_some() {
match transaction.get_database_backend() {
sea_orm::DatabaseBackend::Sqlite => {
return Err(DbErr::Migration(format!(
"The Sqlite driver does not support LIMIT in DELETE. Run these queries manually:\n{}" , delete_queries.map(|s| s.to_string()).join("\n"))));
}
sea_orm::DatabaseBackend::MySql | sea_orm::DatabaseBackend::Postgres => {
for query in delete_queries {
transaction.execute(query).await?;
}
}
}
]),
),
)
.await?;
transaction
.execute(
builder.build(
Query::insert()
.into_table(Memberships::Table)
.columns([Memberships::UserId, Memberships::GroupId])
.values_panic([user_id.into(), group_id.into()]),
),
)
.await?;
}
transaction
.execute(

View File

@@ -314,8 +314,8 @@ pub struct UserRestrictedListerBackendHandler<'a, Handler> {
}
#[async_trait]
impl<'a, Handler: ReadSchemaBackendHandler + Sync> ReadSchemaBackendHandler
for UserRestrictedListerBackendHandler<'a, Handler>
impl<Handler: ReadSchemaBackendHandler + Sync> ReadSchemaBackendHandler
for UserRestrictedListerBackendHandler<'_, Handler>
{
async fn get_schema(&self) -> Result<Schema> {
let mut schema = self.handler.get_schema().await?;
@@ -331,8 +331,8 @@ impl<'a, Handler: ReadSchemaBackendHandler + Sync> ReadSchemaBackendHandler
}
#[async_trait]
impl<'a, Handler: UserListerBackendHandler + Sync> UserListerBackendHandler
for UserRestrictedListerBackendHandler<'a, Handler>
impl<Handler: UserListerBackendHandler + Sync> UserListerBackendHandler
for UserRestrictedListerBackendHandler<'_, Handler>
{
async fn list_users(
&self,
@@ -354,8 +354,8 @@ impl<'a, Handler: UserListerBackendHandler + Sync> UserListerBackendHandler
}
#[async_trait]
impl<'a, Handler: GroupListerBackendHandler + Sync> GroupListerBackendHandler
for UserRestrictedListerBackendHandler<'a, Handler>
impl<Handler: GroupListerBackendHandler + Sync> GroupListerBackendHandler
for UserRestrictedListerBackendHandler<'_, Handler>
{
async fn list_groups(&self, filters: Option<GroupRequestFilter>) -> Result<Vec<Group>> {
let group_filter = self
@@ -379,7 +379,7 @@ pub trait UserAndGroupListerBackendHandler:
}
#[async_trait]
impl<'a, Handler: GroupListerBackendHandler + UserListerBackendHandler + Sync>
UserAndGroupListerBackendHandler for UserRestrictedListerBackendHandler<'a, Handler>
impl<Handler: GroupListerBackendHandler + UserListerBackendHandler + Sync>
UserAndGroupListerBackendHandler for UserRestrictedListerBackendHandler<'_, Handler>
{
}

View File

@@ -37,7 +37,7 @@ impl<'de> Deserialize<'de> for TrueFalseAlways {
struct Visitor;
impl<'de> serde::de::Visitor<'de> for Visitor {
impl serde::de::Visitor<'_> for Visitor {
type Value = TrueFalseAlways;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {

View File

@@ -97,16 +97,16 @@ pub struct Configuration {
pub http_host: String,
#[builder(default = "17170")]
pub http_port: u16,
#[builder(default = r#"SecUtf8::from("secretjwtsecret")"#)]
pub jwt_secret: SecUtf8,
#[builder(default)]
pub jwt_secret: Option<SecUtf8>,
#[builder(default = r#"String::from("dc=example,dc=com")"#)]
pub ldap_base_dn: String,
#[builder(default = r#"UserId::new("admin")"#)]
pub ldap_user_dn: UserId,
#[builder(default)]
pub ldap_user_email: String,
#[builder(default = r#"SecUtf8::from("password")"#)]
pub ldap_user_pass: SecUtf8,
#[builder(default)]
pub ldap_user_pass: Option<SecUtf8>,
#[builder(default)]
pub force_ldap_user_pass_reset: TrueFalseAlways,
#[builder(default = "false")]
@@ -361,7 +361,7 @@ fn get_server_setup<L: Into<PrivateKeyLocationOrFigment>>(
file_path
);
} else if file_path == "server_key" {
eprintln!("WARNING: A key_seed was given, we will ignore the server_key and generate one from the seed! Set server_key to an empty string in the config to silence this message.");
eprintln!("WARNING: A key_seed was given, we will ignore the key_file and generate one from the seed! Set key_file to an empty string in the config to silence this message.");
} else {
println!("Generating the private key from the key_seed");
}
@@ -607,11 +607,24 @@ where
.unwrap_or_default(),
figment_config,
)?);
if config.jwt_secret == SecUtf8::from("secretjwtsecret") {
println!("WARNING: Default JWT secret used! This is highly unsafe and can allow attackers to log in as admin.");
}
if config.ldap_user_pass == SecUtf8::from("password") {
println!("WARNING: Unsecure default admin password is used.");
if config.jwt_secret.is_none() {
use rand::{seq::SliceRandom, Rng};
struct Symbols;
impl rand::prelude::Distribution<char> for Symbols {
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> char {
*b"01234567890ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz+,-./:;<=>?_~!@#$%^&*()[]{}:;".choose(rng).unwrap() as char
}
}
bail!("The JWT secret must be initialized to a random string, preferably at least 32 characters long. \
Either set the `jwt_secret` config value or the `LLDAP_JWT_SECRET` environment variable. \
You can generate the value by running\n\
LC_ALL=C tr -dc 'A-Za-z0-9!#%&'\\''()*+,-./:;<=>?@[\\]^_{{|}}~' </dev/urandom | head -c 32; echo ''\n\
or you can use this random value: {}",
rand::thread_rng()
.sample_iter(&Symbols)
.take(32)
.collect::<String>());
}
if config.smtp_options.tls_required.is_some() {
println!("DEPRECATED: smtp_options.tls_required field is deprecated, it never did anything. You can replace it with smtp_options.smtp_encryption.");
@@ -669,7 +682,9 @@ mod tests {
fn figment_location_extraction_key_file() {
Jail::expect_with(|jail| {
jail.create_file("lldap_config.toml", r#"key_file = "test""#)?;
jail.clear_env();
jail.set_env("LLDAP_KEY_SEED", "a123");
jail.set_env("LLDAP_JWT_SECRET", "secret");
let ignore_keys = ["key_file", "cert_file"];
let figment_config = Figment::from(Serialized::defaults(
ConfigurationBuilder::default().private_build().unwrap(),
@@ -696,7 +711,9 @@ mod tests {
fn check_server_setup_key_extraction_seed_success_with_nonexistant_file() {
Jail::expect_with(|jail| {
jail.create_file("lldap_config.toml", r#"key_file = "test""#)?;
jail.clear_env();
jail.set_env("LLDAP_KEY_SEED", "a123");
jail.set_env("LLDAP_JWT_SECRET", "secret");
init(default_run_opts()).unwrap();
Ok(())
});
@@ -706,7 +723,9 @@ mod tests {
fn check_server_setup_key_extraction_seed_failure_with_existing_file() {
Jail::expect_with(|jail| {
jail.create_file("lldap_config.toml", r#"key_file = "test""#)?;
jail.clear_env();
jail.set_env("LLDAP_KEY_SEED", "a123");
jail.set_env("LLDAP_JWT_SECRET", "secret");
write_random_key(jail, "test");
init(default_run_opts()).unwrap_err();
Ok(())
@@ -717,6 +736,8 @@ mod tests {
fn check_server_setup_key_extraction_file_success_with_existing_file() {
Jail::expect_with(|jail| {
jail.create_file("lldap_config.toml", r#"key_file = "test""#)?;
jail.clear_env();
jail.set_env("LLDAP_JWT_SECRET", "secret");
write_random_key(jail, "test");
init(default_run_opts()).unwrap();
Ok(())
@@ -727,6 +748,8 @@ mod tests {
fn check_server_setup_key_extraction_file_success_with_nonexistent_file() {
Jail::expect_with(|jail| {
jail.create_file("lldap_config.toml", r#"key_file = "test""#)?;
jail.clear_env();
jail.set_env("LLDAP_JWT_SECRET", "secret");
init(default_run_opts()).unwrap();
Ok(())
});
@@ -736,6 +759,8 @@ mod tests {
fn check_server_setup_key_extraction_file_with_previous_different_file() {
Jail::expect_with(|jail| {
jail.create_file("lldap_config.toml", r#"key_file = "test""#)?;
jail.clear_env();
jail.set_env("LLDAP_JWT_SECRET", "secret");
write_random_key(jail, "test");
let config = init(default_run_opts()).unwrap();
let info = config.get_private_key_info();
@@ -766,6 +791,8 @@ mod tests {
#[test]
fn check_server_setup_key_extraction_file_to_seed() {
Jail::expect_with(|jail| {
jail.clear_env();
jail.set_env("LLDAP_JWT_SECRET", "secret");
jail.create_file("lldap_config.toml", "")?;
write_random_key(jail, "server_key");
init(default_run_opts()).unwrap();
@@ -782,6 +809,8 @@ mod tests {
#[test]
fn check_server_setup_key_extraction_file_to_seed_removed_file() {
Jail::expect_with(|jail| {
jail.clear_env();
jail.set_env("LLDAP_JWT_SECRET", "secret");
jail.create_file("lldap_config.toml", "")?;
write_random_key(jail, "server_key");
let config = init(default_run_opts()).unwrap();

View File

@@ -189,7 +189,7 @@ pub async fn build_tcp_server<Backend>(
where
Backend: TcpBackendHandler + BackendHandler + LoginHandler + OpaqueHandler + Clone + 'static,
{
let jwt_secret = config.jwt_secret.clone();
let jwt_secret = config.jwt_secret.clone().unwrap();
let jwt_blacklist = backend_handler
.get_jwt_blacklist()
.await

View File

@@ -33,8 +33,17 @@ use tracing::{debug, error, info, instrument, span, warn, Instrument, Level};
mod domain;
mod infra;
const ADMIN_PASSWORD_MISSING_ERROR : &str = "The LDAP admin password must be initialized. \
Either set the `ldap_user_pass` config value or the `LLDAP_LDAP_USER_PASS` environment variable. \
A minimum of 8 characters is recommended.";
async fn create_admin_user(handler: &SqlBackendHandler, config: &Configuration) -> Result<()> {
let pass_length = config.ldap_user_pass.unsecure().len();
let pass_length = config
.ldap_user_pass
.as_ref()
.expect(ADMIN_PASSWORD_MISSING_ERROR)
.unsecure()
.len();
assert!(
pass_length >= 8,
"Minimum password length is 8 characters, got {} characters",
@@ -48,7 +57,11 @@ async fn create_admin_user(handler: &SqlBackendHandler, config: &Configuration)
..Default::default()
})
.and_then(|_| {
register_password(handler, config.ldap_user_dn.clone(), &config.ldap_user_pass)
register_password(
handler,
config.ldap_user_dn.clone(),
config.ldap_user_pass.as_ref().unwrap(),
)
})
.await
.context("Error creating admin user")?;
@@ -161,7 +174,10 @@ async fn set_up_server(config: Configuration) -> Result<ServerBuilder> {
register_password(
&backend_handler,
config.ldap_user_dn.clone(),
&config.ldap_user_pass,
config
.ldap_user_pass
.as_ref()
.expect(ADMIN_PASSWORD_MISSING_ERROR),
)
.instrument(span)
.await

View File

@@ -3,6 +3,8 @@ use std::env::var;
pub const DB_KEY: &str = "LLDAP_DATABASE_URL";
pub const PRIVATE_KEY_SEED: &str = "LLDAP_KEY_SEED";
pub const JWT_SECRET: &str = "LLDAP_JWT_SECRET";
pub const LDAP_USER_PASSWORD: &str = "LLDAP_LDAP_USER_PASS";
pub fn database_url() -> String {
let url = var(DB_KEY).ok();

View File

@@ -43,14 +43,13 @@ const MAX_HEALTHCHECK_ATTEMPS: u8 = 10;
impl LLDAPFixture {
pub fn new() -> Self {
let mut cmd = create_lldap_command();
cmd.arg("run");
cmd.arg("--verbose");
let child = cmd.spawn().expect("Unable to start server");
let child = create_lldap_command("run")
.arg("--verbose")
.spawn()
.expect("Unable to start server");
let mut started = false;
for _ in 0..MAX_HEALTHCHECK_ATTEMPS {
let status = create_lldap_command()
.arg("healthcheck")
let status = create_lldap_command("healthcheck")
.status()
.expect("healthcheck fail");
if status.success() {
@@ -229,7 +228,7 @@ pub fn new_id(prefix: Option<&str>) -> String {
}
}
fn create_lldap_command() -> Command {
fn create_lldap_command(subcommand: &str) -> Command {
let mut cmd = Command::cargo_bin(env!("CARGO_PKG_NAME")).expect("cargo bin not found");
// This gives us the absolute path of the repo base instead of running it in server/
let path = canonicalize("..").expect("canonical path");
@@ -237,5 +236,10 @@ fn create_lldap_command() -> Command {
cmd.current_dir(path);
cmd.env(env::DB_KEY, db_url);
cmd.env(env::PRIVATE_KEY_SEED, "Random value");
cmd.env(env::JWT_SECRET, "Random value");
cmd.env(env::LDAP_USER_PASSWORD, "password");
cmd.arg(subcommand);
cmd.arg("--config-file=/dev/null");
cmd.arg("--server-key-file=''");
cmd
}