28 Commits

Author SHA1 Message Date
Dedy Martadinata S
1b2dfbe52e Publish job using matrix
Change publish job with matrix, don't fail if another job fail.
2023-05-12 20:41:35 +07:00
Valentin Tolmer
e1aa2bfb18 cargo: depend on the published version of lldap_auth 2023-05-12 15:37:39 +02:00
Valentin Tolmer
1377d5aed9 github: create actions to publish crates 2023-05-12 15:25:59 +02:00
Valentin Tolmer
fa0185af5e cargo: set specific versions for each dependency 2023-05-12 15:25:59 +02:00
nitnelave
d720a7812a cargo: set metadata for publishing crates (#577) 2023-05-12 18:06:06 +07:00
nitnelave
d2dec56cca readme: add buymeacoffee link 2023-05-12 16:21:09 +09:00
poVoq
ab2da7b975 example_configs: Add Ejabberd
Basic auth only for now
2023-05-09 14:31:54 +02:00
Anudeep
8f69e4badd example_configs: add chmod to home-assistant instructions 2023-05-06 07:08:41 +02:00
Valentin Tolmer
5bd00f24a2 docker: ignore more files 2023-05-02 16:15:54 +02:00
Valentin Tolmer
ab9ee8d962 tests: allow dead code in common module
We're running afoul of https://github.com/rust-lang/rust/issues/46379,
where each test is compiled independently, so any test that doesn't use
every helper method triggers a dead code warning.
2023-05-02 16:15:54 +02:00
lordratner
852e1586e7 example_configs: Fix a filter in Grafana 2023-05-02 15:45:04 +02:00
Herwig Hochleitner
23b388f3b8 docs: correct env var names in docker template toml 2023-05-02 15:40:16 +02:00
Hobbabobba
22ae2c7124 example_configs: fix zendto memberrole 2023-04-25 20:53:38 +02:00
lordratner
5ad63d31d3 example_docs: add pfsense.md 2023-04-20 18:10:14 +02:00
Tyler Pace
d55d4487ed Add OPNsense example config. (#558)
* Add OPNsense example config.
---------

Co-authored-by: Tyler Pace <tpace@newrelic.com>
2023-04-15 10:06:34 +07:00
Austin Alvarado
4283d27da6 server: Initial stab at e2e tests (#534)
Initial end to end testing. generates unique names for user and groups, and all tests run serially
2023-04-14 11:45:15 -06:00
Valentin Tolmer
4576cf9f2c bump: bump the version to 0.5-alpha, since we have a breaking change 2023-04-14 17:36:04 +02:00
Valentin Tolmer
d1d5d38b32 server: fix incorrect logging 2023-04-14 17:02:00 +02:00
Valentin Tolmer
e5ce98c874 server: Improve the error message in case of duplicate emails 2023-04-14 17:02:00 +02:00
Valentin Tolmer
96b7dbb1c5 server: Make key_seed a secret value 2023-04-14 00:07:54 +02:00
dependabot[bot]
9408b12bc7 build(deps): bump actions/checkout from 3.5.1 to 3.5.2
Bumps [actions/checkout](https://github.com/actions/checkout) from 3.5.1 to 3.5.2.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v3.5.1...v3.5.2)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-04-13 23:18:22 +02:00
Valentin Tolmer
4e85a4718f server: enforce email and uuid unicity 2023-04-13 17:51:49 +02:00
Valentin Tolmer
d1f1eb8e80 config: Explicit the env variables 2023-04-13 09:22:09 +02:00
Valentin Tolmer
da364746c4 server: Derive the server key from a seed
Fixes #504.
2023-04-13 09:17:05 +02:00
dependabot[bot]
d672f68049 build(deps): bump actions/checkout from 3.5.0 to 3.5.1
Bumps [actions/checkout](https://github.com/actions/checkout) from 3.5.0 to 3.5.1.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v3.5.0...v3.5.1)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-04-13 09:03:27 +02:00
Valentin Tolmer
dcca768b6c github: Add more folders to ignore for coverage 2023-04-11 17:17:28 +02:00
Valentin Tolmer
ea69b4bead version: bump to 0.4.4-alpha 2023-04-11 17:17:28 +02:00
Valentin Tolmer
7b4188a376 cargo: update cargo.lock 2023-04-11 17:17:28 +02:00
43 changed files with 1781 additions and 205 deletions

View File

@@ -34,12 +34,20 @@ package.json
.vscode
.devcontainer
# Created databases
*.db
*.db-shm
*.db-wal
# These are backup files generated by rustfmt
**/*.rs.bk
# Various config files that shouldn't be tracked
.env
lldap_config.toml
server_key
users.db*
screenshot.png
recipe.json
lldap_config.toml
cert.pem
key.pem

2
.github/codecov.yml vendored
View File

@@ -10,3 +10,5 @@ ignore:
- "docs"
- "example_configs"
- "migration-tool"
- "scripts"
- "set-password"

View File

@@ -11,10 +11,10 @@ RUN mkdir -p /lldap/app
RUN if [ "${TARGETPLATFORM}" = "linux/amd64" ]; then \
mv bin/x86_64-unknown-linux-musl-lldap-bin/lldap target/lldap && \
mv bin/x86_64-unknown-linux-musl-migration-tool-bin/migration-tool target/migration-tool && \
mv bin/x86_64-unknown-linux-musl-lldap_migration_tool-bin/lldap_migration_tool target/lldap_migration_tool && \
mv bin/x86_64-unknown-linux-musl-lldap_set_password-bin/lldap_set_password target/lldap_set_password && \
chmod +x target/lldap && \
chmod +x target/migration-tool && \
chmod +x target/lldap_migration_tool && \
chmod +x target/lldap_set_password && \
ls -la target/ . && \
pwd \
@@ -22,10 +22,10 @@ RUN if [ "${TARGETPLATFORM}" = "linux/amd64" ]; then \
RUN if [ "${TARGETPLATFORM}" = "linux/arm64" ]; then \
mv bin/aarch64-unknown-linux-musl-lldap-bin/lldap target/lldap && \
mv bin/aarch64-unknown-linux-musl-migration-tool-bin/migration-tool target/migration-tool && \
mv bin/aarch64-unknown-linux-musl-lldap_migration_tool-bin/lldap_migration_tool target/lldap_migration_tool && \
mv bin/aarch64-unknown-linux-musl-lldap_set_password-bin/lldap_set_password target/lldap_set_password && \
chmod +x target/lldap && \
chmod +x target/migration-tool && \
chmod +x target/lldap_migration_tool && \
chmod +x target/lldap_set_password && \
ls -la target/ . && \
pwd \
@@ -33,10 +33,10 @@ RUN if [ "${TARGETPLATFORM}" = "linux/arm64" ]; then \
RUN if [ "${TARGETPLATFORM}" = "linux/arm/v7" ]; then \
mv bin/armv7-unknown-linux-gnueabihf-lldap-bin/lldap target/lldap && \
mv bin/armv7-unknown-linux-gnueabihf-migration-tool-bin/migration-tool target/migration-tool && \
mv bin/armv7-unknown-linux-gnueabihf-lldap_migration_tool-bin/lldap_migration_tool target/lldap_migration_tool && \
mv bin/armv7-unknown-linux-gnueabihf-lldap_set_password-bin/lldap_set_password target/lldap_set_password && \
chmod +x target/lldap && \
chmod +x target/migration-tool && \
chmod +x target/lldap_migration_tool && \
chmod +x target/lldap_set_password && \
ls -la target/ . && \
pwd \
@@ -47,7 +47,7 @@ COPY docker-entrypoint.sh /docker-entrypoint.sh
COPY lldap_config.docker_template.toml /lldap/
COPY web/index_local.html web/index.html
RUN cp target/lldap /lldap/ && \
cp target/migration-tool /lldap/ && \
cp target/lldap_migration_tool /lldap/ && \
cp target/lldap_set_password /lldap/ && \
cp -R web/index.html \
web/pkg \

View File

@@ -11,10 +11,10 @@ RUN mkdir -p /lldap/app
RUN if [ "${TARGETPLATFORM}" = "linux/amd64" ]; then \
mv bin/x86_64-unknown-linux-musl-lldap-bin/lldap target/lldap && \
mv bin/x86_64-unknown-linux-musl-migration-tool-bin/migration-tool target/migration-tool && \
mv bin/x86_64-unknown-linux-musl-lldap_migration_tool-bin/lldap_migration_tool target/lldap_migration_tool && \
mv bin/x86_64-unknown-linux-musl-lldap_set_password-bin/lldap_set_password target/lldap_set_password && \
chmod +x target/lldap && \
chmod +x target/migration-tool && \
chmod +x target/lldap_migration_tool && \
chmod +x target/lldap_set_password && \
ls -la target/ . && \
pwd \
@@ -22,10 +22,10 @@ RUN if [ "${TARGETPLATFORM}" = "linux/amd64" ]; then \
RUN if [ "${TARGETPLATFORM}" = "linux/arm64" ]; then \
mv bin/aarch64-unknown-linux-musl-lldap-bin/lldap target/lldap && \
mv bin/aarch64-unknown-linux-musl-migration-tool-bin/migration-tool target/migration-tool && \
mv bin/aarch64-unknown-linux-musl-lldap_migration_tool-bin/lldap_migration_tool target/lldap_migration_tool && \
mv bin/aarch64-unknown-linux-musl-lldap_set_password-bin/lldap_set_password target/lldap_set_password && \
chmod +x target/lldap && \
chmod +x target/migration-tool && \
chmod +x target/lldap_migration_tool && \
chmod +x target/lldap_set_password && \
ls -la target/ . && \
pwd \
@@ -33,10 +33,10 @@ RUN if [ "${TARGETPLATFORM}" = "linux/arm64" ]; then \
RUN if [ "${TARGETPLATFORM}" = "linux/arm/v7" ]; then \
mv bin/armv7-unknown-linux-gnueabihf-lldap-bin/lldap target/lldap && \
mv bin/armv7-unknown-linux-gnueabihf-migration-tool-bin/migration-tool target/migration-tool && \
mv bin/armv7-unknown-linux-gnueabihf-lldap_migration_tool-bin/lldap_migration_tool target/lldap_migration_tool && \
mv bin/armv7-unknown-linux-gnueabihf-lldap_set_password-bin/lldap_set_password target/lldap_set_password && \
chmod +x target/lldap && \
chmod +x target/migration-tool && \
chmod +x target/lldap_migration_tool && \
chmod +x target/lldap_set_password && \
ls -la target/ . && \
pwd \
@@ -47,7 +47,7 @@ COPY docker-entrypoint.sh /docker-entrypoint.sh
COPY lldap_config.docker_template.toml /lldap/
COPY web/index_local.html web/index.html
RUN cp target/lldap /lldap/ && \
cp target/migration-tool /lldap/ && \
cp target/lldap_migration_tool /lldap/ && \
cp target/lldap_set_password /lldap/ && \
cp -R web/index.html \
web/pkg \

View File

@@ -89,7 +89,7 @@ jobs:
image: nitnelave/rust-dev:latest
steps:
- name: Checkout repository
uses: actions/checkout@v3.5.0
uses: actions/checkout@v3.5.2
- uses: actions/cache@v3
with:
path: |
@@ -138,7 +138,7 @@ jobs:
CARGO_HOME: ${GITHUB_WORKSPACE}/.cargo
steps:
- name: Checkout repository
uses: actions/checkout@v3.5.0
uses: actions/checkout@v3.5.2
- uses: actions/cache@v3
with:
path: |
@@ -151,7 +151,7 @@ jobs:
restore-keys: |
lldap-bin-${{ matrix.target }}-
- name: Compile ${{ matrix.target }} lldap and tools
run: cargo build --target=${{ matrix.target }} --release -p lldap -p migration-tool -p lldap_set_password
run: cargo build --target=${{ matrix.target }} --release -p lldap -p lldap_migration_tool -p lldap_set_password
- name: Check path
run: ls -al target/release
- name: Upload ${{ matrix.target}} lldap artifacts
@@ -162,8 +162,8 @@ jobs:
- name: Upload ${{ matrix.target }} migration tool artifacts
uses: actions/upload-artifact@v3
with:
name: ${{ matrix.target }}-migration-tool-bin
path: target/${{ matrix.target }}/release/migration-tool
name: ${{ matrix.target }}-lldap_migration_tool-bin
path: target/${{ matrix.target }}/release/lldap_migration_tool
- name: Upload ${{ matrix.target }} password tool artifacts
uses: actions/upload-artifact@v3
with:
@@ -477,7 +477,7 @@ jobs:
packages: write
steps:
- name: Checkout repository
uses: actions/checkout@v3.5.0
uses: actions/checkout@v3.5.2
- name: Download all artifacts
uses: actions/download-artifact@v3
with:
@@ -594,14 +594,14 @@ jobs:
mv bin/aarch64-unknown-linux-musl-lldap-bin/lldap bin/aarch64-lldap
mv bin/x86_64-unknown-linux-musl-lldap-bin/lldap bin/amd64-lldap
mv bin/armv7-unknown-linux-gnueabihf-lldap-bin/lldap bin/armhf-lldap
mv bin/aarch64-unknown-linux-musl-migration-tool-bin/migration-tool bin/aarch64-migration-tool
mv bin/x86_64-unknown-linux-musl-migration-tool-bin/migration-tool bin/amd64-migration-tool
mv bin/armv7-unknown-linux-gnueabihf-migration-tool-bin/migration-tool bin/armhf-migration-tool
mv bin/aarch64-unknown-linux-musl-lldap_migration_tool-bin/lldap_migration_tool bin/aarch64-lldap_migration_tool
mv bin/x86_64-unknown-linux-musl-lldap_migration_tool-bin/lldap_migration_tool bin/amd64-lldap_migration_tool
mv bin/armv7-unknown-linux-gnueabihf-lldap_migration_tool-bin/lldap_migration_tool bin/armhf-lldap_migration_tool
mv bin/aarch64-unknown-linux-musl-lldap_set_password-bin/lldap_set_password bin/aarch64-lldap_set_password
mv bin/x86_64-unknown-linux-musl-lldap_set_password-bin/lldap_set_password bin/amd64-lldap_set_password
mv bin/armv7-unknown-linux-gnueabihf-lldap_set_password-bin/lldap_set_password bin/armhf-lldap_set_password
chmod +x bin/*-lldap
chmod +x bin/*-migration-tool
chmod +x bin/*-lldap_migration_tool
chmod +x bin/*-lldap_set_password
- name: Download llap ui artifacts
@@ -627,9 +627,9 @@ jobs:
mv bin/aarch64-lldap aarch64-lldap/lldap
mv bin/amd64-lldap amd64-lldap/lldap
mv bin/armhf-lldap armhf-lldap/lldap
mv bin/aarch64-migration-tool aarch64-lldap/migration-tool
mv bin/amd64-migration-tool amd64-lldap/migration-tool
mv bin/armhf-migration-tool armhf-lldap/migration-tool
mv bin/aarch64-lldap_migration_tool aarch64-lldap/lldap_migration_tool
mv bin/amd64-lldap_migration_tool amd64-lldap/lldap_migration_tool
mv bin/armhf-lldap_migration_tool armhf-lldap/lldap_migration_tool
mv bin/aarch64-lldap_set_password aarch64-lldap/lldap_set_password
mv bin/amd64-lldap_set_password amd64-lldap/lldap_set_password
mv bin/armhf-lldap_set_password armhf-lldap/lldap_set_password

View File

@@ -33,7 +33,7 @@ jobs:
steps:
- name: Checkout sources
uses: actions/checkout@v3.5.0
uses: actions/checkout@v3.5.2
- uses: Swatinem/rust-cache@v2
- name: Build
run: cargo build --verbose --workspace
@@ -52,7 +52,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout sources
uses: actions/checkout@v3.5.0
uses: actions/checkout@v3.5.2
- uses: Swatinem/rust-cache@v2
@@ -69,7 +69,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout sources
uses: actions/checkout@v3.5.0
uses: actions/checkout@v3.5.2
- uses: Swatinem/rust-cache@v2
@@ -86,7 +86,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout sources
uses: actions/checkout@v3.5.0
uses: actions/checkout@v3.5.2
- name: Install Rust
run: rustup toolchain install nightly --component llvm-tools-preview && rustup component add llvm-tools-preview --toolchain stable-x86_64-unknown-linux-gnu
@@ -112,3 +112,28 @@ jobs:
files: lcov.info
fail_ci_if_error: true
token: ${{ secrets.CODECOV_TOKEN }}
publish-crates:
name: Publish on crates.io
if: ${{ needs.pre_job.outputs.should_skip != 'true' || (github.event_name == 'push' && github.ref == 'refs/heads/main') || github.event_name == 'release' }}
needs: pre_job
strategy:
fail-fast: false
matrix:
target: [lldap_auth, lldap, lldap_app, lldap_set_password, lldap_migration_tool]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3.5.2
- uses: actions-rs/toolchain@v1
with:
toolchain: stable
override: true
- name: Publish ${{ matrix.target }} crate
uses: katyo/publish-crates@v2
with:
args: -p ${{ matrix.target }}
dry-run: ${{ github.event_name != 'release' }}
check-repo: ${{ github.event_name != 'pull_request' }}
registry-token: ${{ secrets.CARGO_REGISTRY_TOKEN }}
ignore-unpublished-changes: ${{ github.event_name != 'release' }}

372
Cargo.lock generated
View File

@@ -118,7 +118,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "465a6172cf69b960917811022d8f29bc0b7fa1398bc4f78b3c466673db1213b6"
dependencies = [
"quote",
"syn",
"syn 1.0.109",
]
[[package]]
@@ -189,7 +189,7 @@ dependencies = [
"pin-project-lite",
"tokio-rustls",
"tokio-util",
"webpki-roots",
"webpki-roots 0.22.6",
]
[[package]]
@@ -252,7 +252,7 @@ dependencies = [
"actix-router",
"proc-macro2",
"quote",
"syn",
"syn 1.0.109",
]
[[package]]
@@ -278,7 +278,7 @@ checksum = "6d44b8fee1ced9671ba043476deddef739dd0959bf77030b26b738cc591737a7"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 1.0.109",
]
[[package]]
@@ -400,7 +400,7 @@ checksum = "726535892e8eae7e70657b4c8ea93d26b8553afb1ce617caee529ef96d7dee6c"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 1.0.109",
"synstructure",
]
@@ -412,7 +412,21 @@ checksum = "2777730b2039ac0f95f093556e61b6d26cebed5393ca6f152717777cec3a42ed"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 1.0.109",
]
[[package]]
name = "assert_cmd"
version = "2.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9834fcc22e0874394a010230586367d4a3e9f11b560f469262678547e1d2575e"
dependencies = [
"bstr",
"doc-comment",
"predicates",
"predicates-core",
"predicates-tree",
"wait-timeout",
]
[[package]]
@@ -434,7 +448,7 @@ checksum = "e4655ae1a7b0cdf149156f780c5bf3f1352bc53cbd9e0a361a7ef7b22947e965"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 1.0.109",
]
[[package]]
@@ -445,7 +459,7 @@ checksum = "1cd7fce9ba8c3c042128ce72d8b2ddbf3a05747efb67ea0313c635e10bda47a2"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 1.0.109",
]
[[package]]
@@ -497,7 +511,7 @@ dependencies = [
"proc-macro-error",
"proc-macro2",
"quote",
"syn",
"syn 1.0.109",
]
[[package]]
@@ -606,6 +620,18 @@ dependencies = [
"uuid 0.8.2",
]
[[package]]
name = "bstr"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3d4260bcc2e8fc9df1eac4919a720effeb63a3f0952f5bf4944adfa18897f09"
dependencies = [
"memchr",
"once_cell",
"regex-automata",
"serde",
]
[[package]]
name = "bumpalo"
version = "3.12.0"
@@ -695,7 +721,7 @@ dependencies = [
"proc-macro-error",
"proc-macro2",
"quote",
"syn",
"syn 1.0.109",
]
[[package]]
@@ -958,7 +984,7 @@ dependencies = [
"proc-macro2",
"quote",
"scratch",
"syn",
"syn 1.0.109",
]
[[package]]
@@ -975,7 +1001,7 @@ checksum = "086c685979a698443656e5cf7856c95c642295a38599f12fb1ff76fb28d19892"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 1.0.109",
]
[[package]]
@@ -999,7 +1025,7 @@ dependencies = [
"proc-macro2",
"quote",
"strsim",
"syn",
"syn 1.0.109",
]
[[package]]
@@ -1010,7 +1036,20 @@ checksum = "b36230598a2d5de7ec1c6f51f72d8a99a9208daff41de2084d06e3fd3ea56685"
dependencies = [
"darling_core",
"quote",
"syn",
"syn 1.0.109",
]
[[package]]
name = "dashmap"
version = "5.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "907076dfda823b0b36d2a1bb5f90c96660a5bbcd7729e10727f07858f22c4edc"
dependencies = [
"cfg-if",
"hashbrown 0.12.3",
"lock_api",
"once_cell",
"parking_lot_core 0.9.7",
]
[[package]]
@@ -1062,7 +1101,7 @@ dependencies = [
"darling",
"proc-macro2",
"quote",
"syn",
"syn 1.0.109",
]
[[package]]
@@ -1072,7 +1111,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ebcda35c7a396850a55ffeac740804b40ffec779b98fffbb1738f4033f0ee79e"
dependencies = [
"derive_builder_core",
"syn",
"syn 1.0.109",
]
[[package]]
@@ -1085,7 +1124,7 @@ dependencies = [
"proc-macro2",
"quote",
"rustc_version",
"syn",
"syn 1.0.109",
]
[[package]]
@@ -1096,7 +1135,7 @@ checksum = "532b4c15dccee12c7044f1fcad956e98410860b22231e44a3b827464797ca7bf"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 1.0.109",
]
[[package]]
@@ -1153,7 +1192,7 @@ checksum = "adc2ab4d5a16117f9029e9a6b5e4e79f4c67f6519bc134210d4d4a04ba31f41b"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 1.0.109",
]
[[package]]
@@ -1164,9 +1203,15 @@ checksum = "3bf95dc3f046b9da4f2d51833c0d3547d8564ef6910f5c1ed130306a75b92886"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 1.0.109",
]
[[package]]
name = "doc-comment"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10"
[[package]]
name = "dotenvy"
version = "0.15.6"
@@ -1255,7 +1300,7 @@ checksum = "aa4da3c766cd7a0db8242e326e9e4e081edd567072893ed320008189715366a4"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 1.0.109",
"synstructure",
]
@@ -1349,6 +1394,16 @@ version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c2141d6d6c8512188a7891b4b01590a45f6dac67afb4f255c4124dbb86d4eaa"
[[package]]
name = "fslock"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04412b8935272e3a9bae6f48c7bfff74c2911f60525404edfdd28e49884c3bfb"
dependencies = [
"libc",
"winapi",
]
[[package]]
name = "futures"
version = "0.3.26"
@@ -1388,7 +1443,7 @@ checksum = "3422d14de7903a52e9dbc10ae05a7e14445ec61890100e098754e120b2bd7b1e"
dependencies = [
"derive_utils",
"quote",
"syn",
"syn 1.0.109",
]
[[package]]
@@ -1427,7 +1482,7 @@ checksum = "95a73af87da33b5acf53acfebdc339fe592ecf5357ac7c0a7734ab9d8c876a70"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 1.0.109",
]
[[package]]
@@ -1718,7 +1773,7 @@ dependencies = [
"quote",
"serde",
"serde_json",
"syn",
"syn 1.0.109",
]
[[package]]
@@ -1735,7 +1790,7 @@ dependencies = [
"quote",
"serde",
"serde_json",
"syn",
"syn 1.0.109",
]
[[package]]
@@ -1746,7 +1801,7 @@ checksum = "e56b093bfda71de1da99758b036f4cc811fd2511c8a76f75680e9ffbd2bb4251"
dependencies = [
"graphql_client_codegen 0.10.0",
"proc-macro2",
"syn",
"syn 1.0.109",
]
[[package]]
@@ -1757,7 +1812,7 @@ checksum = "a755cc59cda2641ea3037b4f9f7ef40471c329f55c1fa2db6fa0bb7ae6c1f7ce"
dependencies = [
"graphql_client_codegen 0.11.0",
"proc-macro2",
"syn",
"syn 1.0.109",
]
[[package]]
@@ -2162,7 +2217,7 @@ dependencies = [
"proc-macro-error",
"proc-macro2",
"quote",
"syn",
"syn 1.0.109",
]
[[package]]
@@ -2268,7 +2323,7 @@ dependencies = [
"peg",
"tokio-util",
"tracing",
"uuid 1.3.0",
"uuid 1.3.1",
]
[[package]]
@@ -2296,7 +2351,7 @@ dependencies = [
"socket2",
"tokio",
"tokio-rustls",
"webpki-roots",
"webpki-roots 0.22.6",
]
[[package]]
@@ -2345,7 +2400,7 @@ checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4"
[[package]]
name = "lldap"
version = "0.4.3-alpha"
version = "0.5.0-alpha"
dependencies = [
"actix",
"actix-files",
@@ -2357,6 +2412,7 @@ dependencies = [
"actix-web",
"actix-web-httpauth",
"anyhow",
"assert_cmd",
"async-trait",
"base64 0.21.0",
"bincode",
@@ -2368,6 +2424,7 @@ dependencies = [
"figment_file_provider_adapter",
"futures",
"futures-util",
"graphql_client 0.11.0",
"hmac 0.12.1",
"http",
"image",
@@ -2375,14 +2432,17 @@ dependencies = [
"juniper",
"jwt 0.16.0",
"lber 0.4.1",
"ldap3",
"ldap3_proto",
"lettre",
"lldap_auth",
"lldap_auth 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"log",
"mockall",
"nix",
"opaque-ke",
"orion",
"rand 0.8.5",
"rand_chacha 0.3.1",
"reqwest",
"rustls",
"rustls-pemfile",
@@ -2391,6 +2451,7 @@ dependencies = [
"serde",
"serde_bytes",
"serde_json",
"serial_test",
"sha2 0.10.6",
"thiserror",
"time 0.3.19",
@@ -2405,13 +2466,13 @@ dependencies = [
"tracing-log",
"tracing-subscriber",
"urlencoding",
"uuid 1.3.0",
"webpki-roots",
"uuid 1.3.1",
"webpki-roots 0.23.0",
]
[[package]]
name = "lldap_app"
version = "0.4.3-alpha"
version = "0.5.0-alpha"
dependencies = [
"anyhow",
"base64 0.13.1",
@@ -2424,13 +2485,13 @@ dependencies = [
"image",
"indexmap",
"jwt 0.13.0",
"lldap_auth",
"lldap_auth 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.8.5",
"serde",
"serde_json",
"url-escape",
"validator",
"validator_derive",
"validator_derive 0.16.0",
"wasm-bindgen",
"wasm-bindgen-futures",
"web-sys",
@@ -2457,13 +2518,49 @@ dependencies = [
"thiserror",
]
[[package]]
name = "lldap_auth"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c17867a28e09989643401bb7849a494b328416634a335ddf7d3dabc9806ba563"
dependencies = [
"chrono",
"curve25519-dalek",
"digest 0.9.0",
"generic-array",
"getrandom 0.2.8",
"opaque-ke",
"rand 0.8.5",
"rust-argon2",
"serde",
"sha2 0.9.9",
"thiserror",
]
[[package]]
name = "lldap_migration_tool"
version = "0.4.2"
dependencies = [
"anyhow",
"base64 0.13.1",
"graphql_client 0.11.0",
"ldap3",
"lldap_auth 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.8.5",
"requestty",
"reqwest",
"serde",
"serde_json",
"smallvec",
]
[[package]]
name = "lldap_set_password"
version = "0.1.0"
dependencies = [
"anyhow",
"clap",
"lldap_auth",
"lldap_auth 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.8.5",
"reqwest",
"serde",
@@ -2538,20 +2635,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
[[package]]
name = "migration-tool"
version = "0.4.2"
name = "memoffset"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4"
dependencies = [
"anyhow",
"base64 0.13.1",
"graphql_client 0.11.0",
"ldap3",
"lldap_auth",
"rand 0.8.5",
"requestty",
"reqwest",
"serde",
"serde_json",
"smallvec",
"autocfg",
]
[[package]]
@@ -2621,7 +2710,21 @@ dependencies = [
"cfg-if",
"proc-macro2",
"quote",
"syn",
"syn 1.0.109",
]
[[package]]
name = "nix"
version = "0.26.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bfdda3d196821d6af13126e40375cdf7da646a96114af134d5f417a9a1dc8e1a"
dependencies = [
"bitflags",
"cfg-if",
"libc",
"memoffset",
"pin-utils",
"static_assertions",
]
[[package]]
@@ -2831,7 +2934,7 @@ dependencies = [
"proc-macro-error",
"proc-macro2",
"quote",
"syn",
"syn 1.0.109",
]
[[package]]
@@ -2914,7 +3017,7 @@ dependencies = [
"proc-macro2",
"proc-macro2-diagnostics",
"quote",
"syn",
"syn 1.0.109",
]
[[package]]
@@ -2976,7 +3079,7 @@ checksum = "069bdb1e05adc7a8990dce9cc75370895fbe4e3d58b9b73bf1aee56359344a55"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 1.0.109",
]
[[package]]
@@ -3064,7 +3167,7 @@ dependencies = [
"proc-macro-error-attr",
"proc-macro2",
"quote",
"syn",
"syn 1.0.109",
"version_check",
]
@@ -3081,9 +3184,9 @@ dependencies = [
[[package]]
name = "proc-macro2"
version = "1.0.51"
version = "1.0.54"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d727cae5b39d21da60fa540906919ad737832fe0b1c165da3a34d6548c849d6"
checksum = "e472a104799c74b514a57226160104aa483546de37e839ec50e3c2e41dd87534"
dependencies = [
"unicode-ident",
]
@@ -3096,16 +3199,16 @@ checksum = "4bf29726d67464d49fa6224a1d07936a8c08bb3fba727c7493f6cf1616fdaada"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 1.0.109",
"version_check",
"yansi",
]
[[package]]
name = "quote"
version = "1.0.23"
version = "1.0.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b"
checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc"
dependencies = [
"proc-macro2",
]
@@ -3301,7 +3404,7 @@ dependencies = [
"wasm-bindgen",
"wasm-bindgen-futures",
"web-sys",
"webpki-roots",
"webpki-roots 0.22.6",
"winreg",
]
@@ -3429,6 +3532,16 @@ dependencies = [
"base64 0.21.0",
]
[[package]]
name = "rustls-webpki"
version = "0.100.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d6207cd5ed3d8dca7816f8f3725513a34609c0c765bf652b8c3cb4cfd87db46b"
dependencies = [
"ring",
"untrusted",
]
[[package]]
name = "rustversion"
version = "1.0.11"
@@ -3499,7 +3612,7 @@ dependencies = [
"thiserror",
"tracing",
"url",
"uuid 1.3.0",
"uuid 1.3.1",
]
[[package]]
@@ -3512,7 +3625,7 @@ dependencies = [
"heck 0.3.3",
"proc-macro2",
"quote",
"syn",
"syn 1.0.109",
]
[[package]]
@@ -3523,7 +3636,7 @@ checksum = "d2fbe015dbdaa7d8829d71c1e14fb6289e928ac256b93dfda543c85cd89d6f03"
dependencies = [
"chrono",
"sea-query-derive",
"uuid 1.3.0",
"uuid 1.3.1",
]
[[package]]
@@ -3535,7 +3648,7 @@ dependencies = [
"chrono",
"sea-query",
"sqlx",
"uuid 1.3.0",
"uuid 1.3.1",
]
[[package]]
@@ -3547,7 +3660,7 @@ dependencies = [
"heck 0.4.1",
"proc-macro2",
"quote",
"syn",
"syn 1.0.109",
"thiserror",
]
@@ -3570,7 +3683,7 @@ dependencies = [
"proc-macro2",
"quote",
"rustversion",
"syn",
"syn 1.0.109",
]
[[package]]
@@ -3650,7 +3763,7 @@ checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 1.0.109",
]
[[package]]
@@ -3677,6 +3790,30 @@ dependencies = [
"serde",
]
[[package]]
name = "serial_test"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0e56dd856803e253c8f298af3f4d7eb0ae5e23a737252cd90bb4f3b435033b2d"
dependencies = [
"dashmap",
"fslock",
"lazy_static",
"parking_lot 0.12.1",
"serial_test_derive",
]
[[package]]
name = "serial_test_derive"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "91d129178576168c589c9ec973feedf7d3126c01ac2bf08795109aa35b69fb8f"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.12",
]
[[package]]
name = "sha1"
version = "0.10.5"
@@ -3895,8 +4032,8 @@ dependencies = [
"thiserror",
"tokio-stream",
"url",
"uuid 1.3.0",
"webpki-roots",
"uuid 1.3.1",
"webpki-roots 0.22.6",
"whoami",
]
@@ -3914,7 +4051,7 @@ dependencies = [
"quote",
"sqlx-core",
"sqlx-rt",
"syn",
"syn 1.0.109",
"url",
]
@@ -3968,6 +4105,17 @@ dependencies = [
"unicode-ident",
]
[[package]]
name = "syn"
version = "2.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "79d9531f94112cfc3e4c8f5f02cb2b58f72c97b7efd85f70203cc6d8efda5927"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "synstructure"
version = "0.12.6"
@@ -3976,7 +4124,7 @@ checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 1.0.109",
"unicode-xid",
]
@@ -4037,7 +4185,7 @@ checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 1.0.109",
]
[[package]]
@@ -4131,7 +4279,7 @@ checksum = "d266c00fde287f55d3f1c3e96c500c362a2b8c695076ec180f27918820bc6df8"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 1.0.109",
]
[[package]]
@@ -4207,7 +4355,7 @@ dependencies = [
"actix-web",
"pin-project",
"tracing",
"uuid 1.3.0",
"uuid 1.3.1",
]
[[package]]
@@ -4218,7 +4366,7 @@ checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 1.0.109",
]
[[package]]
@@ -4408,9 +4556,9 @@ checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7"
[[package]]
name = "uuid"
version = "1.3.0"
version = "1.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1674845326ee10d37ca60470760d4288a6f80f304007d92e5c53bab78c9cfd79"
checksum = "5b55a3fef2a1e3b3a00ce878640918820d3c51081576ac657d23af9fc7928fdb"
dependencies = [
"getrandom 0.2.8",
"md-5",
@@ -4429,7 +4577,7 @@ dependencies = [
"serde_derive",
"serde_json",
"url",
"validator_types",
"validator_types 0.14.0",
]
[[package]]
@@ -4444,8 +4592,24 @@ dependencies = [
"proc-macro2",
"quote",
"regex",
"syn",
"validator_types",
"syn 1.0.109",
"validator_types 0.14.0",
]
[[package]]
name = "validator_derive"
version = "0.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc44ca3088bb3ba384d9aecf40c6a23a676ce23e09bdaca2073d99c207f864af"
dependencies = [
"if_chain",
"lazy_static",
"proc-macro-error",
"proc-macro2",
"quote",
"regex",
"syn 1.0.109",
"validator_types 0.16.0",
]
[[package]]
@@ -4455,7 +4619,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ded9d97e1d42327632f5f3bae6403c04886e2de3036261ef42deebd931a6a291"
dependencies = [
"proc-macro2",
"syn",
"syn 1.0.109",
]
[[package]]
name = "validator_types"
version = "0.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "111abfe30072511849c5910134e8baf8dc05de4c0e5903d681cbd5c9c4d611e3"
dependencies = [
"proc-macro2",
"syn 1.0.109",
]
[[package]]
@@ -4482,6 +4656,15 @@ version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
[[package]]
name = "wait-timeout"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6"
dependencies = [
"libc",
]
[[package]]
name = "want"
version = "0.3.0"
@@ -4531,7 +4714,7 @@ dependencies = [
"once_cell",
"proc-macro2",
"quote",
"syn",
"syn 1.0.109",
"wasm-bindgen-shared",
]
@@ -4565,7 +4748,7 @@ checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 1.0.109",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
@@ -4605,6 +4788,15 @@ dependencies = [
"webpki",
]
[[package]]
name = "webpki-roots"
version = "0.23.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa54963694b65584e170cf5dc46aeb4dcaa5584e652ff5f3952e56d66aff0125"
dependencies = [
"rustls-webpki",
]
[[package]]
name = "whoami"
version = "1.3.0"
@@ -4790,7 +4982,7 @@ dependencies = [
"proc-macro-error",
"proc-macro2",
"quote",
"syn",
"syn 1.0.109",
]
[[package]]
@@ -4821,7 +5013,7 @@ checksum = "39049d193b52eaad4ffc80916bf08806d142c90b5edcebd527644de438a7e19a"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 1.0.109",
]
[[package]]
@@ -4831,7 +5023,7 @@ source = "git+https://github.com/jfbilodeau/yew_form?rev=4b9fabffb63393ec7626a44
dependencies = [
"gloo-console",
"validator",
"validator_derive",
"validator_derive 0.14.0",
"wasm-bindgen",
"web-sys",
"yew",
@@ -4844,7 +5036,7 @@ source = "git+https://github.com/jfbilodeau/yew_form?rev=4b9fabffb63393ec7626a44
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 1.0.109",
"yew_form",
]
@@ -4865,7 +5057,7 @@ checksum = "44bf07cb3e50ea2003396695d58bf46bc9887a1f362260446fad6bc4e79bd36c"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 1.0.109",
"synstructure",
]

View File

@@ -31,12 +31,12 @@ FROM chef AS builder
COPY --from=planner /tmp/recipe.json recipe.json
RUN cargo chef cook --release -p lldap_app --target wasm32-unknown-unknown \
&& cargo chef cook --release -p lldap \
&& cargo chef cook --release -p migration-tool \
&& cargo chef cook --release -p lldap_migration_tool \
&& cargo chef cook --release -p lldap_set_password
# Copy the source and build the app and server.
COPY --chown=app:app . .
RUN cargo build --release -p lldap -p migration-tool -p lldap_set_password \
RUN cargo build --release -p lldap -p lldap_migration_tool -p lldap_set_password \
# Build the frontend.
&& ./app/build.sh
@@ -78,7 +78,7 @@ WORKDIR /app
COPY --from=builder /app/app/index_local.html app/index.html
COPY --from=builder /app/app/static app/static
COPY --from=builder /app/app/pkg app/pkg
COPY --from=builder /app/target/release/lldap /app/target/release/migration-tool /app/target/release/lldap_set_password ./
COPY --from=builder /app/target/release/lldap /app/target/release/lldap_migration_tool /app/target/release/lldap_set_password ./
COPY docker-entrypoint.sh lldap_config.docker_template.toml ./
RUN set -x \

View File

@@ -13,6 +13,7 @@
<a href="https://discord.gg/h5PEdRMNyP">
<img alt="Discord" src="https://img.shields.io/discord/898492935446876200?label=discord&logo=discord" />
</a>
<a href="https://twitter.com/nitnelave1?ref_src=twsrc%5Etfw">
<img
src="https://img.shields.io/twitter/follow/nitnelave1?style=social"
@@ -26,6 +27,10 @@
<a href="https://app.codecov.io/gh/lldap/lldap">
<img alt="Codecov" src="https://img.shields.io/codecov/c/github/lldap/lldap" />
</a>
<br/>
<a href="https://www.buymeacoffee.com/nitnelave" target="_blank">
<img src="https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png" alt="Buy Me A Coffee" style="height: 41px !important;width: 174px !important;box-shadow: 0px 3px 2px 0px rgba(190, 190, 190, 0.5) !important;-webkit-box-shadow: 0px 3px 2px 0px rgba(190, 190, 190, 0.5) !important;" >
</a>
</p>
- [About](#about)
@@ -158,7 +163,7 @@ To compile the project, you'll need:
Then you can compile the server (and the migration tool if you want):
```shell
cargo build --release -p lldap -p migration-tool
cargo build --release -p lldap -p lldap_migration_tool
```
The resulting binaries will be in `./target/release/`. Alternatively, you can
@@ -257,6 +262,7 @@ folder for help with:
- [Dex](example_configs/dex_config.yml)
- [Dokuwiki](example_configs/dokuwiki.md)
- [Dolibarr](example_configs/dolibarr.md)
- [Ejabberd](example_configs/ejabberd.md)
- [Emby](example_configs/emby.md)
- [Gitea](example_configs/gitea.md)
- [Grafana](example_configs/grafana_ldap_config.toml)

View File

@@ -1,8 +1,12 @@
[package]
name = "lldap_app"
version = "0.4.3"
authors = ["Valentin Tolmer <valentin@tolmer.fr>"]
description = "Frontend for LLDAP"
edition = "2021"
homepage = "https://github.com/lldap/lldap"
license = "GPL-3.0-only"
name = "lldap_app"
repository = "https://github.com/lldap/lldap"
version = "0.5.0-alpha"
include = ["src/**/*", "queries/**/*", "Cargo.toml", "../schema.graphql"]
[dependencies]
@@ -10,7 +14,7 @@ anyhow = "1"
base64 = "0.13"
gloo-console = "0.2.3"
gloo-file = "0.2.3"
gloo-net = "*"
gloo-net = "0.2"
graphql_client = "0.10"
http = "0.2"
jwt = "0.13"
@@ -19,9 +23,9 @@ serde = "1"
serde_json = "1"
url-escape = "0.1.1"
validator = "=0.14"
validator_derive = "*"
validator_derive = "0.16"
wasm-bindgen = "0.2"
wasm-bindgen-futures = "*"
wasm-bindgen-futures = "0.4"
yew = "0.19.3"
yew-router = "0.16"
@@ -43,13 +47,13 @@ features = [
]
[dependencies.chrono]
version = "*"
version = "0.4"
features = [
"wasmbind"
]
[dependencies.lldap_auth]
path = "../auth"
version = "0.3"
features = [ "opaque_client" ]
[dependencies.image]

View File

@@ -1,8 +1,12 @@
[package]
name = "lldap_auth"
version = "0.3.0"
authors = ["Valentin Tolmer <valentin@tolmer.fr>"]
description = "Authentication protocol for LLDAP"
edition = "2021"
homepage = "https://github.com/lldap/lldap"
license = "GPL-3.0-only"
name = "lldap_auth"
repository = "https://github.com/lldap/lldap"
version = "0.3.0"
[features]
default = ["opaque_server", "opaque_client"]
@@ -14,17 +18,17 @@ js = []
rust-argon2 = "0.8"
curve25519-dalek = "3"
digest = "0.9"
generic-array = "*"
generic-array = "0.14"
rand = "0.8"
serde = "*"
serde = "1"
sha2 = "0.9"
thiserror = "*"
thiserror = "1"
[dependencies.opaque-ke]
version = "0.6"
[dependencies.chrono]
version = "*"
version = "0.4"
features = [ "serde" ]
# For WASM targets, use the JS getrandom.

View File

@@ -0,0 +1,58 @@
# Migration from 0.4 to 0.5
Welcome! If you're here, it's probably that the migration from 0.4.x to 0.5
didn't go smoothly for you. Don't worry, we can fix that.
## Multiple users with the same email
This is the most common case. You can see in the LLDAP logs that there are
several users with the same email, and they are listed.
This is not allowed anymore in v0.5, to prevent a user from setting their email
to someone else's email and gaining access to systems that identify by email.
The problem is that you currently have several users with the same email, so the
constraint cannot be enforced.
### Step 1: Take a note of the users with duplicate emails
In the LLDAP logs when you tried to start v0.5+, you'll see some warnings with
the list of users with the same emails. Take note of them.
### Step 2: Downgrade to v0.4.3
If using docker, switch to the `lldap/lldap:v0.4.3` image. Alternatively, grab
the binaries at https://github.com/lldap/lldap/releases/tag/v0.4.3.
This downgrade is safe and supported.
### Step 3: Remove duplicate emails
Restart LLDAP with the v0.4.3 version, and using your notes from step 1, change
the email of users with duplicate emails to make sure that each email is unique.
### Step 4: Upgrade again
You can now revert to the initial version.
## Multiple users/groups with the same UUID
This should be extremely rare. In this case, you'll need to find which users
have the same UUID, revert to v0.4.3 to be able to apply the changes, and delete
one of the duplicates.
## FAQ
### What if I want several users to be controlled by the same email?
You can use plus codes to set "the same" email to several users, while ensuring
that they can't identify as each other. For instance:
- Admin: `admin@example.com`
- Read-only admin: `admin+readonly@example.com`
- Jellyfin admin: `admin+jellyfin@example.com`
### I'm upgrading to a higher version than v0.5.
This guide is still relevant: you can use whatever later version in place of
v0.5. You'll still need to revert to v0.4.3 to apply the changes.

View File

@@ -0,0 +1,30 @@
# Basic LDAP auth for a Ejabberd XMPP server
[Main documentation here.](https://docs.ejabberd.im/admin/configuration/ldap/)
For simple user auth add this to main ejabberd.yml:
```
host_config:
xmpp.example.org:
auth_method: [ldap]
ldap_servers:
- 127.0.0.1 #IP or hostname of LLDAP server
ldap_port: 3890
ldap_uids:
- uid
ldap_rootdn: "uid=lldap_readonly,ou=people,dc=example,dc=org"
ldap_password: "secret"
ldap_base: "ou=people,dc=example,dc=org"
```
## vCard from LDAP
Theoretically possible, [see the documentation.](https://docs.ejabberd.im/admin/configuration/ldap/#vcard-in-ldap)
TODO
## Shared roster groups from LDAP
Theoretically possible, [see the documentation.](https://docs.ejabberd.im/admin/configuration/ldap/#shared-roster-in-ldap)
TODO

View File

@@ -44,6 +44,6 @@ username = "uid"
# If you want to map your ldap groups to grafana's groups, see: https://grafana.com/docs/grafana/latest/auth/ldap/#group-mappings
# As a quick example, here is how you would map lldap's admin group to grafana's admin
# [[servers.group_mappings]]
# group_dn = "uid=lldap_admin,ou=groups,dc=example,dc=org"
# group_dn = "cn=lldap_admin,ou=groups,dc=example,dc=org"
# org_role = "Admin"
# grafana_admin = true

View File

@@ -7,6 +7,7 @@ Home Assistant configures ldap auth via the [Command Line Auth Provider](https:/
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`.
- Set the script as executable by running `chmod +x /config/lldap-auth-sh`
2. Add the following to your configuration.yaml in Home assistant:
```yaml
homeassistant:
@@ -20,4 +21,4 @@ homeassistant:
args: ["https://lldap.example.com", "homeassistant_user"]
meta: true
```
3. Reload your config or restart Home Assistant
3. Reload your config or restart Home Assistant

113
example_configs/opnsense.md Normal file
View File

@@ -0,0 +1,113 @@
# Configuration for OPNsense
## Create a LDAP Server
- Login to OPNsense
- Navigate to: `System > Access > Servers`
- Create a new server by clicking on the `+` icon
## Server Config
- Descriptive Name: `A Descriptive Name`
- Type: `LDAP`
- Hostname or IP address: `Hostname or IP for your LLDAP host`
- Port value: `Your LLDAP port`
- Default: `3890`
- Transport: `TCP - Standard`
- Protocol version: `3`
Make sure the host running LLDAP is accessible to OPNsense and that you mapped the LLDAP port to the LLDAP host.
## LDAP Config
### Bind credentials
#### User DN
```
uid=admin,ou=people,dc=example,dc=com
```
It is recommended that you create a separate user account (e.g, `bind_user`) instead of `admin` for sharing Bind credentials with other services. The `bind_user` should be a member of the `lldap_strict_readonly` group to limit access to your LDAP configuration in LLDAP.
#### Password
```
xxx
```
Enter the password that you set for the user specified in the User DN field.
### Search Scope
```
One Level
```
### Base DN
```
dc=example,dc=com
```
This is the same LDAP Base DN that you set via the *LLDAP_LDAP_BASE_DN* environment variable or in `lldap_config.toml`.
### Authentication containers
```
ou=people,dc=example,dc=com
```
Note: The `Select` box may not work for selecting containers. You can just enter the `Authentication containers` directly into the text field.
### Extended Query
```
&(objectClass=person)(memberof=cn=lldap_admin,ou=groups,dc=example,dc=com)
```
It is recommended that you create a unique LDAP group (e.g., `lldap_opnsense`) in LLDAP and use that group in this query instead of `lldap_admin`. This will limit OPNsense access to users in the `lldap_opnsense` group and make it easier to synchronize LLDAP groups with OPNsense groups for managing OPNsense access.
### Initial Template
```
OpenLDAP
```
### User naming attribute
```
uid
```
## Optional Configuration
The above configuration will connect OPNsense to LLDAP. This optional configuration will synchronize groups between LLDAP and OPNsense and automate user creation when an authorized LLDAP user logs into OPNsense.
### Remaining Server Configuration
Enable the following options on the OPNsense configuration page for your LLDAP server (the same page where you entered the prior configuration):
- Read Properties: `Checked`
- Synchronize groups: `Checked`
- Automatic user creation: `Checked`
### Create OPNsense Group
Go to `System > Access > Groups` and create a new group with the **same** name as the LLDAP group used to authenticate users for OPNsense.
By default, you would name your OPNsense group `lldap_admin` unless you followed the recommended advice in this guide and created a separate `lldap_opnsense` group for managing OPNsense users.
If you want your LLDAP users to have full administrator access in OPNsense, then you need to edit the `Assigned Privileges` for the group and add the `GUI - All pages` system privilege.
### Enable LLDAP as an Authentication Option
Go to `System > Settings > Administration` page and scroll down to the `Authentication` section. Add your LLDAP server configuration to the `Server` field.
## Testing LLDAP
OPNsense includes a built-in feature for testing user authentication at `System > Access > Tester`. Select your LLDAP server configuration in the `Authentication Server` to test logins for your LLDAP users.
## More Information
Please read the [OPNsense docs](https://docs.opnsense.org/manual/how-tos/user-ldap.html) for more information on LDAP configuration and managing access to OPNsense.

117
example_configs/pfsense.md Normal file
View File

@@ -0,0 +1,117 @@
# Configuration for pfSense
## Create a LDAP Server
- Login to pfSense
- Navigate to: `System > User Manager > Authentication Servers`
- Create a new server by clicking on the `+ Add` button
## LDAP Server Settings
- Descriptive Name: `A Descriptive Name`
- Type: `LDAP`
- Hostname or IP address: `Hostname or IP for your LLDAP host`
- Port value: `Your LLDAP port`
- Transport: `TCP - Standard`
- Protocol version: `3`
- Server Timeout: `25`
(Make sure the host running LLDAP is accessible to pfSense and that you mapped the LLDAP port to the LLDAP host)
### Search Scope
```
Entire Subtree
```
### Base DN
```
dc=example,dc=com
```
This is the same LDAP Base DN that you set via the *LLDAP_LDAP_BASE_DN* environment variable or in `lldap_config.toml`.
### Authentication containers
```
ou=people
```
Note: The `Select a container` box may not work for selecting containers. You can just enter the `Authentication containers` directly into the text field.
### Extended Query
Enable extended query: `Checked`
### Query:
```
&(objectClass=person)(|(memberof=cn=pfsense_admin,ou=groups,dc=example,dc=com)(memberof=cn=pfsense_guest,ou=groups,dc=example,dc=com))
```
This example gives you two groups in LLDAP, one for pfSense admin access (`pfsense_admin`) and one for guest access (`pfsense_guest`). You **must** create these exact same groups in both LLDAP and pfSense, then give them the correct permissions in pfSense.
### Bind Anonymous
`Unchecked`
### Bind credentials
#### User DN
```
uid=yourbinduser,ou=people,dc=example,dc=com
```
It is recommended that you create a separate read-only user account (e.g, `readonly`) instead of `admin` for sharing Bind credentials with other services. The `readonly` should be a member of the `lldap_strict_readonly` group to limit access to your LDAP configuration in LLDAP.
#### Password
```
LLDAPPasswordForBindUser
```
### User naming attribute
```
uid
```
### Group naming attribute
```
cn
```
### Group member attribute
```
memberof
```
### RFC 2307 Groups
`Unchecked`
### Group Object Class
`groupOfUniqueNames`
### Shell Authentication Group DN
`cn=pfsense_admin,ou=groups,dc=example,dc=com`
(This is only if you want to give a group shell access through LDAP. Leave blank and only the pfSense admin user will have shell access.
### Remaining Server Configuration
Enable the following options on the pfSense configuration page for your LLDAP server (the same page where you entered the prior configuration):
- UTF8 Encodes: `Checked`
- Username Alterations: `Unchecked`
- Allow unauthenticated bind: `Unchecked`
### Create pfSense Groups
Go to `System > User Manager > Groups` and create a new group(s) with the **same exact** name as the LLDAP group(s) used to authenticate users for pfSense (`pfsense_admin` and `pfsense_guest` in this example).
If you want your LLDAP users to have full administrator access in pfSense, then you need to edit the `Assigned Privileges` for the group and add the `WebCfg - All pages` system privilege. If you do not give any permissions to a group, you will be able to log in but only see an empty webUI.
### Enable LLDAP as an Authentication Option
Go to `System > User Manager > Settings` page. Add your LLDAP server configuration to the `Authentication Server` field. **The "Save & Test" Button will fail the test results at step 3. No clue why.**
## Testing LLDAP
pfSense includes a built-in feature for testing user authentication at `Diagnostics > Authentication`. Select your LLDAP server configuration in the `Authentication Server` to test logins for your LLDAP users. The groups (only the ones you added to pfSense) should show up when tested.
## More Information
Please read the [pfSense docs](https://docs.netgate.com/pfsense/en/latest/usermanager/ldap.html) for more information on LDAP configuration and managing access to pfSense.

View File

@@ -13,6 +13,6 @@ You setup https://zend.to/ for using LDAP by editing `/opt/zendto/config/prefere
'authLDAPUsernameAttr' => 'uid',
'authLDAPEmailAttr' => 'mail',
'authLDAPMemberKey' => 'memberOf',
'authLDAPMemberRole' => 'uid=zendto,ou=groups,dc=example,dc=com',
'authLDAPMemberRole' => 'cn=zendto,ou=groups,dc=example,dc=com',
```
Every user of the group `zendto` is allowed to login.

View File

@@ -93,8 +93,17 @@ database_url = "sqlite:///data/users.db?mode=rwc"
## would still have to perform an (expensive) brute force attack to find
## each password.
## Randomly generated on first run if it doesn't exist.
## Alternatively, you can use key_seed to override this instead of relying on
## a file.
## Env variable: LLDAP_KEY_FILE
key_file = "/data/private_key"
## Seed to generate the server private key, see key_file above.
## This can be any random string, the recommendation is that it's at least 12
## characters long.
## Env variable: LLDAP_KEY_SEED
#key_seed = "RanD0m STR1ng"
## Ignored attributes.
## Some services will request attributes that are not present in LLDAP. When it
## is the case, LLDAP will warn about the attribute being unknown. If you want
@@ -106,7 +115,7 @@ key_file = "/data/private_key"
## Options to configure SMTP parameters, to send password reset emails.
## To set these options from environment variables, use the following format
## (example with "password"): LLDAP_SMTP_OPTIONS__PASSWORD
#[smtp_options]
[smtp_options]
## Whether to enabled password reset via email, from LLDAP.
#enable_password_reset=true
## The SMTP server.
@@ -128,7 +137,7 @@ key_file = "/data/private_key"
## Options to configure LDAPS.
## To set these options from environment variables, use the following format
## (example with "port"): LLDAP_LDAPS_OPTIONS__PORT
#[ldaps_options]
[ldaps_options]
## Whether to enable LDAPS.
#enabled=true
## Port on which to listen.

View File

@@ -1,20 +1,24 @@
[package]
name = "migration-tool"
version = "0.4.2"
edition = "2021"
authors = ["Valentin Tolmer <valentin@tolmer.fr>"]
description = "CLI migration tool to go from OpenLDAP to LLDAP"
edition = "2021"
homepage = "https://github.com/lldap/lldap"
license = "GPL-3.0-only"
name = "lldap_migration_tool"
repository = "https://github.com/lldap/lldap"
version = "0.4.2"
[dependencies]
anyhow = "*"
anyhow = "1"
base64 = "0.13"
rand = "0.8"
requestty = "0.4.1"
serde = "1"
serde_json = "1"
smallvec = "*"
smallvec = "1"
[dependencies.lldap_auth]
path = "../auth"
version = "0.3"
features = ["opaque_client"]
[dependencies.graphql_client]
@@ -23,11 +27,11 @@ default-features = false
version = "0.11"
[dependencies.reqwest]
version = "*"
version = "0.11"
default-features = false
features = ["json", "blocking", "rustls-tls"]
[dependencies.ldap3]
version = "*"
version = "0.11"
default-features = false
features = ["sync", "tls-rustls"]

View File

@@ -1,8 +1,14 @@
[package]
authors = ["Valentin Tolmer <valentin@tolmer.fr>"]
categories = ["authentication", "command-line-utilities"]
description = "Super-simple and lightweight LDAP server"
edition = "2021"
homepage = "https://github.com/lldap/lldap"
keywords = ["cli", "ldap", "graphql", "server", "authentication"]
license = "GPL-3.0-only"
name = "lldap"
version = "0.4.3"
repository = "https://github.com/lldap/lldap"
version = "0.5.0-alpha"
[dependencies]
actix = "0.13"
@@ -13,44 +19,45 @@ actix-server = "2"
actix-service = "2"
actix-web = "4.3"
actix-web-httpauth = "0.8"
anyhow = "*"
anyhow = "1"
async-trait = "0.1"
base64 = "0.21"
bincode = "1.3"
cron = "*"
cron = "0.12"
derive_builder = "0.12"
figment_file_provider_adapter = "0.1"
futures = "*"
futures-util = "*"
futures = "0.3"
futures-util = "0.3"
hmac = "0.12"
http = "*"
http = "0.2"
itertools = "0.10"
juniper = "0.15"
jwt = "0.16"
lber = "0.4.1"
ldap3_proto = ">=0.3.1"
log = "*"
log = "0.4"
orion = "0.17"
rand_chacha = "0.3"
rustls-pemfile = "1"
serde = "*"
serde = "1"
serde_bytes = "0.11"
serde_json = "1"
sha2 = "0.10"
thiserror = "*"
thiserror = "1"
time = "0.3"
tokio-rustls = "0.23"
tokio-stream = "*"
tokio-stream = "0.1"
tokio-util = "0.7"
tracing = "*"
tracing = "0.1"
tracing-actix-web = "0.7"
tracing-attributes = "^0.1.21"
tracing-log = "*"
tracing-log = "0.1"
urlencoding = "2"
webpki-roots = "*"
webpki-roots = "0.23"
[dependencies.chrono]
features = ["serde"]
version = "*"
version = "0.4"
[dependencies.clap]
features = ["std", "color", "suggestions", "derive", "env"]
@@ -58,7 +65,7 @@ version = "4"
[dependencies.figment]
features = ["env", "toml"]
version = "*"
version = "0.10"
[dependencies.tracing-subscriber]
version = "0.3"
@@ -70,7 +77,7 @@ default-features = false
version = "0.10.1"
[dependencies.lldap_auth]
path = "../auth"
version = "0.3"
[dependencies.opaque-ke]
version = "0.6"
@@ -81,7 +88,7 @@ version = "0.8"
[dependencies.secstr]
features = ["serde"]
version = "*"
version = "0.5"
[dependencies.tokio]
features = ["full"]
@@ -89,7 +96,7 @@ version = "1.25"
[dependencies.uuid]
features = ["v3"]
version = "*"
version = "1"
[dependencies.tracing-forest]
features = ["smallvec", "chrono", "tokio"]
@@ -119,4 +126,30 @@ version = "0.20"
features = ["dangerous_configuration"]
[dev-dependencies]
assert_cmd = "2.0"
mockall = "0.11"
nix = "0.26.2"
[dev-dependencies.graphql_client]
features = ["graphql_query_derive", "reqwest-rustls"]
default-features = false
version = "0.11"
[dev-dependencies.ldap3]
version = "0.11"
default-features = false
features = ["sync", "tls-rustls"]
[dev-dependencies.reqwest]
version = "0.11"
default-features = false
features = ["json", "blocking", "rustls-tls"]
[dev-dependencies.serial_test]
version = "2.0.0"
default-features = false
features = ["file_locks"]
[dev-dependencies.uuid]
version = "1"
features = ["v4"]

View File

@@ -86,7 +86,7 @@ pub mod tests {
handler
.create_user(CreateUserRequest {
user_id: UserId::new(name),
email: "bob@bob.bob".to_string(),
email: format!("{}@bob.bob", name),
display_name: Some("display ".to_string() + name),
first_name: Some("first ".to_string() + name),
last_name: Some("last ".to_string() + name),

View File

@@ -2,15 +2,17 @@ use crate::domain::{
sql_tables::{DbConnection, SchemaVersion},
types::{GroupId, UserId, Uuid},
};
use anyhow::Context;
use itertools::Itertools;
use sea_orm::{
sea_query::{self, ColumnDef, Expr, ForeignKey, ForeignKeyAction, Query, Table, Value},
ConnectionTrait, FromQueryResult, Iden, Statement, TransactionTrait,
sea_query::{
self, all, ColumnDef, Expr, ForeignKey, ForeignKeyAction, Func, Index, Query, Table, Value,
},
ConnectionTrait, FromQueryResult, Iden, Order, Statement, TransactionTrait,
};
use serde::{Deserialize, Serialize};
use tracing::{info, instrument, warn};
use super::sql_tables::LAST_SCHEMA_VERSION;
#[derive(Iden, PartialEq, Eq, Debug, Serialize, Deserialize, Clone, Copy)]
pub enum Users {
Table,
@@ -460,30 +462,136 @@ async fn migrate_to_v3(pool: &DbConnection) -> anyhow::Result<()> {
Ok(())
}
async fn migrate_to_v4(pool: &DbConnection) -> anyhow::Result<()> {
let builder = pool.get_database_backend();
// Make emails and UUIDs unique.
if let Err(e) = pool
.execute(
builder.build(
Index::create()
.if_not_exists()
.name("unique-user-email")
.table(Users::Table)
.col(Users::Email)
.unique(),
),
)
.await
.context(
r#"while enforcing unicity on emails (2 users have the same email).
See https://github.com/lldap/lldap/blob/main/docs/migration_guides/v0.5.md for details.
"#,
)
{
warn!("Found several users with the same email:");
for (email, users) in &pool
.query_all(
builder.build(
Query::select()
.from(Users::Table)
.columns([Users::Email, Users::UserId])
.order_by_columns([(Users::Email, Order::Asc), (Users::UserId, Order::Asc)])
.and_where(
Expr::col(Users::Email).in_subquery(
Query::select()
.from(Users::Table)
.column(Users::Email)
.group_by_col(Users::Email)
.cond_having(all![Expr::gt(
Expr::expr(Func::count(Expr::col(Users::Email))),
1
)])
.take(),
),
),
),
)
.await
.expect("Could not check duplicate users")
.into_iter()
.map(|row| {
(
row.try_get::<UserId>("", &Users::UserId.to_string())
.unwrap(),
row.try_get::<String>("", &Users::Email.to_string())
.unwrap(),
)
})
.group_by(|(_user, email)| email.to_owned())
{
warn!("Email: {email}");
for (user, _email) in users {
warn!(" User: {}", user.as_str());
}
}
return Err(e);
}
pool.execute(
builder.build(
Index::create()
.if_not_exists()
.name("unique-user-uuid")
.table(Users::Table)
.col(Users::Uuid)
.unique(),
),
)
.await
.context("while enforcing unicity on user UUIDs (2 users have the same UUID)")?;
pool.execute(
builder.build(
Index::create()
.if_not_exists()
.name("unique-group-uuid")
.table(Groups::Table)
.col(Groups::Uuid)
.unique(),
),
)
.await
.context("while enforcing unicity on group UUIDs (2 groups have the same UUID)")?;
Ok(())
}
// This is needed to make an array of async functions.
macro_rules! to_sync {
($l:ident) => {
|pool| -> std::pin::Pin<Box<dyn std::future::Future<Output = anyhow::Result<()>>>> {
Box::pin($l(pool))
}
};
}
pub async fn migrate_from_version(
pool: &DbConnection,
version: SchemaVersion,
last_version: SchemaVersion,
) -> anyhow::Result<()> {
match version.cmp(&LAST_SCHEMA_VERSION) {
std::cmp::Ordering::Less => info!(
"Upgrading DB schema from {} to {}",
version.0, LAST_SCHEMA_VERSION.0
),
match version.cmp(&last_version) {
std::cmp::Ordering::Less => (),
std::cmp::Ordering::Equal => return Ok(()),
std::cmp::Ordering::Greater => anyhow::bail!("DB version downgrading is not supported"),
}
if version < SchemaVersion(2) {
migrate_to_v2(pool).await?;
}
if version < SchemaVersion(3) {
migrate_to_v3(pool).await?;
info!("Upgrading DB schema from version {}", version.0);
let migrations = [
to_sync!(migrate_to_v2),
to_sync!(migrate_to_v3),
to_sync!(migrate_to_v4),
];
for migration in 2..=4 {
if version < SchemaVersion(migration) && SchemaVersion(migration) <= last_version {
info!("Upgrading DB schema to version {}", migration);
migrations[(migration - 2) as usize](pool).await?;
}
}
let builder = pool.get_database_backend();
pool.execute(
builder.build(
Query::update()
.table(Metadata::Table)
.value(Metadata::Version, Value::from(LAST_SCHEMA_VERSION)),
.value(Metadata::Version, Value::from(last_version)),
),
)
.await?;

View File

@@ -21,7 +21,7 @@ impl From<SchemaVersion> for Value {
}
}
pub const LAST_SCHEMA_VERSION: SchemaVersion = SchemaVersion(3);
const LAST_SCHEMA_VERSION: SchemaVersion = SchemaVersion(4);
pub async fn init_table(pool: &DbConnection) -> anyhow::Result<()> {
let version = {
@@ -32,7 +32,7 @@ pub async fn init_table(pool: &DbConnection) -> anyhow::Result<()> {
SchemaVersion(1)
}
};
migrate_from_version(pool, version).await?;
migrate_from_version(pool, version, LAST_SCHEMA_VERSION).await?;
Ok(())
}
@@ -46,6 +46,7 @@ mod tests {
use super::*;
use chrono::prelude::*;
use sea_orm::{ConnectionTrait, Database, DbBackend, FromQueryResult};
use tracing::error;
async fn get_in_memory_db() -> DbConnection {
let mut sql_opt = sea_orm::ConnectOptions::new("sqlite::memory:".to_owned());
@@ -100,21 +101,21 @@ mod tests {
let sql_pool = get_in_memory_db().await;
sql_pool
.execute(raw_statement(
r#"CREATE TABLE users ( user_id TEXT, display_name TEXT, first_name TEXT NOT NULL, last_name TEXT, avatar BLOB, creation_date TEXT);"#,
r#"CREATE TABLE users ( user_id TEXT, display_name TEXT, first_name TEXT NOT NULL, last_name TEXT, avatar BLOB, creation_date TEXT, email TEXT);"#,
))
.await
.unwrap();
sql_pool
.execute(raw_statement(
r#"INSERT INTO users (user_id, display_name, first_name, creation_date)
VALUES ("bôb", "", "", "1970-01-01 00:00:00")"#,
r#"INSERT INTO users (user_id, display_name, first_name, creation_date, email)
VALUES ("bôb", "", "", "1970-01-01 00:00:00", "bob@bob.com")"#,
))
.await
.unwrap();
sql_pool
.execute(raw_statement(
r#"INSERT INTO users (user_id, display_name, first_name, creation_date)
VALUES ("john", "John Doe", "John", "1971-01-01 00:00:00")"#,
r#"INSERT INTO users (user_id, display_name, first_name, creation_date, email)
VALUES ("john", "John Doe", "John", "1971-01-01 00:00:00", "bob2@bob.com")"#,
))
.await
.unwrap();
@@ -206,6 +207,69 @@ mod tests {
);
}
#[tokio::test]
async fn test_migration_to_v4() {
crate::infra::logging::init_for_tests();
let sql_pool = get_in_memory_db().await;
upgrade_to_v1(&sql_pool).await.unwrap();
migrate_from_version(&sql_pool, SchemaVersion(1), SchemaVersion(3))
.await
.unwrap();
sql_pool
.execute(raw_statement(
r#"INSERT INTO users (user_id, email, display_name, first_name, creation_date, uuid)
VALUES ("bob", "bob@bob.com", "", "", "1970-01-01 00:00:00", "a02eaf13-48a7-30f6-a3d4-040ff7c52b04")"#,
))
.await
.unwrap();
sql_pool
.execute(raw_statement(
r#"INSERT INTO users (user_id, email, display_name, first_name, creation_date, uuid)
VALUES ("bob2", "bob@bob.com", "", "", "1970-01-01 00:00:00", "986765a5-3f03-389e-b47b-536b2d6e1bec")"#,
))
.await
.unwrap();
error!(
"{}",
migrate_from_version(&sql_pool, SchemaVersion(3), SchemaVersion(4))
.await
.expect_err("migration should fail")
);
assert_eq!(
sql_migrations::JustSchemaVersion::find_by_statement(raw_statement(
r#"SELECT version FROM metadata"#
))
.one(&sql_pool)
.await
.unwrap()
.unwrap(),
sql_migrations::JustSchemaVersion {
version: SchemaVersion(3)
}
);
sql_pool
.execute(raw_statement(
r#"UPDATE users SET email = "new@bob.com" WHERE user_id = "bob2""#,
))
.await
.unwrap();
migrate_from_version(&sql_pool, SchemaVersion(3), SchemaVersion(4))
.await
.unwrap();
assert_eq!(
sql_migrations::JustSchemaVersion::find_by_statement(raw_statement(
r#"SELECT version FROM metadata"#
))
.one(&sql_pool)
.await
.unwrap()
.unwrap(),
sql_migrations::JustSchemaVersion {
version: SchemaVersion(4)
}
);
}
#[tokio::test]
async fn test_too_high_version() {
let sql_pool = get_in_memory_db().await;

View File

@@ -54,9 +54,16 @@ pub struct RunOpts {
/// Path to the file that contains the private server key.
/// It will be created if it doesn't exist.
/// Alternatively, you can set `server_key_seed`. If `server_key_seed` is given,
/// `server_key_file` will be ignored.
#[clap(long, env = "LLDAP_SERVER_KEY_FILE")]
pub server_key_file: Option<String>,
/// Seed used to generate the private server key.
/// Takes precedence over `server_key_file`.
#[clap(long, env = "LLDAP_SERVER_KEY_SEED")]
pub server_key_seed: Option<String>,
/// Change ldap host. Default: "0.0.0.0"
#[clap(long, env = "LLDAP_LDAP_HOST")]
pub ldap_host: Option<String>,

View File

@@ -17,7 +17,7 @@ use serde::{Deserialize, Serialize};
pub struct MailOptions {
#[builder(default = "false")]
pub enable_password_reset: bool,
#[builder(default = "None")]
#[builder(default)]
pub from: Option<Mailbox>,
#[builder(default = "None")]
pub reply_to: Option<Mailbox>,
@@ -25,7 +25,7 @@ pub struct MailOptions {
pub server: String,
#[builder(default = "587")]
pub port: u16,
#[builder(default = r#"String::default()"#)]
#[builder(default)]
pub user: String,
#[builder(default = r#"SecUtf8::from("")"#)]
pub password: SecUtf8,
@@ -78,7 +78,7 @@ pub struct Configuration {
pub ldap_base_dn: String,
#[builder(default = r#"UserId::new("admin")"#)]
pub ldap_user_dn: UserId,
#[builder(default = r#"String::default()"#)]
#[builder(default)]
pub ldap_user_email: String,
#[builder(default = r#"SecUtf8::from("password")"#)]
pub ldap_user_pass: SecUtf8,
@@ -92,6 +92,10 @@ pub struct Configuration {
pub verbose: bool,
#[builder(default = r#"String::from("server_key")"#)]
pub key_file: String,
// We want an Option to see whether there is a value or not, since the value is printed as
// "***SECRET***".
#[builder(default)]
pub key_seed: Option<SecUtf8>,
#[builder(default)]
pub smtp_options: MailOptions,
#[builder(default)]
@@ -111,7 +115,14 @@ impl std::default::Default for Configuration {
impl ConfigurationBuilder {
pub fn build(self) -> Result<Configuration> {
let server_setup = get_server_setup(self.key_file.as_deref().unwrap_or("server_key"))?;
let server_setup = get_server_setup(
self.key_file.as_deref().unwrap_or("server_key"),
self.key_seed
.as_ref()
.and_then(|o| o.as_ref())
.map(SecUtf8::unsecure)
.unwrap_or_default(),
)?;
Ok(self.server_setup(Some(server_setup)).private_build()?)
}
@@ -154,10 +165,25 @@ fn write_to_readonly_file(path: &std::path::Path, buffer: &[u8]) -> Result<()> {
Ok(file.write_all(buffer)?)
}
fn get_server_setup(file_path: &str) -> Result<ServerSetup> {
fn get_server_setup(file_path: &str, key_seed: &str) -> Result<ServerSetup> {
use std::fs::read;
let path = std::path::Path::new(file_path);
if path.exists() {
if !key_seed.is_empty() {
if file_path != "server_key" || path.exists() {
eprintln!("WARNING: A key_seed was given, we will ignore the server_key and generate one from the seed!");
} else {
println!("Got a key_seed, ignoring key_file");
}
let hash = |val: &[u8]| -> [u8; 32] {
use sha2::{Digest, Sha256};
let mut seed_hasher = Sha256::new();
seed_hasher.update(val);
seed_hasher.finalize().into()
};
use rand::SeedableRng;
let mut rng = rand_chacha::ChaCha20Rng::from_seed(hash(key_seed.as_bytes()));
Ok(ServerSetup::new(&mut rng))
} else if path.exists() {
let bytes = read(file_path).context(format!("Could not read key file `{}`", file_path))?;
Ok(ServerSetup::deserialize(&bytes)?)
} else {
@@ -198,6 +224,10 @@ impl ConfigOverrider for RunOpts {
config.key_file = path.to_string();
}
if let Some(seed) = self.server_key_seed.as_ref() {
config.key_seed = Some(SecUtf8::from(seed));
}
if let Some(port) = self.ldap_port {
config.ldap_port = port;
}
@@ -306,7 +336,14 @@ where
if config.verbose {
println!("Configuration: {:#?}", &config);
}
config.server_setup = Some(get_server_setup(&config.key_file)?);
config.server_setup = Some(get_server_setup(
&config.key_file,
config
.key_seed
.as_ref()
.map(SecUtf8::unsecure)
.unwrap_or_default(),
)?);
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.");
}
@@ -318,3 +355,29 @@ where
}
Ok(config)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn check_generated_server_key() {
assert_eq!(
bincode::serialize(&get_server_setup("/doesnt/exist", "key seed").unwrap()).unwrap(),
[
255, 206, 202, 50, 247, 13, 59, 191, 69, 244, 148, 187, 150, 227, 12, 250, 20, 207,
211, 151, 147, 33, 107, 132, 2, 252, 121, 94, 97, 6, 97, 232, 163, 168, 86, 246,
249, 186, 31, 204, 59, 75, 65, 134, 108, 159, 15, 70, 246, 250, 150, 195, 54, 197,
195, 176, 150, 200, 157, 119, 13, 173, 119, 8, 32, 0, 0, 0, 0, 0, 0, 0, 248, 123,
35, 91, 194, 51, 52, 57, 191, 210, 68, 227, 107, 166, 232, 37, 195, 244, 100, 84,
88, 212, 190, 12, 195, 57, 83, 72, 127, 189, 179, 16, 32, 0, 0, 0, 0, 0, 0, 0, 128,
112, 60, 207, 205, 69, 67, 73, 24, 175, 187, 62, 16, 45, 59, 136, 78, 40, 187, 54,
159, 94, 116, 33, 133, 119, 231, 43, 199, 164, 141, 7, 32, 0, 0, 0, 0, 0, 0, 0,
212, 134, 53, 203, 131, 24, 138, 211, 162, 28, 23, 233, 251, 82, 34, 66, 98, 12,
249, 205, 35, 208, 241, 50, 128, 131, 46, 189, 211, 51, 56, 109, 32, 0, 0, 0, 0, 0,
0, 0, 84, 20, 147, 25, 50, 5, 243, 203, 216, 180, 175, 121, 159, 96, 123, 183, 146,
251, 22, 44, 98, 168, 67, 224, 255, 139, 159, 25, 24, 254, 88, 3
]
);
}
}

View File

@@ -0,0 +1,27 @@
use crate::common::env;
use reqwest::blocking::Client;
pub fn get_token(client: &Client) -> String {
let username = env::admin_dn();
let password = env::admin_password();
let base_url = env::http_url();
let response = client
.post(format!("{base_url}/auth/simple/login"))
.header(reqwest::header::CONTENT_TYPE, "application/json")
.body(
serde_json::to_string(&lldap_auth::login::ClientSimpleLoginRequest {
username,
password,
})
.expect("Failed to encode the username/password as json to log in"),
)
.send()
.expect("Failed to send auth request")
.error_for_status()
.expect("Auth attempt failed");
serde_json::from_str::<lldap_auth::login::ServerLoginResponse>(
&response.text().expect("Failed to get response text"),
)
.expect("Failed to parse json")
.token
}

View File

@@ -0,0 +1,36 @@
#![allow(dead_code)]
use std::env::var;
pub const DB_KEY: &str = "LLDAP_DATABASE_URL";
pub fn database_url() -> String {
let url = var(DB_KEY).ok();
url.unwrap_or("sqlite://e2e_test.db?mode=rwc".to_string())
}
pub fn ldap_url() -> String {
let port = var("LLDAP_LDAP_PORT").ok();
let port = port.unwrap_or("3890".to_string());
format!("ldap://localhost:{}", port)
}
pub fn http_url() -> String {
let port = var("LLDAP_HTTP_PORT").ok();
let port = port.unwrap_or("17170".to_string());
format!("http://localhost:{}", port)
}
pub fn admin_dn() -> String {
let user = var("LLDAP_LDAP_USER_DN").ok();
user.unwrap_or("admin".to_string())
}
pub fn admin_password() -> String {
let pass = var("LLDAP_LDAP_USER_PASS").ok();
pass.unwrap_or("password".to_string())
}
pub fn base_dn() -> String {
let dn = var("LLDAP_LDAP_BASE_DN").ok();
dn.unwrap_or("dc=example,dc=com".to_string())
}

View File

@@ -0,0 +1,240 @@
use crate::common::{
auth::get_token,
env,
graphql::{
add_user_to_group, create_group, create_user, delete_group_query, delete_user_query, post,
AddUserToGroup, CreateGroup, CreateUser, DeleteGroupQuery, DeleteUserQuery,
},
};
use assert_cmd::prelude::*;
use nix::{
sys::signal::{self, Signal},
unistd::Pid,
};
use reqwest::blocking::{Client, ClientBuilder};
use std::collections::{HashMap, HashSet};
use std::process::{Child as ChildProcess, Command};
use std::{fs::canonicalize, thread, time::Duration};
use uuid::Uuid;
#[derive(Clone)]
pub struct User {
pub username: String,
pub groups: Vec<String>,
}
impl User {
pub fn new(username: &str, groups: Vec<&str>) -> Self {
let username = username.to_owned();
let groups = groups.iter().map(|username| username.to_string()).collect();
Self { username, groups }
}
}
pub struct LLDAPFixture {
token: String,
client: Client,
child: ChildProcess,
users: HashSet<String>,
groups: HashMap<String, i64>,
}
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 mut started = false;
for _ in 0..MAX_HEALTHCHECK_ATTEMPS {
let status = create_lldap_command()
.arg("healthcheck")
.status()
.expect("healthcheck fail");
if status.success() {
started = true;
break;
}
thread::sleep(Duration::from_millis(1000));
}
assert!(started);
let client = ClientBuilder::new()
.connect_timeout(std::time::Duration::from_secs(2))
.timeout(std::time::Duration::from_secs(5))
.redirect(reqwest::redirect::Policy::none())
.build()
.expect("failed to make http client");
let token = get_token(&client);
Self {
client,
token,
child,
users: HashSet::new(),
groups: HashMap::new(),
}
}
pub fn load_state(&mut self, state: &Vec<User>) {
let mut users: HashSet<String> = HashSet::new();
let mut groups: HashSet<String> = HashSet::new();
for user in state {
users.insert(user.username.clone());
groups.extend(user.groups.clone());
}
for user in &users {
self.add_user(user);
}
for group in &groups {
self.add_group(group);
}
for User { username, groups } in state {
for group in groups {
self.add_user_to_group(username, group);
}
}
}
fn add_user(&mut self, user: &String) {
post::<CreateUser>(
&self.client,
&self.token,
create_user::Variables {
user: create_user::CreateUserInput {
id: user.clone(),
email: format!("{}@lldap.test", user),
avatar: None,
display_name: None,
first_name: None,
last_name: None,
},
},
)
.expect("failed to add user");
self.users.insert(user.clone());
}
fn add_group(&mut self, group: &str) {
let id = post::<CreateGroup>(
&self.client,
&self.token,
create_group::Variables {
name: group.to_owned(),
},
)
.expect("failed to add group")
.create_group
.id;
self.groups.insert(group.to_owned(), id);
}
fn delete_user(&mut self, user: &String) {
post::<DeleteUserQuery>(
&self.client,
&self.token,
delete_user_query::Variables { user: user.clone() },
)
.expect("failed to delete user");
self.users.remove(user);
}
fn delete_group(&mut self, group: &String) {
let group_id = self.groups.get(group).unwrap();
post::<DeleteGroupQuery>(
&self.client,
&self.token,
delete_group_query::Variables {
group_id: *group_id,
},
)
.expect("failed to delete group");
self.groups.remove(group);
}
fn add_user_to_group(&mut self, user: &str, group: &String) {
let group_id = self.groups.get(group).unwrap();
post::<AddUserToGroup>(
&self.client,
&self.token,
add_user_to_group::Variables {
user: user.to_owned(),
group: *group_id,
},
)
.expect("failed to add user to group");
}
}
impl Drop for LLDAPFixture {
fn drop(&mut self) {
let users = self.users.clone();
for user in users {
self.delete_user(&user);
}
let groups = self.groups.clone();
for group in groups.keys() {
self.delete_group(group);
}
let result = signal::kill(
Pid::from_raw(self.child.id().try_into().unwrap()),
Signal::SIGTERM,
);
if let Err(err) = result {
println!("Failed to send kill signal: {:?}", err);
let _ = self
.child
.kill()
.map_err(|err| println!("Failed to kill LLDAP: {:?}", err));
return;
}
for _ in 0..10 {
let status = self.child.try_wait();
if status.is_err() {}
match status {
Err(e) => {
println!(
"Failed to get status while waiting for graceful exit: {}",
e
);
break;
}
Ok(None) => {
println!("LLDAP still running, sleeping for 1 second.");
}
Ok(Some(status)) => {
if !status.success() {
println!("LLDAP exited with status {}", status)
}
return;
}
}
thread::sleep(Duration::from_millis(1000));
}
println!("LLDAP alive after 10 seconds, forcing exit.");
let _ = self
.child
.kill()
.map_err(|err| println!("Failed to kill LLDAP: {:?}", err));
}
}
pub fn new_id(prefix: Option<&str>) -> String {
let id = Uuid::new_v4();
let id = format!("{}-lldap-test", id.simple());
match prefix {
Some(prefix) => format!("{}{}", prefix, id),
None => id,
}
}
fn create_lldap_command() -> 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");
let db_url = env::database_url();
cmd.current_dir(path);
cmd.env(env::DB_KEY, db_url);
cmd
}

View File

@@ -0,0 +1,121 @@
use crate::common::env;
use anyhow::{anyhow, Context, Result};
use graphql_client::GraphQLQuery;
use reqwest::blocking::Client;
pub type DateTimeUtc = chrono::DateTime<chrono::Utc>;
#[derive(GraphQLQuery)]
#[graphql(
schema_path = "../schema.graphql",
query_path = "tests/queries/add_user_to_group.graphql",
response_derives = "Debug",
variables_derives = "Debug,Clone",
custom_scalars_module = "crate::common::graphql"
)]
pub struct AddUserToGroup;
#[derive(GraphQLQuery)]
#[graphql(
schema_path = "../schema.graphql",
query_path = "tests/queries/create_user.graphql",
response_derives = "Debug",
variables_derives = "Debug,Clone",
custom_scalars_module = "crate::common::graphql"
)]
pub struct CreateUser;
#[derive(GraphQLQuery)]
#[graphql(
schema_path = "../schema.graphql",
query_path = "tests/queries/create_group.graphql",
response_derives = "Debug",
variables_derives = "Debug,Clone",
custom_scalars_module = "crate::common::graphql"
)]
pub struct CreateGroup;
#[derive(GraphQLQuery)]
#[graphql(
schema_path = "../schema.graphql",
query_path = "tests/queries/list_users.graphql",
response_derives = "Debug",
custom_scalars_module = "crate::common::graphql"
)]
pub struct ListUsers;
#[derive(GraphQLQuery)]
#[graphql(
schema_path = "../schema.graphql",
query_path = "tests/queries/get_user_details.graphql",
response_derives = "Debug",
variables_derives = "Debug,Clone",
custom_scalars_module = "crate::common::graphql"
)]
pub struct GetUserDetails;
#[derive(GraphQLQuery)]
#[graphql(
schema_path = "../schema.graphql",
query_path = "tests/queries/list_groups.graphql",
response_derives = "Debug",
custom_scalars_module = "crate::common::graphql"
)]
pub struct ListGroups;
#[derive(GraphQLQuery)]
#[graphql(
schema_path = "../schema.graphql",
query_path = "tests/queries/delete_group.graphql",
response_derives = "Debug",
custom_scalars_module = "crate::common::graphql"
)]
pub struct DeleteGroupQuery;
#[derive(GraphQLQuery)]
#[graphql(
schema_path = "../schema.graphql",
query_path = "tests/queries/delete_user.graphql",
response_derives = "Debug",
custom_scalars_module = "crate::common::graphql"
)]
pub struct DeleteUserQuery;
pub fn post<QueryType>(
client: &Client,
token: &String,
variables: QueryType::Variables,
) -> Result<QueryType::ResponseData>
where
QueryType: GraphQLQuery + 'static,
{
let unwrap_graphql_response = |graphql_client::Response { data, errors, .. }| {
data.ok_or_else(|| {
anyhow!(
"Errors: [{}]",
errors
.unwrap_or_default()
.iter()
.map(ToString::to_string)
.collect::<Vec<_>>()
.join(", ")
)
})
};
let url = env::http_url() + "/api/graphql";
let auth_header = format!("Bearer {}", token);
client
.post(url)
.header(reqwest::header::AUTHORIZATION, auth_header)
// Request body.
.json(&QueryType::build_query(variables))
.send()
.context("while sending a request to the LLDAP server")?
.error_for_status()
.context("error from an LLDAP response")?
// Parse response as Json.
.json::<graphql_client::Response<QueryType::ResponseData>>()
.context("while parsing backend response")
.and_then(unwrap_graphql_response)
.context("GraphQL error from an LLDAP response")
}

View File

@@ -0,0 +1,4 @@
pub mod auth;
pub mod env;
pub mod fixture;
pub mod graphql;

70
server/tests/graphql.rs Normal file
View File

@@ -0,0 +1,70 @@
use crate::common::{
auth::get_token,
env,
fixture::{new_id, LLDAPFixture, User},
graphql::{get_user_details, list_users, post, GetUserDetails, ListUsers},
};
use reqwest::blocking::ClientBuilder;
use serial_test::file_serial;
use std::collections::HashSet;
mod common;
#[test]
#[file_serial]
fn list_users() {
let mut fixture = LLDAPFixture::new();
let prefix = "graphql-list_users-";
let user1_name = new_id(Some(prefix));
let user2_name = new_id(Some(prefix));
let user3_name = new_id(Some(prefix));
let group1_name = new_id(Some(prefix));
let group2_name = new_id(Some(prefix));
let initial_state = vec![
User::new(&user1_name, vec![&group1_name]),
User::new(&user2_name, vec![&group1_name, &group2_name]),
User::new(&user3_name, vec![]),
];
fixture.load_state(&initial_state);
let client = ClientBuilder::new()
.connect_timeout(std::time::Duration::from_secs(2))
.timeout(std::time::Duration::from_secs(5))
.redirect(reqwest::redirect::Policy::none())
.build()
.expect("failed to make http client");
let token = get_token(&client);
let result =
post::<ListUsers>(&client, &token, list_users::Variables {}).expect("failed to list users");
let users: HashSet<String> = result.users.iter().map(|user| user.id.clone()).collect();
assert!(users.contains(&user1_name));
assert!(users.contains(&user2_name));
assert!(users.contains(&user3_name));
}
#[test]
#[file_serial]
fn get_admin() {
let mut _fixture = LLDAPFixture::new();
let client = ClientBuilder::new()
.connect_timeout(std::time::Duration::from_secs(2))
.timeout(std::time::Duration::from_secs(5))
.redirect(reqwest::redirect::Policy::none())
.build()
.expect("failed to make http client");
let admin_name = env::admin_dn();
let admin_group_name = "lldap_admin";
let token = get_token(&client);
let result = post::<GetUserDetails>(
&client,
&token,
get_user_details::Variables { id: admin_name },
)
.expect("failed to get admin");
let admin_groups: HashSet<String> = result
.user
.groups
.iter()
.map(|group| group.display_name.clone())
.collect();
assert!(admin_groups.contains(admin_group_name));
}

View File

@@ -0,0 +1,57 @@
use std::collections::HashSet;
use crate::common::{
env,
fixture::{new_id, LLDAPFixture, User},
};
use ldap3::{LdapConn, Scope, SearchEntry};
use serial_test::file_serial;
mod common;
#[test]
#[file_serial]
fn gitea() {
let mut fixture = LLDAPFixture::new();
let gitea_user_group = new_id(Some("gitea_user-"));
let gitea_admin_group = new_id(Some("gitea_admin-"));
let gitea_user1 = new_id(Some("gitea1-"));
let gitea_user2 = new_id(Some("gitea2-"));
let gitea_user3 = new_id(Some("gitea3-"));
let initial_state = vec![
User::new(&gitea_user1, vec![&gitea_user_group, &gitea_admin_group]),
User::new(&gitea_user2, vec![&gitea_user_group]),
User::new(&gitea_user3, vec![]),
];
fixture.load_state(&initial_state);
let mut ldap =
LdapConn::new(env::ldap_url().as_str()).expect("failed to create ldap connection");
let base_dn = env::base_dn();
let bind_dn = format!("uid={},ou=people,{}", env::admin_dn(), base_dn);
ldap.simple_bind(bind_dn.as_str(), env::admin_password().as_str())
.expect("failed to bind to ldap");
let user_base = format!("ou=people,{}", base_dn);
let attrs = vec!["uid", "givenName", "sn", "mail", "jpegPhoto"];
let results = ldap
.search(
user_base.as_str(),
Scope::Subtree,
format!("(memberof=cn={},ou=groups,{})", gitea_user_group, base_dn).as_str(),
attrs,
)
.expect("failed to find gitea users")
.success()
.expect("failed to get gitea user results")
.0;
let mut found_users: HashSet<String> = HashSet::new();
for result in results {
let attrs = SearchEntry::construct(result).attrs;
let user = attrs.get("uid").unwrap().get(0).unwrap();
found_users.insert(user.clone());
}
assert!(found_users.contains(&gitea_user1));
assert!(found_users.contains(&gitea_user2));
assert!(!found_users.contains(&gitea_user3));
ldap.unbind().expect("failed to unbind ldap connection");
}

112
server/tests/ldap.rs Normal file
View File

@@ -0,0 +1,112 @@
use std::collections::{HashMap, HashSet};
use crate::common::{
env,
fixture::{new_id, LLDAPFixture, User},
};
use ldap3::{LdapConn, Scope, SearchEntry, SearchResult};
use serial_test::file_serial;
mod common;
#[test]
#[file_serial]
fn basic_users_search() {
let mut fixture = LLDAPFixture::new();
let prefix = "ldap-basic_users_search-";
let user1_name = new_id(Some(prefix));
let user2_name = new_id(Some(prefix));
let user3_name = new_id(Some(prefix));
let group1_name = new_id(Some(prefix));
let group2_name = new_id(Some(prefix));
let initial_state = vec![
User::new(&user1_name, vec![&group1_name]),
User::new(&user2_name, vec![&group1_name, &group2_name]),
User::new(&user3_name, vec![]),
];
fixture.load_state(&initial_state);
let mut ldap =
LdapConn::new(env::ldap_url().as_str()).expect("failed to create ldap connection");
let base_dn = env::base_dn();
let bind_dn = format!("uid={},ou=people,{}", env::admin_dn(), base_dn);
ldap.simple_bind(bind_dn.as_str(), env::admin_password().as_str())
.expect("failed to bind to ldap");
let attrs = vec!["uid", "memberof"];
let found_users = get_users_and_groups(
ldap.search(
env::base_dn().as_str(),
Scope::Subtree,
"(objectclass=person)",
attrs,
)
.expect("failed to find users"),
);
assert!(found_users.contains_key(&user1_name));
assert!(found_users
.get(&user1_name)
.unwrap()
.contains(format!("cn={},ou=groups,{}", &group1_name, base_dn).as_str()));
assert!(found_users.contains_key(&user2_name));
assert!(found_users
.get(&user2_name)
.unwrap()
.contains(format!("cn={},ou=groups,{}", &group1_name, base_dn).as_str()));
assert!(found_users
.get(&user2_name)
.unwrap()
.contains(format!("cn={},ou=groups,{}", &group2_name, base_dn).as_str()));
assert!(found_users.contains_key(&user3_name));
assert!(found_users.get(&user3_name).unwrap().is_empty());
ldap.unbind().expect("failed to unbind ldap connection");
}
#[test]
#[file_serial]
fn admin_search() {
let mut _fixture = LLDAPFixture::new();
let mut ldap =
LdapConn::new(env::ldap_url().as_str()).expect("failed to create ldap connection");
let base_dn = env::base_dn();
let bind_dn = format!("uid={},ou=people,{}", env::admin_dn(), base_dn);
ldap.simple_bind(bind_dn.as_str(), env::admin_password().as_str())
.expect("failed to bind to ldap");
let attrs = vec!["uid", "memberof"];
let admin_name = env::admin_dn();
let admin_group_name = "lldap_admin";
let found_users = get_users_and_groups(
ldap.search(
env::base_dn().as_str(),
Scope::Subtree,
format!("(&(objectclass=person)(uid={}))", admin_name).as_str(),
attrs,
)
.expect("failed to find admin"),
);
assert!(found_users.contains_key(&admin_name));
assert!(found_users
.get(&admin_name)
.unwrap()
.contains(format!("cn={},ou=groups,{}", admin_group_name, base_dn).as_str()));
ldap.unbind().expect("failed to unbind ldap connection");
}
fn get_users_and_groups(results: SearchResult) -> HashMap<String, HashSet<String>> {
let results = results
.success()
.expect("failed to get successful result")
.0;
let mut found_users: HashMap<String, HashSet<String>> = HashMap::new();
for result in results {
let attrs = SearchEntry::construct(result).attrs;
let user = attrs.get("uid").unwrap().get(0).unwrap();
let user_groups = attrs.get("memberof").unwrap().clone();
let mut groups: HashSet<String> = HashSet::new();
groups.extend(user_groups.clone());
found_users.insert(user.clone(), groups);
}
found_users
}

View File

@@ -0,0 +1,5 @@
mutation AddUserToGroup($user: String!, $group: Int!) {
addUserToGroup(userId: $user, groupId: $group) {
ok
}
}

View File

@@ -0,0 +1,6 @@
mutation CreateGroup($name: String!) {
createGroup(name: $name) {
id
displayName
}
}

View File

@@ -0,0 +1,5 @@
mutation CreateUser($user: CreateUserInput!) {
createUser(user: $user) {
id
}
}

View File

@@ -0,0 +1,5 @@
mutation DeleteGroupQuery($groupId: Int!) {
deleteGroup(groupId: $groupId) {
ok
}
}

View File

@@ -0,0 +1,5 @@
mutation DeleteUserQuery($user: String!) {
deleteUser(userId: $user) {
ok
}
}

View File

@@ -0,0 +1,16 @@
query GetUserDetails($id: String!) {
user(userId: $id) {
id
email
displayName
firstName
lastName
avatar
creationDate
uuid
groups {
id
displayName
}
}
}

View File

@@ -0,0 +1,9 @@
query ListGroups {
groups {
id
displayName
users {
id
}
}
}

View File

@@ -0,0 +1,5 @@
query ListUsers {
users(filters: null) {
id
}
}

View File

@@ -1,12 +1,17 @@
[package]
name = "lldap_set_password"
version = "0.1.0"
authors = ["Valentin Tolmer <valentin@tolmer.fr>"]
description = "CLI tool to set a user password in LLDAP"
edition = "2021"
homepage = "https://github.com/lldap/lldap"
license = "GPL-3.0-only"
name = "lldap_set_password"
repository = "https://github.com/lldap/lldap"
version = "0.1.0"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
anyhow = "*"
anyhow = "1"
rand = "0.8"
serde = "1"
serde_json = "1"
@@ -16,10 +21,10 @@ features = ["std", "color", "suggestions", "derive", "env"]
version = "4"
[dependencies.lldap_auth]
path = "../auth"
version = "0.3"
features = ["opaque_client"]
[dependencies.reqwest]
version = "*"
version = "0.11"
default-features = false
features = ["json", "blocking", "rustls-tls"]