diff --git a/Cargo.lock b/Cargo.lock index bd10531..696a646 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,12 +2,6 @@ # It is not intended for manual editing. version = 4 -[[package]] -name = "adler2" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" - [[package]] name = "argh" version = "0.1.19" @@ -39,130 +33,12 @@ dependencies = [ "serde", ] -[[package]] -name = "async-trait" -version = "0.1.92" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82f6aeea286b8eb4dd3431a1be1b59d290ace00f5bfd8e2a159bc2a05e2c1667" -dependencies = [ - "proc-macro2", - "quote", - "syn 3.0.4", -] - -[[package]] -name = "atomic-waker" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" - -[[package]] -name = "axum" -version = "0.8.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31b698c5f9a010f6573133b09e0de5408834d0c82f8d7475a89fc1867a71cd90" -dependencies = [ - "axum-core", - "axum-macros", - "bytes", - "form_urlencoded", - "futures-util", - "http", - "http-body", - "http-body-util", - "hyper", - "hyper-util", - "itoa", - "matchit", - "memchr", - "mime", - "percent-encoding", - "pin-project-lite", - "serde_core", - "serde_json", - "serde_path_to_error", - "serde_urlencoded", - "sync_wrapper", - "tokio", - "tower", - "tower-layer", - "tower-service", - "tracing", -] - -[[package]] -name = "axum-core" -version = "0.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08c78f31d7b1291f7ee735c1c6780ccde7785daae9a9206026862dab7d8792d1" -dependencies = [ - "bytes", - "futures-core", - "http", - "http-body", - "http-body-util", - "mime", - "pin-project-lite", - "sync_wrapper", - "tower-layer", - "tower-service", - "tracing", -] - -[[package]] -name = "axum-extra" -version = "0.12.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be44683b41ccb9ab2d23a5230015c9c3c55be97a25e4428366de8873103f7970" -dependencies = [ - "axum", - "axum-core", - "bytes", - "cookie", - "futures-core", - "futures-util", - "http", - "http-body", - "http-body-util", - "mime", - "pin-project-lite", - "tower-layer", - "tower-service", - "tracing", -] - -[[package]] -name = "axum-macros" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7aa268c23bfbbd2c4363b9cd302a4f504fb2a9dfe7e3451d66f35dd392e20aca" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.119", -] - [[package]] name = "base64" version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" -[[package]] -name = "bitflags" -version = "2.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b588b76d00fde79687d7646a9b5bdf3cc0f655e0bbd080335a95d7e96f3587da" - -[[package]] -name = "block-buffer" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2f6c7dbe95a6ed67ad9f18e57daf93a2f034c524b99fd2b76d18fdfeb6660aa" -dependencies = [ - "hybrid-array", -] - [[package]] name = "bumpalo" version = "3.20.3" @@ -180,21 +56,6 @@ name = "camino" version = "1.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bb1307f12aa967b5a58416e87b3653360e0fd614a016b6e970db08fecbb1b80d" -dependencies = [ - "serde_core", -] - -[[package]] -name = "cc" -version = "1.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "005ec2760ca554fae18df7a11195552ec576cd665632a881bc011d5bb2fd4d80" -dependencies = [ - "find-msvc-tools", - "jobserver", - "libc", - "shlex", -] [[package]] name = "cfg-if" @@ -202,112 +63,10 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" -[[package]] -name = "const-oid" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6ef517f0926dd24a1582492c791b6a4818a4d94e789a334894aa15b0d12f55c" - -[[package]] -name = "cookie" -version = "0.18.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a373e3602691c3cdea496d2f0ee5935151e6168fe87739483c463db1b2f2f87" -dependencies = [ - "percent-encoding", - "time", - "version_check", -] - -[[package]] -name = "cpufeatures" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ca28b0ae3115b884660db4118d803791fd6756b6e88f39c0f3f7859060d7566" -dependencies = [ - "libc", -] - -[[package]] -name = "crc32fast" -version = "1.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8498c871161e1742aaa9d52551b2d6ebdd4c3d45a3be423e3728f33b955be550" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "crypto-common" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce6e4c961d6cd6c9a86db418387425e8bdeaf05b3c8bc1411e6dca4c252f1453" -dependencies = [ - "hybrid-array", -] - -[[package]] -name = "deranged" -version = "0.5.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" - -[[package]] -name = "digest" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1dd6dbb5841937940781866fa1281a1ff7bd3bf827091440879f9994983d5c2" -dependencies = [ - "block-buffer", - "const-oid", - "crypto-common", -] - -[[package]] -name = "display_full_error" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc7c47e9a2fd28a8edd1446f10dabe8ac5d26bb77ed2b1077bfcd8308904e8c6" - [[package]] name = "dn_escape" version = "0.1.0" -[[package]] -name = "find-msvc-tools" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e0f1c7c3a72c66fd80abe965175f7523475c0489a87d3ff9d6e8c87d87a9d2d" - -[[package]] -name = "flate2" -version = "1.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e634e2e0ebac1ee034020da1ca582e17ffe4e0f5e985823721e168928136dcb" -dependencies = [ - "crc32fast", - "miniz_oxide", - "zlib-rs", -] - -[[package]] -name = "form_urlencoded" -version = "1.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" -dependencies = [ - "percent-encoding", -] - -[[package]] -name = "futures-channel" -version = "0.3.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1f9e3d69d39e4862ffed03ed071a76f9a13ba1d9109d355b0f0aa6b15e393c4" -dependencies = [ - "futures-core", -] - [[package]] name = "futures-core" version = "0.3.34" @@ -362,123 +121,6 @@ dependencies = [ "r-efi", ] -[[package]] -name = "glob" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4eba85ea1d0a966a983acd07deee566e67395d2d96b6fb39e62b5a833f1eb0b" - -[[package]] -name = "http" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "918d3568bebf352712bc2ef3d46a8bcf1a75b373be6539de198e9105cbbf9ce0" -dependencies = [ - "bytes", - "itoa", -] - -[[package]] -name = "http-body" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca2a8f2913ee65f60facd6a5905613afaa448497a0230cc41ce022d93290bc2c" -dependencies = [ - "bytes", - "http", -] - -[[package]] -name = "http-body-util" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23169fe34a5fbcdd3f3862e78fb9b6fccd5f02a6dc6f732547005d45631ce71c" -dependencies = [ - "bytes", - "futures-core", - "http", - "http-body", - "pin-project-lite", -] - -[[package]] -name = "http-range-header" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9171a2ea8a68358193d15dd5d70c1c10a2afc3e7e4c5bc92bc9f025cebd7359c" - -[[package]] -name = "httparse" -version = "1.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" - -[[package]] -name = "httpdate" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" - -[[package]] -name = "hybrid-array" -version = "0.4.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "707114b52a152fa7bdb290cd7cd5912d9467273b6d74e21b8d81aca1f8533f6b" -dependencies = [ - "typenum", -] - -[[package]] -name = "hyper" -version = "1.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27b501faa50e7a26c3d3560ca625132f4078a17771f4810baf70475ae48cbe43" -dependencies = [ - "atomic-waker", - "bytes", - "futures-channel", - "futures-core", - "http", - "http-body", - "httparse", - "httpdate", - "itoa", - "pin-project-lite", - "smallvec", - "tokio", -] - -[[package]] -name = "hyper-util" -version = "0.1.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0" -dependencies = [ - "bytes", - "http", - "http-body", - "hyper", - "pin-project-lite", - "tokio", - "tower-service", -] - -[[package]] -name = "itoa" -version = "1.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" - -[[package]] -name = "jobserver" -version = "0.1.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c00acbd29eabad4a2392fa0e921c874934dbbf4194312ad20f04a0ed67a3cb3" -dependencies = [ - "getrandom", - "libc", -] - [[package]] name = "js-sys" version = "0.3.104" @@ -534,23 +176,13 @@ name = "llldap" version = "0.1.0" dependencies = [ "argh", - "async-trait", - "axum", - "axum-extra", "camino", "dn_escape", "futures-util", - "http", "ldap3_proto", - "minijinja", - "minijinja-embed", "serde", - "serde_json", - "static-serve", "tokio", "tokio-util", - "tower", - "tower-http", "tracing", "tracing-subscriber", "uuid", @@ -562,72 +194,18 @@ version = "0.4.34" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f9f8bd3e56ce4dfc153cf470fffbfa98c7620958b312ca5c3a4b8d5181fd13c6" -[[package]] -name = "matchit" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3" - [[package]] name = "memchr" version = "2.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98" -[[package]] -name = "memo-map" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5449c8c750f1a07ea702bbd212bd999fceece9b3d1508b17023b3e174583124b" - -[[package]] -name = "mime" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" - -[[package]] -name = "mime_guess" -version = "2.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e" -dependencies = [ - "mime", - "unicase", -] - -[[package]] -name = "minijinja" -version = "2.24.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86886cf6dbf4e614b19c9a1eec9775f021869d7eadde0fc73921a81b90c9b4c9" -dependencies = [ - "memo-map", - "serde", -] - -[[package]] -name = "minijinja-embed" -version = "2.24.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2795289c499a1331f73166ff8b8e0e68fdc81ef0b385eecb0e4de96f1638b66" - [[package]] name = "minimal-lexical" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" -[[package]] -name = "miniz_oxide" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b63fbc4a50860e98e7b2aa7804ded1db5cbc3aff9193adaff57a6931bf7c4b4c" -dependencies = [ - "adler2", - "simd-adler32", -] - [[package]] name = "mio" version = "1.2.2" @@ -658,12 +236,6 @@ dependencies = [ "windows-sys", ] -[[package]] -name = "num-conv" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "521739c6d2bac4aa25192232afe6841231376b2b26d4d9fae5ecf8ca5772e441" - [[package]] name = "once_cell" version = "1.21.4" @@ -697,30 +269,12 @@ version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7011d97b484a5ebdc4b1fdb3b12d5e4bbbea56e9d22b688f2e79e04b65a7d8a6" -[[package]] -name = "percent-encoding" -version = "2.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" - [[package]] name = "pin-project-lite" version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" -[[package]] -name = "pkg-config" -version = "0.3.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6b464fbc74e149a392436b17d523f769e057cb6877f6a5c4618bc6f11800548" - -[[package]] -name = "powerfmt" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" - [[package]] name = "proc-macro2" version = "1.0.107" @@ -745,30 +299,12 @@ version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" -[[package]] -name = "range-requests" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e30cea6780132baa8257e81ab3f6e11526a9271e9032963eab8d830972e68b6" -dependencies = [ - "axum-core", - "bytes", - "http", - "thiserror", -] - [[package]] name = "rustversion" version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f" -[[package]] -name = "ryu" -version = "1.0.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" - [[package]] name = "serde" version = "1.0.229" @@ -799,53 +335,6 @@ dependencies = [ "syn 3.0.4", ] -[[package]] -name = "serde_json" -version = "1.0.151" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c841b55ecdae098c80dcae9cf767f6f8a0c2cdb3416bbef72181df4d0fe73f14" -dependencies = [ - "itoa", - "memchr", - "serde", - "serde_core", - "zmij", -] - -[[package]] -name = "serde_path_to_error" -version = "0.1.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10a9ff822e371bb5403e391ecd83e182e0e77ba7f6fe0160b795797109d1b457" -dependencies = [ - "itoa", - "serde", - "serde_core", -] - -[[package]] -name = "serde_urlencoded" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" -dependencies = [ - "form_urlencoded", - "itoa", - "ryu", - "serde", -] - -[[package]] -name = "sha2" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "446ba717509524cb3f22f17ecc096f10f4822d76ab5c0b9822c5f9c284e825f4" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest", -] - [[package]] name = "sharded-slab" version = "0.1.7" @@ -855,18 +344,6 @@ dependencies = [ "lazy_static", ] -[[package]] -name = "shlex" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" - -[[package]] -name = "simd-adler32" -version = "0.3.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a219298ac11a56ea9a6d2120044824d6f01aeb034955e7af7bc16858527deea" - [[package]] name = "slab" version = "0.4.12" @@ -889,36 +366,6 @@ dependencies = [ "windows-sys", ] -[[package]] -name = "static-serve" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49c7a4b3dbe44375f7e30895c04d3ca4fde020eb7336c0f6a71ed495c5302e1a" -dependencies = [ - "axum", - "bytes", - "range-requests", - "static-serve-macro", -] - -[[package]] -name = "static-serve-macro" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cb887d817cf39e80fd7fdd21c4f138c8277d7dc2623fee4d07e141f4ce32a0a" -dependencies = [ - "display_full_error", - "flate2", - "glob", - "mime_guess", - "proc-macro2", - "quote", - "sha2", - "syn 3.0.4", - "thiserror", - "zstd", -] - [[package]] name = "syn" version = "2.0.119" @@ -941,12 +388,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "sync_wrapper" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" - [[package]] name = "thiserror" version = "2.0.20" @@ -976,43 +417,12 @@ dependencies = [ "cfg-if", ] -[[package]] -name = "time" -version = "0.3.55" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdb87b95ec50ddfa440816d227a17b2ccbdda963a316a727fda0fc4334f7d134" -dependencies = [ - "deranged", - "num-conv", - "powerfmt", - "serde_core", - "time-core", - "time-macros", -] - -[[package]] -name = "time-core" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1c906769ad99c88eaa54e728060edef082f8e358ff32030cb7c7d315e81109" - -[[package]] -name = "time-macros" -version = "0.2.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e689342a48d2ea927c87ea50cabf8594854bf940e9310208848d680d668ed85" -dependencies = [ - "num-conv", - "time-core", -] - [[package]] name = "tokio" version = "1.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "202caea871b69668250d242070849eb495be178ed697a3e98aebce5bc81a0bed" dependencies = [ - "bytes", "libc", "mio", "pin-project-lite", @@ -1046,66 +456,12 @@ dependencies = [ "tokio", ] -[[package]] -name = "tower" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4" -dependencies = [ - "futures-core", - "futures-util", - "pin-project-lite", - "sync_wrapper", - "tokio", - "tower-layer", - "tower-service", - "tracing", -] - -[[package]] -name = "tower-http" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08a05a66a4fdd61cbbe0a1d755ffe0ca6aba159dd4820936a0ff8a8278245b9c" -dependencies = [ - "bitflags", - "bytes", - "futures-core", - "futures-util", - "http", - "http-body", - "http-body-util", - "http-range-header", - "httpdate", - "mime", - "mime_guess", - "percent-encoding", - "pin-project-lite", - "tokio", - "tokio-util", - "tower-layer", - "tower-service", -] - -[[package]] -name = "tower-layer" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" - -[[package]] -name = "tower-service" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" - [[package]] name = "tracing" version = "0.1.44" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" dependencies = [ - "log", "pin-project-lite", "tracing-attributes", "tracing-core", @@ -1157,18 +513,6 @@ dependencies = [ "tracing-log", ] -[[package]] -name = "typenum" -version = "1.20.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20" - -[[package]] -name = "unicase" -version = "2.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbc4bc3a9f746d862c45cb89d705aa10f187bb96c76001afab07a0d35ce60142" - [[package]] name = "unicode-ident" version = "1.0.24" @@ -1192,12 +536,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" -[[package]] -name = "version_check" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" - [[package]] name = "wasi" version = "0.11.1+wasi-snapshot-preview1" @@ -1263,43 +601,3 @@ checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" dependencies = [ "windows-link", ] - -[[package]] -name = "zlib-rs" -version = "0.6.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34b31d188d9d685a4f9c7b46d6e36631b07058d2cfe190267adce54dc230bf12" - -[[package]] -name = "zmij" -version = "1.0.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b" - -[[package]] -name = "zstd" -version = "0.13.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e91ee311a569c327171651566e07972200e76fcfe2242a4fa446149a3881c08a" -dependencies = [ - "zstd-safe", -] - -[[package]] -name = "zstd-safe" -version = "7.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f49c4d5f0abb602a93fb8736af2a4f4dd9512e36f7f570d66e65ff867ed3b9d" -dependencies = [ - "zstd-sys", -] - -[[package]] -name = "zstd-sys" -version = "2.0.16+zstd.1.5.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91e19ebc2adc8f83e43039e79776e3fda8ca919132d68a1fed6a5faca2683748" -dependencies = [ - "cc", - "pkg-config", -] diff --git a/Cargo.toml b/Cargo.toml index 07c7928..0ea53c5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,23 +5,13 @@ edition = "2024" [dependencies] argh = "0.1.19" -async-trait = "0.1.92" -axum = { version = "0.8.9", optional = true, features = ["macros"] } -axum-extra = { version = "0.12.6", features = ["cookie"], optional = true } -camino = { version = "1.2.5", features = ["serde1"] } +camino = "1.2.5" dn_escape = { path = "vendor/dn_escape" } futures-util = { version = "0.3.34", features = ["sink"] } -http = { version = "1.5.0", optional = true } ldap3_proto = "0.8.1" -minijinja = { version = "2.24.0", optional = true } -minijinja-embed = { version = "2.24.0", optional = true } serde = { version = "1.0.229", features = ["derive"] } -serde_json = "1.0.151" -static-serve = { version = "0.6.3", optional = true } tokio = { version = "1.53.1", features = ["macros", "net", "rt", "time", "sync"] } tokio-util = { version = "0.7.19", features = ["codec"] } -tower = "0.5.3" -tower-http = { version = "0.7.1", features = ["fs"], optional = true } tracing = "0.1.44" tracing-subscriber = "0.3.23" uuid = { version = "1.26.0", features = ["v4"] } @@ -32,14 +22,3 @@ complexity = "deny" pedantic = "deny" perf = "deny" style = "deny" - -[features] -default = [ "http", "noembed" ] -# Embed assets and templates in binary -embed = [ "dep:minijinja-embed", "dep:static-serve" ] -# Don't embed assets and templates in binary -noembed = [ "dep:tower-http" ] -http = ["dep:axum", "dep:axum-extra", "dep:http", "dep:minijinja"] - -[build-dependencies] -minijinja-embed = { version = "2.24.0", optional = true } diff --git a/README.md b/README.md index d28b9ff..bf3fa61 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,6 @@ Compared to lldap, llldap: - does not use GraphQL to build an API, and therefore does not require custom logic in the client (JS/WASM) - supports virtualhosts out-of-the-box - supports mailalias with multiple values in search queries -- support listening on unix domain sockets (UDS) out-of-the-box ## Potential future features diff --git a/assets/css/main.css b/assets/css/main.css deleted file mode 100644 index 5660244..0000000 --- a/assets/css/main.css +++ /dev/null @@ -1,81 +0,0 @@ -body { - margin: 0; -} - -#logo { - display: block; - max-height: 5rem; - max-width: 5rem; - height: auto; - width: auto; - margin: 0 auto; -} - -#center { - width: 100vw; - height: 100vh; - display: flex; - justify-content: center; - align-items: space-around; - flex-direction: column; - max-width: 250px; - margin: auto; -} - -#login-form { - max-width: 200px; - margin: 0 auto; -} - -#login-line { - margin-top: 1rem; - display: flex; - justify-content: center; - font-size: 2rem; - text-align: center; - height: 2.5rem; - gap: 0.5rem; -} - -#login-line > img { - width: 1.75rem; - height: auto; -} - -#login-line > input { - border-color: rgba(31, 41, 55, 0.2); - border-radius: 8px; - padding: 0.5rem 1rem; -} - -#submit-login { - width: 100%; - background-color: rgb(87, 13, 248); - cursor: pointer; - box-sizing: border-box; - border: none; - color: rgb(240, 240, 240); - font-weight: bolder; - text-transform: uppercase; - margin: auto; -} - -.banner { - margin: 1rem auto; - width: 100%; - border-radius: 5px; -} - -.banner > p { - text-align: center; -} - -.is-success { - background-color: #28cb28; - color: #efefef; -} - -.is-error { - background-color: #d42929; - color: #efefef; -} diff --git a/assets/img/logo.png b/assets/img/logo.png deleted file mode 100644 index 9278cb6..0000000 Binary files a/assets/img/logo.png and /dev/null differ diff --git a/assets/img/password.svg b/assets/img/password.svg deleted file mode 100644 index db7eb11..0000000 --- a/assets/img/password.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/assets/img/user.svg b/assets/img/user.svg deleted file mode 100644 index 74ce0d9..0000000 --- a/assets/img/user.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/build.rs b/build.rs deleted file mode 100644 index 19fb9dc..0000000 --- a/build.rs +++ /dev/null @@ -1,4 +0,0 @@ -fn main() { - #[cfg(feature = "embed")] - minijinja_embed::embed_templates!("templates"); -} diff --git a/src/cli.rs b/src/cli.rs index d70e141..f93abe3 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -1,18 +1,9 @@ use argh::FromArgs; -use camino::Utf8PathBuf; /// Run the llldap server #[derive(FromArgs)] pub struct CliArgs { - /// path to the JSON file for the database - #[argh(option)] - pub db: Option, - - /// address or socket to listen on for LDAP connections - #[argh(option, default = "String::from(\"127.0.0.1:3389\")")] - pub listen_ldap: String, - - /// address or socket to listen on for HTTP connections - #[argh(option, default = "String::from(\"127.0.0.1:3390\")")] - pub listen_http: String, + /// address or socket to listen on + #[argh(positional, default = "String::from(\"127.0.0.1:3389\")")] + pub listen: String, } diff --git a/src/db/common.rs b/src/db/common.rs index 3a5f655..4f278b8 100644 --- a/src/db/common.rs +++ b/src/db/common.rs @@ -3,22 +3,22 @@ use tokio::sync::RwLock; use std::sync::Arc; use crate::db::error::BoxedError; -use crate::db::{DatabaseInterface, Domain, User, UserRef}; +use crate::db::{DatabaseInterface, UserRef}; #[derive(Clone, Debug)] -pub struct Database { - pub inner: Arc>>, +pub struct Database { + pub inner: Arc>, } -impl Database { - pub fn new(db: impl DatabaseInterface) -> Self { +impl Database { + pub fn new(db: D) -> Self { Self { - inner: Arc::new(RwLock::new(Box::new(db))), + inner: Arc::new(RwLock::new(db)), } } } -impl Database { +impl Database { /// Return false if the user doesn't exist, or the password is wrong. /// /// TODO: should we return something else when the account doesn't exist? @@ -36,13 +36,4 @@ impl Database { tracing::debug!("Comparing {} and {}", user.password, password); Ok(user.password == password) } - - pub async fn domains_user_can_see(&self, user: &User) -> Result, BoxedError> { - Ok(self - .list_all_domains() - .await? - .into_iter() - .filter(|d| user.can_see_domain(&d.name)) - .collect()) - } } diff --git a/src/db/domain.rs b/src/db/domain.rs deleted file mode 100644 index 9c7d7e6..0000000 --- a/src/db/domain.rs +++ /dev/null @@ -1,6 +0,0 @@ -use serde::{Deserialize, Serialize}; - -#[derive(Clone, Debug, Default, Deserialize, Serialize)] -pub struct Domain { - pub name: String, -} diff --git a/src/db/error.rs b/src/db/error.rs index 9fdce05..573bbe1 100644 --- a/src/db/error.rs +++ b/src/db/error.rs @@ -5,42 +5,12 @@ use crate::db::UserRef; pub type BoxedError = Box; #[derive(Debug)] -pub enum UserCreationError { - DomainNotFound(String), - UserAlreadyExists(UserRef), - Permissions, -} +pub struct UserAlreadyExists(pub UserRef); -impl fmt::Display for UserCreationError { +impl fmt::Display for UserAlreadyExists { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::DomainNotFound(domain) => write!(f, "No domain {domain} to create user in"), - Self::UserAlreadyExists(user) => write!(f, "User already exists: {user}"), - Self::Permissions => write!(f, "You do not have permissions to create this user"), - } + write!(f, "User already exists: {}", self.0) } } -impl std::error::Error for UserCreationError {} - -#[derive(Debug)] -pub enum DomainCreationError { - DomainAlreadyExists(String), - InvalidDomain(String), -} - -impl fmt::Display for DomainCreationError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::DomainAlreadyExists(domain) => { - write!(f, "Cannot create domain {domain} because it already exists") - } - Self::InvalidDomain(domain) => write!( - f, - "Cannot create domain `{domain}` because it's not considered a valid domain" - ), - } - } -} - -impl std::error::Error for DomainCreationError {} +impl std::error::Error for UserAlreadyExists {} diff --git a/src/db/filesystem.rs b/src/db/filesystem.rs deleted file mode 100644 index 9074822..0000000 --- a/src/db/filesystem.rs +++ /dev/null @@ -1,156 +0,0 @@ -use camino::{Utf8Path, Utf8PathBuf}; -use serde::{Deserialize, Serialize}; -use serde_json::Error as JsonError; - -use std::fmt; -use std::io::Error as IOError; - -use crate::db::error::{BoxedError, DomainCreationError, UserCreationError}; -use crate::db::{Database, DatabaseInterface, Domain, MemoryDatabase, User, UserRef}; - -#[derive(Debug)] -pub enum FilesystemDatabaseError { - ReadFileIO(Utf8PathBuf, IOError), - ReadFileJson(Utf8PathBuf, JsonError), - WriteFileIO(Utf8PathBuf, IOError), - WriteFileJson(Utf8PathBuf, JsonError), -} - -impl fmt::Display for FilesystemDatabaseError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( - f, - "{}", - match self { - Self::ReadFileIO(path, e) => format!("Failed to read database file {path}: {e}"), - Self::ReadFileJson(path, e) => - format!("Failed to parse database JSON file {path}: {e}"), - Self::WriteFileIO(path, e) => format!("Failed to write database file {path}: {e}"), - Self::WriteFileJson(path, e) => - format!("Failed to convert database to JSON file {path}: {e}"), - } - ) - } -} - -impl std::error::Error for FilesystemDatabaseError {} - -#[derive(Clone, Debug, Default, Deserialize, Serialize)] -pub struct FilesystemDatabase { - // TODO: Once we have common validation steps in place across DB backends, we can reduce cloning. - // For now, we clone the DB on every write operation, and update it when saving to disk - // succeeds. - pub inner: MemoryDatabase, - pub path: Utf8PathBuf, -} - -impl FilesystemDatabase { - pub async fn from_path(path: impl AsRef) -> Result { - let path = path.as_ref(); - - if !tokio::fs::try_exists(path) - .await - .map_err(|e| Box::new(FilesystemDatabaseError::ReadFileIO(path.to_path_buf(), e)))? - { - // The database doesn't exist yet, we create an empty one and try to write it - // to make sure the permissions are correct and the destination folder exists. - let mut db = Self { - inner: MemoryDatabase::default(), - path: path.to_path_buf(), - }; - - db.save().await?; - return Ok(Database::new(db)); - } - - let s = tokio::fs::read(path) - .await - .map_err(|e| Box::new(FilesystemDatabaseError::ReadFileIO(path.to_path_buf(), e)))?; - let inner: MemoryDatabase = serde_json::from_slice(&s) - .map_err(|e| Box::new(FilesystemDatabaseError::ReadFileJson(path.to_path_buf(), e)))?; - Ok(Database::new(Self { - inner, - path: path.to_path_buf(), - })) - } - - pub async fn save(&mut self) -> Result<(), BoxedError> { - let s = serde_json::to_string(&self) - .map_err(|e| Box::new(FilesystemDatabaseError::WriteFileJson(self.path.clone(), e)))?; - Ok(tokio::fs::write(&self.path, &s) - .await - .map_err(|e| Box::new(FilesystemDatabaseError::WriteFileIO(self.path.clone(), e)))?) - } -} - -#[async_trait::async_trait] -impl DatabaseInterface for FilesystemDatabase { - async fn create_domain( - &mut self, - domain: &str, - ) -> Result, BoxedError> { - let mut new_db = self.inner.clone(); - - if let Err(e) = new_db.create_domain(domain).await? { - return Ok(Err(e)); - } - - self.save().await?; - self.inner = new_db; - - Ok(Ok(())) - } - - async fn get_domain(&self, domain: &str) -> Result, BoxedError> { - self.inner.get_domain(domain).await - } - - async fn get_user(&self, req_user: &UserRef) -> Result, BoxedError> { - self.inner.get_user(req_user).await - } - - async fn create_user( - &mut self, - user: User, - ) -> Result, BoxedError> { - let mut new_db = self.inner.clone(); - - if let Err(e) = new_db.create_user(user).await? { - return Ok(Err(e)); - } - - self.save().await?; - self.inner = new_db; - - Ok(Ok(())) - } - - async fn try_create_user( - &mut self, - new_user: User, - current_user: &User, - ) -> Result, BoxedError> { - let mut new_db = self.inner.clone(); - - if let Err(e) = new_db.try_create_user(new_user, current_user).await? { - return Ok(Err(e)); - } - - self.save().await?; - self.inner = new_db; - - Ok(Ok(())) - } - - async fn list_all_domains(&self) -> Result, BoxedError> { - self.inner.list_all_domains().await - } - - async fn list_all_users(&self) -> Result, BoxedError> { - self.inner.list_all_users().await - } - - async fn list_domain_users(&self, domain: Option) -> Result, BoxedError> { - self.inner.list_domain_users(domain).await - } -} diff --git a/src/db/group.rs b/src/db/group.rs index 6a2793f..428d8f9 100644 --- a/src/db/group.rs +++ b/src/db/group.rs @@ -1,6 +1,4 @@ -use serde::{Deserialize, Serialize}; - -#[derive(Clone, Debug, Deserialize, Serialize)] +#[derive(Clone, Debug)] pub struct Group { name: String, domain: String, diff --git a/src/db/interface.rs b/src/db/interface.rs index 6115f39..f8ca052 100644 --- a/src/db/interface.rs +++ b/src/db/interface.rs @@ -1,19 +1,7 @@ -use crate::db::error::{BoxedError, DomainCreationError, UserCreationError}; -use crate::db::{Database, Domain, User, UserRef}; - -#[async_trait::async_trait] -impl DatabaseInterface for Database { - async fn create_domain( - &mut self, - domain: &str, - ) -> Result, BoxedError> { - self.inner.write().await.create_domain(domain).await - } - - async fn get_domain(&self, domain: &str) -> Result, BoxedError> { - self.inner.read().await.get_domain(domain).await - } +use crate::db::error::{BoxedError, UserAlreadyExists}; +use crate::db::{Database, User, UserRef}; +impl DatabaseInterface for Database { async fn get_user(&self, user: &UserRef) -> Result, BoxedError> { self.inner.read().await.get_user(user).await } @@ -21,60 +9,15 @@ impl DatabaseInterface for Database { async fn create_user( &mut self, user: User, - ) -> Result, BoxedError> { + ) -> Result, BoxedError> { self.inner.write().await.create_user(user).await } - - async fn try_create_user( - &mut self, - new_user: User, - current_user: &User, - ) -> Result, BoxedError> { - self.inner - .write() - .await - .try_create_user(new_user, current_user) - .await - } - - async fn list_all_domains(&self) -> Result, BoxedError> { - self.inner.read().await.list_all_domains().await - } - - async fn list_all_users(&self) -> Result, BoxedError> { - self.inner.read().await.list_all_users().await - } - - async fn list_domain_users(&self, domain: Option) -> Result, BoxedError> { - self.inner.read().await.list_domain_users(domain).await - } } -#[async_trait::async_trait] -pub trait DatabaseInterface: std::fmt::Debug + Send + Sync + 'static { - async fn create_domain( - &mut self, - domain: &str, - ) -> Result, BoxedError>; - async fn get_domain(&self, domain: &str) -> Result, BoxedError>; - - async fn get_user(&self, user: &UserRef) -> Result, BoxedError>; +pub trait DatabaseInterface: Clone + Send + Sync + 'static { + fn get_user(&self, user: &UserRef) -> impl std::future::Future, BoxedError>> + Send; async fn create_user( &mut self, user: User, - ) -> Result, BoxedError>; - async fn try_create_user( - &mut self, - new_user: User, - current_user: &User, - ) -> Result, BoxedError>; - - async fn list_all_domains(&self) -> Result, BoxedError>; - #[expect(unused)] - async fn list_all_users(&self) -> Result, BoxedError>; - - /// List users on a specific domain. - /// - /// A `None` domain requested lists global service users. - async fn list_domain_users(&self, domain: Option) -> Result, BoxedError>; + ) -> Result, BoxedError>; } diff --git a/src/db/memory.rs b/src/db/memory.rs index bae3c8e..186486c 100644 --- a/src/db/memory.rs +++ b/src/db/memory.rs @@ -1,118 +1,44 @@ -use serde::{Deserialize, Serialize}; +use std::future::{Future, ready}; -use crate::db::error::{BoxedError, DomainCreationError, UserCreationError}; -use crate::db::{Database, DatabaseInterface, Domain, Group, Operation, User, UserRef}; +use crate::db::error::{BoxedError, UserAlreadyExists}; +use crate::db::{Database, DatabaseInterface, Group, User, UserRef}; -#[derive(Clone, Debug, Default, Deserialize, Serialize)] +#[derive(Clone, Debug, Default)] pub struct MemoryDatabase { - // We store data in tables like in SQL - pub domains: Vec, - pub groups: Vec, pub users: Vec, + pub groups: Vec, } impl MemoryDatabase { - #[allow(clippy::new_ret_no_self)] - pub fn new() -> Database { + pub fn new() -> Database { Database::new(Self::default()) } } -#[async_trait::async_trait] impl DatabaseInterface for MemoryDatabase { - async fn create_domain( - &mut self, - domain: &str, - ) -> Result, BoxedError> { - if domain.is_empty() { - return Ok(Err(DomainCreationError::InvalidDomain(domain.to_string()))); + fn get_user( + &self, + req_user: &UserRef, + ) -> impl Future, BoxedError>> { + for user in &self.users { + if user.username == req_user.username && user.domain == req_user.domain { + return ready(Ok(Some(user.clone()))); + } } - if self.domains.iter().find(|d| d.name == domain).is_some() { - return Ok(Err(DomainCreationError::DomainAlreadyExists( - domain.to_string(), - ))); - } - - self.domains.push(Domain { - name: domain.to_string(), - }); - - Ok(Ok(())) - } - - async fn get_domain(&self, domain: &str) -> Result, BoxedError> { - let Some(domain) = self.domains.iter().find(|d| d.name == domain) else { - return Ok(None); - }; - - Ok(Some(domain.clone())) - } - - async fn get_user(&self, req_user: &UserRef) -> Result, BoxedError> { - Ok(self - .users - .iter() - .find(|u| u.username == req_user.username && u.domain == req_user.domain) - .cloned()) + ready(Ok(None)) } async fn create_user( &mut self, user: User, - ) -> Result, BoxedError> { + ) -> Result, BoxedError> { let user_ref = user.user_ref(); - if self.get_user(&user_ref).await?.is_some() { - return Ok(Err(UserCreationError::UserAlreadyExists(user_ref))); - } - - // If a domain is requested (i.e. not a global user), make sure the domain exists - if let Some(req_domain) = &user.domain - && self.get_domain(req_domain).await?.is_none() - { - return Ok(Err(UserCreationError::DomainNotFound(req_domain.clone()))); + return Ok(Err(UserAlreadyExists(user_ref))); } self.users.push(user); Ok(Ok(())) } - - async fn try_create_user( - &mut self, - new_user: User, - current_user: &User, - ) -> Result, BoxedError> { - // First check permissions, then apply the operation - // - // TODO: for now we don't allow creating service users manually - // so we assume there's a domain provided - let Some(new_user_domain) = &new_user.domain else { - return Ok(Err(UserCreationError::Permissions)); - }; - - let op = Operation::CreateUser(new_user_domain.clone()); - if !current_user.can_perform(&op) { - return Ok(Err(UserCreationError::Permissions)); - } - - self.create_user(new_user).await - } - - async fn list_all_domains(&self) -> Result, BoxedError> { - Ok(self.domains.clone()) - } - - async fn list_all_users(&self) -> Result, BoxedError> { - Ok(self.users.clone()) - } - - async fn list_domain_users(&self, domain: Option) -> Result, BoxedError> { - Ok(self - .users - .iter() - .filter(|u| u.domain == domain) - .cloned() - .collect()) - } } diff --git a/src/db/mod.rs b/src/db/mod.rs index 902e89c..b54747a 100644 --- a/src/db/mod.rs +++ b/src/db/mod.rs @@ -1,17 +1,11 @@ mod common; pub use common::Database; -mod domain; -pub use domain::Domain; pub mod error; -mod filesystem; -pub use filesystem::FilesystemDatabase; mod group; pub use group::Group; mod interface; pub use interface::DatabaseInterface; mod memory; pub use memory::MemoryDatabase; -mod role; -pub use role::{Operation, Role}; mod user; pub use user::{User, UserRef}; diff --git a/src/db/role.rs b/src/db/role.rs deleted file mode 100644 index 37f6b51..0000000 --- a/src/db/role.rs +++ /dev/null @@ -1,55 +0,0 @@ -use serde::{Deserialize, Serialize}; - -#[derive(Clone, Debug)] -pub enum Operation { - CreateDomain, - CreateUser(String), - ListUsers(Option), -} - -#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] -pub enum Role { - /// Can do anything - Admin, - /// Can only read data across all vhosts - ReadonlyAdmin, - /// Can do anything on a domain, except removing - /// oneself as a domain admin. - /// - /// Can not give away roles other than DomainModerator/User - DomainAdmin(String), - /// Can create users and reset passwords on a domain - DomainModerator(String), - /// Can only edit own profile - User, -} - -impl Role { - pub fn can_perform(&self, operation: &Operation) -> bool { - match operation { - Operation::CreateDomain => self == &Self::Admin, - Operation::CreateUser(op_domain) => match self { - Self::Admin => true, - Self::DomainAdmin(usr_domain) | Self::DomainModerator(usr_domain) => { - op_domain == usr_domain - } - _ => false, - }, - Operation::ListUsers(op_domain) => match self { - Self::Admin => true, - Self::DomainAdmin(usr_domain) | Self::DomainModerator(usr_domain) => { - op_domain.as_ref() == Some(usr_domain) - } - _ => false, - }, - } - } - - pub fn can_see_domain(&self, domain: &str) -> bool { - match self { - Self::Admin | Self::ReadonlyAdmin => true, - Self::DomainAdmin(d) | Self::DomainModerator(d) => domain == d, - Self::User => false, - } - } -} diff --git a/src/db/user.rs b/src/db/user.rs index ba9e986..4301d73 100644 --- a/src/db/user.rs +++ b/src/db/user.rs @@ -1,71 +1,23 @@ -use serde::{Deserialize, Serialize}; - use std::fmt; -use crate::db::{Operation, Role}; - -/// A requested user/domain combo for login, lowercased. -/// -/// Domain may be empty, but a value with more than one -/// `@` is considered invalid. #[derive(Clone, Debug)] pub struct UserRef { pub username: String, - pub domain: Option, -} - -#[derive(Clone, Debug)] -pub struct InvalidUserRef(pub String); - -impl std::error::Error for InvalidUserRef {} - -impl fmt::Display for InvalidUserRef { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "Invalid username: {}", self.0) - } -} - -impl UserRef { - pub fn from_user_maybe_domain(value: &str) -> Result { - let value = value.to_lowercase(); - - let mut parts = value.split('@'); - let username = parts.next().unwrap(); - let domain = parts.next(); - - if parts.next().is_some() { - return Err(InvalidUserRef(value.clone())); - } - - Ok(Self { - username: username.to_string(), - domain: domain.map(Into::into), - }) - } + pub domain: String, } impl fmt::Display for UserRef { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - if let Some(domain) = &self.domain { - write!(f, "{}@{}", self.username, domain) - } else { - write!(f, "{}", self.username) - } + write!(f, "{}@{}", self.username, self.domain) } } -#[derive(Clone, Debug, Deserialize, Serialize)] +#[derive(Clone, Debug)] pub struct User { - /// Username, without the domain part. Once set, cannot be edited. pub username: String, - /// Domain of the user. Once set, cannot be edited. - /// - /// Service accounts may exist with an empty domain. - pub domain: Option, + pub domain: String, pub password: String, - /// Mail for the user. Computed from username/domain, cannot be edited. pub mail: String, - pub role: Role, // recovery_mail: String, } @@ -76,28 +28,10 @@ impl User { domain: self.domain.clone(), } } - - pub fn can_perform(&self, operation: &Operation) -> bool { - self.role.can_perform(operation) - } - - pub fn can_create_domain(&self) -> bool { - self.role.can_perform(&Operation::CreateDomain) - } - - pub fn can_see_domain(&self, domain: &str) -> bool { - let allowed = self.role.can_see_domain(domain); - tracing::debug!("{} can see domain {}: {}", self.mail, domain, allowed); - allowed - } } impl fmt::Display for User { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - if let Some(domain) = &self.domain { - write!(f, "{}@{}", self.username, domain) - } else { - write!(f, "{}", self.username) - } + write!(f, "{}@{}", self.username, self.domain) } } diff --git a/src/http/domain.rs b/src/http/domain.rs deleted file mode 100644 index 4c05af0..0000000 --- a/src/http/domain.rs +++ /dev/null @@ -1,71 +0,0 @@ -use axum::extract::{Form, Path, State}; -use axum::response::{Html, IntoResponse, Redirect, Response}; -use http::StatusCode; -use minijinja::context; -use serde::Deserialize; - -use crate::db::{DatabaseInterface, Operation}; -use crate::http::{HttpSession, HttpState}; - -pub async fn get_domain( - State(state): State, - session: HttpSession, - Path(domain): Path, -) -> Response { - let domain = match state.db.get_domain(&domain).await { - Ok(Some(domain)) => domain, - Ok(None) => return format!("Domain not found: {domain}").into_response(), - Err(e) => { - return format!("Database error: {e}").into_response(); - } - }; - - let domain_users = match state.db.list_domain_users(Some(domain.name.clone())).await { - Ok(users) => users, - Err(e) => { - return format!("Database error: {e}").into_response(); - } - }; - - // Redundant because someone who can see the domain admin page for the moment - // always can create accounts. - let op = Operation::CreateUser(domain.name.clone()); - let can_create_user = session.user.can_perform(&op); - - let ctx = context! { - can_create_user, - domain, - user => session.user, - users => domain_users, - }; - - let page = state - .templates - .get_template("domain.html") - .unwrap() - .render(ctx) - .unwrap(); - (StatusCode::OK, Html(page)).into_response() -} - -#[derive(Clone, Debug, Deserialize)] -pub struct DomainCreationForm { - domainname: String, -} - -pub async fn create_domain( - State(mut state): State, - session: HttpSession, - Form(form): Form, -) -> Response { - let op = Operation::CreateDomain; - if !session.user.can_perform(&op) { - return "Not authorized to create a new domain".into_response(); - } - - match state.db.create_domain(&form.domainname).await { - Ok(Ok(())) => Redirect::to(&format!("/domain/{}", form.domainname)).into_response(), - Ok(Err(e)) => format!("Failed to create domain {}: {}", form.domainname, e).into_response(), - Err(e) => format!("Database error: {e}").into_response(), - } -} diff --git a/src/http/home.rs b/src/http/home.rs deleted file mode 100644 index 32802b0..0000000 --- a/src/http/home.rs +++ /dev/null @@ -1,58 +0,0 @@ -use axum::extract::State; -use axum::response::{Html, IntoResponse, Response}; -use http::StatusCode; -use minijinja::context; - -use crate::db::{DatabaseInterface, Operation}; -use crate::http::{HttpSession, HttpState}; - -pub async fn home( - State(state): State, - // Only logged in users are allowed here - session: HttpSession, -) -> Response { - // When the user has no domain (service admin) list all domains - let op = Operation::ListUsers(session.user.domain.clone()); - let other_users = if session.user.can_perform(&op) { - match state - .db - .list_domain_users(session.user.domain.clone()) - .await - { - Ok(other_users) => other_users, - Err(e) => { - return format!("Database error: {e}").into_response(); - } - } - } else { - vec![] - }; - - tracing::info!( - "Found {} users on domain {:?}", - other_users.len(), - session.user.domain - ); - - let domains = match state.db.domains_user_can_see(&session.user).await { - Ok(domains) => domains, - Err(e) => { - return format!("Database error: {e}").into_response(); - } - }; - - let ctx = context! { - domains, - user => session.user, - can_create_domain => session.user.can_create_domain(), - other_users, - }; - - let page = state - .templates - .get_template("home.html") - .unwrap() - .render(ctx) - .unwrap(); - (StatusCode::OK, Html(page)).into_response() -} diff --git a/src/http/login.rs b/src/http/login.rs deleted file mode 100644 index 578b5ff..0000000 --- a/src/http/login.rs +++ /dev/null @@ -1,92 +0,0 @@ -use axum::extract::{Form, State}; -use axum::response::{Html, IntoResponse, Redirect, Response}; -use axum_extra::extract::cookie::CookieJar; -use http::StatusCode; -use minijinja::context; -use serde::{Deserialize, Serialize}; - -use crate::db::{DatabaseInterface, UserRef}; -use crate::http::{HttpState, OptionalHttpSession}; - -#[derive(Debug, Deserialize)] -pub struct LoginForm { - username: String, - password: String, -} - -#[derive(Debug, Serialize)] -pub enum LoginError { - InvalidCredentials, - SessionInvalidated, -} - -pub async fn login_page( - State(state): State, - login_error: Option, -) -> Response { - let page = state - .templates - .get_template("login.html") - .unwrap() - .render(context! {login_error => login_error}) - .unwrap(); - (StatusCode::OK, Html(page)).into_response() -} - -pub async fn get_login( - State(state): State, - maybe_session: Option, -) -> Response { - if maybe_session.is_some() { - return Redirect::to("/").into_response(); - } - - login_page(State(state), None).await -} - -pub async fn post_login( - State(state): State, - session: Option, - cookies: CookieJar, - Form(form): Form, -) -> Response { - if session.is_some() { - // Already logged in - return (cookies, Redirect::to("/")).into_response(); - } - - let req_user = match UserRef::from_user_maybe_domain(&form.username) { - Ok(req_user) => req_user, - Err(e) => { - return e.to_string().into_response(); - } - }; - - let success = match state.db.check_password(&req_user, &form.password).await { - Ok(success) => success, - Err(e) => { - return format!("Database error: {e}").into_response(); - } - }; - - if success { - let maybe_user = match state.db.get_user(&req_user).await { - Ok(user) => user, - Err(e) => { - return format!("Database error: {e}").into_response(); - } - }; - - let Some(user) = maybe_user else { - return "Woops, user has been deleted while you were logging in. What are the chances?!".to_string() - .into_response(); - }; - - let cookies = state.sessions.add_session(user, cookies); - (cookies, Redirect::to("/")).into_response() - } else { - login_page(State(state), Some(LoginError::InvalidCredentials)) - .await - .into_response() - } -} diff --git a/src/http/logout.rs b/src/http/logout.rs deleted file mode 100644 index 3dc5151..0000000 --- a/src/http/logout.rs +++ /dev/null @@ -1,23 +0,0 @@ -use axum::extract::State; -use axum::response::{IntoResponse, Redirect, Response}; -use axum_extra::extract::cookie::CookieJar; - -use crate::http::login::{LoginError, login_page}; -use crate::http::{HttpState, OptionalHttpSession}; - -pub async fn logout( - State(state): State, - session: Option, - cookies: CookieJar, -) -> Response { - let Some(session) = session else { - return (cookies, Redirect::to("/")).into_response(); - }; - - let cookies = state.sessions.remove_session(&session, cookies); - ( - cookies, - login_page(State(state), Some(LoginError::SessionInvalidated)).await, - ) - .into_response() -} diff --git a/src/http/mod.rs b/src/http/mod.rs deleted file mode 100644 index daf1507..0000000 --- a/src/http/mod.rs +++ /dev/null @@ -1,110 +0,0 @@ -use axum::Router; -use axum::routing::{get, post}; -use axum::serve::Listener as AxumListener; -use minijinja::Environment; -#[cfg(not(feature = "embed"))] -use minijinja::path_loader; -#[cfg(feature = "embed")] -use static_serve::embed_assets; -#[cfg(not(feature = "embed"))] -use tower_http::services::ServeDir; - -use crate::db::Database; -use crate::listener::{Listener, ListenerKind}; -use crate::stream::{AbstractSocketAddr, AbstractStreamKind}; - -mod domain; -mod home; -mod login; -mod logout; -mod session; -use session::{HttpSession, HttpSessionManager, OptionalHttpSession}; -mod user; - -impl AxumListener for Listener { - type Io = AbstractStreamKind; - type Addr = AbstractSocketAddr; - - async fn accept(&mut self) -> (Self::Io, Self::Addr) { - loop { - let res = match &self.kind { - ListenerKind::Tcp(l) => l - .accept() - .await - .map(|(stream, remote_addr)| (stream.into(), remote_addr.into())), - ListenerKind::Uds(l) => l - .accept() - .await - .map(|(stream, remote_addr)| (stream.into(), remote_addr.into())), - }; - - match res { - Ok((stream, remote_addr)) => return (stream, remote_addr), - Err(e) => { - // Here the error could be fatal, or could simply be that a client aborted the connected, - // in which case we don't want to crash the server, simply skip this client connection. - // https://doc.rust-lang.org/stable/std/net/struct.TcpListener.html#errors - match e.kind() { - std::io::ErrorKind::ConnectionAborted => {} - _ => panic!("Unrecoverable HTTP client connection error: {e}"), - } - } - } - } - } - - fn local_addr(&self) -> std::io::Result { - match &self.kind { - ListenerKind::Tcp(l) => l.local_addr().map(Into::into), - ListenerKind::Uds(l) => l.local_addr().map(Into::into), - } - } -} - -#[derive(Clone)] -pub struct HttpState { - pub db: Database, - pub sessions: HttpSessionManager, - pub templates: Environment<'static>, -} - -impl HttpState { - pub fn new(db: Database) -> Self { - let mut templates = Environment::new(); - #[cfg(feature = "embed")] - minijinja_embed::load_templates!(&mut templates); - #[cfg(not(feature = "embed"))] - templates.set_loader(path_loader("templates")); - Self { - db, - sessions: HttpSessionManager::new(), - templates, - } - } -} - -pub async fn http_listen(listener: Listener, db: Database) { - #[cfg(all(feature = "embed", feature = "noembed"))] - compile_error!("You cannot have `embed` and `noembed` features enabled at the same time."); - #[cfg(not(any(feature = "embed", feature = "noembed")))] - compile_error!("You must have `embed` or `noembed` feature enabled."); - - #[cfg(feature = "embed")] - let app = { - embed_assets!("assets"); - Router::new().nest("/assets", static_router()) - }; - #[cfg(not(feature = "embed"))] - let app = { Router::new().nest_service("/assets", ServeDir::new("assets")) }; - let app = app - .route("/", get(home::home)) - .route("/login", get(login::get_login)) - .route("/login", post(login::post_login)) - .route("/logout", get(logout::logout)) - .route("/domain/{domain}", get(domain::get_domain)) - .route("/domain", post(domain::create_domain)) - .route("/user", post(user::create_user)) - .with_state(HttpState::new(db)); - - axum::serve(listener, app).await.unwrap(); -} diff --git a/src/http/session.rs b/src/http/session.rs deleted file mode 100644 index 1f88874..0000000 --- a/src/http/session.rs +++ /dev/null @@ -1,124 +0,0 @@ -use axum::extract::{FromRequestParts, OptionalFromRequestParts}; -use axum::response::Redirect; -use axum_extra::extract::cookie::{Cookie, CookieJar}; -use http::request::Parts; -use uuid::Uuid; - -use std::sync::{Arc, RwLock}; - -use crate::db::User; -use crate::http::HttpState; - -pub const COOKIE_NAME: &str = "lldap_session"; - -#[derive(Clone, Debug)] -pub struct HttpSessionManager { - inner: Arc>>, -} - -impl HttpSessionManager { - pub fn new() -> Self { - Self { - inner: Arc::new(RwLock::new(vec![])), - } - } - - pub fn add_session(&self, user: User, cookies: CookieJar) -> CookieJar { - let uuid = Uuid::new_v4(); - - let mut cookie = Cookie::new(COOKIE_NAME, uuid.to_string()); - cookie.set_path("/"); - - let session = HttpSession { user, uuid }; - - { - self.inner.write().unwrap().push(session); - } - - cookies.add(cookie) - } - - pub fn get_session(&self, cookies: &CookieJar) -> Option { - let cookie = cookies.get(COOKIE_NAME)?; - let previous_uuid = Uuid::parse_str(cookie.value()).ok()?; - - self.inner - .read() - .unwrap() - .iter() - .find(|session| session.uuid == previous_uuid) - .cloned() - } - - pub fn remove_session(&self, session: &HttpSession, cookies: CookieJar) -> CookieJar { - let mut cookie = Cookie::new(COOKIE_NAME, String::new()); - cookie.set_path("/"); - - let idx = { - let Some(idx) = self.inner.read().unwrap().iter().position(|s| s == session) else { - return cookies; - }; - idx - }; - - { - self.inner.write().unwrap().remove(idx); - } - - cookies.remove(cookie) - } -} - -#[derive(Clone, Debug)] -pub struct HttpSession { - // TODO: by storing the session here, it means - // it needs to be updated every time we change the user. - pub user: User, - pub uuid: Uuid, -} - -impl PartialEq for HttpSession { - fn eq(&self, other: &Self) -> bool { - self.uuid == other.uuid - } -} - -impl FromRequestParts for HttpSession { - type Rejection = Redirect; - - async fn from_request_parts( - parts: &mut Parts, - state: &HttpState, - ) -> Result { - let cookies = CookieJar::from_request_parts(parts, state).await.unwrap(); - state - .sessions - .get_session(&cookies) - .ok_or(Redirect::to("/login")) - } -} - -pub struct OptionalHttpSession(pub HttpSession); - -impl std::ops::Deref for OptionalHttpSession { - type Target = HttpSession; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl OptionalFromRequestParts for OptionalHttpSession { - type Rejection = Redirect; - - async fn from_request_parts( - parts: &mut Parts, - state: &HttpState, - ) -> Result, Self::Rejection> { - let maybe_session = HttpSession::from_request_parts(parts, state) - .await - .ok() - .map(Self); - Ok(maybe_session) - } -} diff --git a/src/http/user.rs b/src/http/user.rs deleted file mode 100644 index 00fd976..0000000 --- a/src/http/user.rs +++ /dev/null @@ -1,37 +0,0 @@ -use axum::extract::{Form, State}; -use axum::response::{IntoResponse, Redirect, Response}; -use serde::Deserialize; - -use crate::db::{DatabaseInterface, Role, User}; -use crate::http::{HttpSession, HttpState}; - -#[derive(Clone, Debug, Deserialize)] -pub struct UserCreationForm { - pub username: String, - pub domain: String, - pub password: String, -} - -pub async fn create_user( - State(mut state): State, - session: HttpSession, - Form(form): Form, -) -> Response { - let new_user = User { - mail: format!("{}@{}", form.username, form.domain), - username: form.username.clone(), - domain: Some(form.domain.clone()), - password: form.password, - role: Role::User, - }; - - match state.db.try_create_user(new_user, &session.user).await { - Ok(Ok(())) => Redirect::to(&format!("/domain/{}", form.domain)).into_response(), - Ok(Err(e)) => format!( - "Failed to create user {} on domain {}: {}", - form.username, form.domain, e - ) - .into_response(), - Err(e) => format!("Database error: {e}").into_response(), - } -} diff --git a/src/ldap/dn.rs b/src/ldap/dn.rs index 33371fe..87922aa 100644 --- a/src/ldap/dn.rs +++ b/src/ldap/dn.rs @@ -137,7 +137,6 @@ impl Dn { /// Overrides the DN hostname (`dc` fields) with the provided host. /// /// When no `dc` fields are present, they are only added when `force` is true. - #[expect(unused)] pub fn set_hostname(&mut self, host: &str, force: bool) { let domain_components: Vec = host.split('.').map(|x| dn_escape(x).to_string()).collect(); @@ -149,10 +148,8 @@ impl Dn { let mut keys = VecMap::new(); keys.insert_or_append("uid", &user.username); keys.insert_or_append("ou", "people"); - if let Some(domain_components) = &user.domain { - for domain_component in domain_components.split('.') { - keys.insert_or_append("dc", domain_component); - } + for domain_component in user.domain.split('.') { + keys.insert_or_append("dc", domain_component); } Self { keys } diff --git a/src/ldap/filter/mail.rs b/src/ldap/filter/mail.rs index 625ca8d..0f17ac0 100644 --- a/src/ldap/filter/mail.rs +++ b/src/ldap/filter/mail.rs @@ -109,7 +109,7 @@ impl MailFilter { pub fn to_user_ref(&self) -> UserRef { UserRef { username: self.username.clone(), - domain: Some(self.domain.clone()), + domain: self.domain.clone(), } } } diff --git a/src/ldap/handler.rs b/src/ldap/handler.rs index 084cfa5..c8af21e 100644 --- a/src/ldap/handler.rs +++ b/src/ldap/handler.rs @@ -1,13 +1,34 @@ use ldap3_proto::LdapMsg; use ldap3_proto::proto::LdapOp; -use crate::db::Database; +use crate::db::{Database, DatabaseInterface}; +use crate::error::GlobalError; use crate::ldap::{ LdapClientState, LdapStream, LdapStreamError, op_bind, op_ext, search_by_mail_filter, }; +use crate::listener::Listener; + +pub async fn ldap_listen(listener: Listener, db: Database) { + // If the connection is None, it's because the client aborted early + // so there's nothing to do about it. + loop { + match listener.accept_ldap().await { + Ok(Some(stream)) => { + let db = db.clone(); + tokio::spawn(ldap_handler(stream, db)); + } + Ok(None) => { + panic!("LDAP listener closed"); + } + Err(e) => { + panic!("Failed to listen on LDAP listener"); + } + } + } +} #[tracing::instrument(name = "ldap", skip(stream, db), fields(session = %stream.session))] -pub async fn ldap_handler(mut stream: LdapStream, mut db: Database) { +pub async fn ldap_handler(mut stream: LdapStream, mut db: Database) { tracing::info! { remote_addr = ?stream.remote_addr, "New client connection" @@ -45,11 +66,11 @@ pub async fn ldap_handler(mut stream: LdapStream, mut db: Database) { /// Return true to keep the connection going, false to close it. #[tracing::instrument(name = "ldap-handler", skip(client_state, db, stream))] -pub async fn ldap_handler_inner( +pub async fn ldap_handler_inner( stream: &mut LdapStream, msg: LdapMsg, client_state: &mut LdapClientState, - db: &mut Database, + db: &mut Database, ) -> Result { tracing::debug!(msg = ?msg, "Received LDAP message"); match msg { diff --git a/src/ldap/mod.rs b/src/ldap/mod.rs index d72d094..dc33d6c 100644 --- a/src/ldap/mod.rs +++ b/src/ldap/mod.rs @@ -5,7 +5,7 @@ pub use dn::{Dn, MalformedDn}; mod filter; mod handler; mod op; -pub use handler::ldap_handler; +pub use handler::ldap_listen; pub use op::bind::{BindDn, op_bind}; pub use op::ext::op_ext; pub use op::search::search_by_mail_filter; diff --git a/src/ldap/op/bind.rs b/src/ldap/op/bind.rs index 144efaa..5b44c41 100644 --- a/src/ldap/op/bind.rs +++ b/src/ldap/op/bind.rs @@ -2,7 +2,7 @@ use ldap3_proto::proto::{LdapBindCred, LdapBindRequest, LdapBindResponse, LdapOp use ldap3_proto::{LdapMsg, LdapResultCode}; use crate::db::error::BoxedError; -use crate::db::{Database, UserRef}; +use crate::db::{Database, DatabaseInterface, UserRef}; use crate::ldap::{Dn, LdapReturnError, LdapStream, LdapStreamError, MalformedDn}; #[derive(Debug)] @@ -76,15 +76,9 @@ impl BindDn { pub fn to_user_ref(&self) -> UserRef { let username = self.0.keys.get("uid").unwrap()[0].clone(); - // TODO: if we want service domains to connect over LDAP - // we need to remove this unwrap and allow a BindDn to - // not have a domain part. let domain = self.0.keys.get("dc").unwrap().join("."); - UserRef { - username, - domain: Some(domain), - } + UserRef { username, domain } } } @@ -165,9 +159,9 @@ pub async fn bind_success(stream: &mut LdapStream, msgid: i32) -> Result<(), Lda /// /// On success, returns `Ok(Some(bound_dn))`. `Ok(None)` means credentials failed, /// either because the account does not exist, or the password is wrong. -pub async fn op_bind( +pub async fn op_bind( stream: &mut LdapStream, - db: &Database, + db: &Database, req: LdapBindRequest, msgid: i32, ) -> Result, LdapStreamError> { diff --git a/src/ldap/op/search.rs b/src/ldap/op/search.rs index d881508..b65b160 100644 --- a/src/ldap/op/search.rs +++ b/src/ldap/op/search.rs @@ -39,7 +39,7 @@ impl LdapReturnError for InvalidSearchDn { } #[derive(Clone, Debug)] -pub struct SearchDn(#[expect(unused)] Dn); +pub struct SearchDn(Dn); impl SearchDn { pub fn from_dn_str(input: &str) -> Result { @@ -174,9 +174,9 @@ fn search_entry_from_user(user: &User, req_attrs: &[String]) -> LdapSearchResult } } -pub async fn search_by_mail_filter( +pub async fn search_by_mail_filter( stream: &mut LdapStream, - db: &Database, + db: &Database, sr: LdapSearchRequest, msgid: i32, ) -> Result<(), LdapStreamError> { diff --git a/src/main.rs b/src/main.rs index ac42c60..3cd41f9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,51 +3,23 @@ mod cli; mod db; mod error; -#[cfg(feature = "http")] -mod http; mod ldap; mod listener; mod stream; -#[cfg(feature = "http")] -use crate::http::http_listen; use cli::CliArgs; -use db::{Database, DatabaseInterface, FilesystemDatabase, MemoryDatabase, Role, User}; +use db::{Database, DatabaseInterface, MemoryDatabase, User}; use error::GlobalError; -use ldap::ldap_handler; +use ldap::ldap_listen; use listener::ListenerPath; -async fn create_dummy_users(db: &mut Database) { - db.create_user(User { - username: "admin".to_string(), - domain: None, - password: "adminadmin".to_string(), - // TODO: what should we put here? - mail: "admin".to_string(), - role: Role::Admin, - }) - .await - .unwrap() - .unwrap(); - for letter in &["a", "b", "c"] { - let domain = format!("{letter}.localhost"); - db.create_domain(&domain).await.unwrap().unwrap(); +async fn create_dummy_users(db: &mut Database) { + for domain in &["a", "b", "c"] { db.create_user(User { - username: letter.to_string(), - domain: Some(domain.clone()), + username: domain.to_string(), + domain: format!("{domain}.localhost"), password: "adminadmin".to_string(), - mail: format!("{letter}@{domain}"), - role: Role::DomainAdmin(domain.clone()), - }) - .await - .unwrap() - .unwrap(); - db.create_user(User { - username: format!("user{letter}"), - domain: Some(domain.clone()), - password: "adminadmin".to_string(), - mail: format!("user{letter}@{domain}"), - role: Role::User, + mail: format!("{domain}@{domain}.localhost"), }) .await .unwrap() @@ -67,28 +39,13 @@ async fn main() -> Result<(), GlobalError> { // .init(); // tracing::subscriber::set_global_default(subscriber)?; - let ldap_listener = ListenerPath::new(&cli.listen_ldap)?.listener().await?; + let listener = ListenerPath::new(&cli.listen)?.listener().await?; - let mut db = if let Some(db_path) = &cli.db { - FilesystemDatabase::from_path(db_path).await.unwrap() - } else { - MemoryDatabase::new() - }; + let mut db = MemoryDatabase::new(); create_dummy_users(&mut db).await; - #[cfg(feature = "http")] - { - let http_listener = ListenerPath::new(&cli.listen_http)?.listener().await?; - let http_db = db.clone(); - tokio::spawn(http_listen(http_listener, http_db)); - } - - // If the connection is None, it's because the client aborted early - // so there's nothing to do about it. - while let Some(stream) = ldap_listener.accept_ldap().await? { - let db = db.clone(); - tokio::spawn(ldap_handler(stream, db)); - } + let ldap_db = db.clone(); + tokio::spawn(ldap_listen(listener, ldap_db)); Ok(()) } diff --git a/templates/base.html b/templates/base.html deleted file mode 100644 index b92b6da..0000000 --- a/templates/base.html +++ /dev/null @@ -1,11 +0,0 @@ - - - - LLLDAP - - - -{% block main %} -{% endblock %} - - diff --git a/templates/domain.html b/templates/domain.html deleted file mode 100644 index 758ad65..0000000 --- a/templates/domain.html +++ /dev/null @@ -1,30 +0,0 @@ -{% extends 'base.html' %} -{% block main %} -
- -

You are logged in as {{ user.username }}

-
- Logout -
- {% if can_create_user %} -
-

Create user

-
- - - - -
-
- {% endif %} -
-

Users on {{ domain.name }}

-
    - {% for user in users %} -
  • {{ user.mail }}
  • - {% endfor %} -
-
-
-{% endblock %} - diff --git a/templates/home.html b/templates/home.html deleted file mode 100644 index d1f71b2..0000000 --- a/templates/home.html +++ /dev/null @@ -1,38 +0,0 @@ -{% extends 'base.html' %} -{% block main %} -
- -

You are logged in as {{ user.username }}

-
- Logout -
- {% if can_create_domain %} -
-

Create domain

-
- - -
-
- {% endif %} -
-

Active domains you can see

- -
- {% if other_users %} -
-

Other users you have permission to see on your own domain

-
    - {% for user in other_users %} -
  • {{ user.mail }}
  • - {% endfor %} -
-
- {% endif %} -
-{% endblock %} - diff --git a/templates/login.html b/templates/login.html deleted file mode 100644 index 11ae59a..0000000 --- a/templates/login.html +++ /dev/null @@ -1,29 +0,0 @@ -{% extends 'base.html' %} -{% block main %} -
- - {% if login_error == "InvalidCredentials" %} - - {% elif login_error == "SessionInvalidated" %} - - {% endif %} -
-
- - -
-
- - -
-
- -
-
-
-{% endblock %} -