mirror of
https://forgejo.ellis.link/continuwuation/continuwuity.git
synced 2025-09-09 14:33:03 +02:00
feat: ldap login
This commit is contained in:
parent
8b35de6a43
commit
c7adbae03f
17 changed files with 921 additions and 155 deletions
413
Cargo.lock
generated
413
Cargo.lock
generated
|
@ -126,7 +126,7 @@ checksum = "0ae92a5119aa49cdbcf6b9f893fe4e1d98b04ccbf82ee0584ad948a44a734dea"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.104",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -183,7 +183,7 @@ dependencies = [
|
|||
"rustc-hash 2.1.1",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"syn",
|
||||
"syn 2.0.104",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -198,6 +198,45 @@ dependencies = [
|
|||
"winnow",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "asn1-rs"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f6fd5ddaf0351dff5b8da21b2fb4ff8e08ddd02857f0bf69c47639106c0fff0"
|
||||
dependencies = [
|
||||
"asn1-rs-derive",
|
||||
"asn1-rs-impl",
|
||||
"displaydoc",
|
||||
"nom",
|
||||
"num-traits",
|
||||
"rusticata-macros",
|
||||
"thiserror 1.0.69",
|
||||
"time",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "asn1-rs-derive"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "726535892e8eae7e70657b4c8ea93d26b8553afb1ce617caee529ef96d7dee6c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
"synstructure 0.12.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "asn1-rs-impl"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2777730b2039ac0f95f093556e61b6d26cebed5393ca6f152717777cec3a42ed"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "assign"
|
||||
version = "1.1.1"
|
||||
|
@ -250,7 +289,7 @@ checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.104",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -261,7 +300,7 @@ checksum = "e539d3fca749fcee5236ab05e93a52867dd549cc157c8cb7f99595f3cedffdb5"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.104",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -433,11 +472,11 @@ dependencies = [
|
|||
"hyper",
|
||||
"hyper-util",
|
||||
"pin-project-lite",
|
||||
"rustls",
|
||||
"rustls-pemfile",
|
||||
"rustls 0.23.29",
|
||||
"rustls-pemfile 2.2.0",
|
||||
"rustls-pki-types",
|
||||
"tokio",
|
||||
"tokio-rustls",
|
||||
"tokio-rustls 0.26.2",
|
||||
"tower-service",
|
||||
]
|
||||
|
||||
|
@ -452,9 +491,9 @@ dependencies = [
|
|||
"http",
|
||||
"http-body-util",
|
||||
"pin-project",
|
||||
"rustls",
|
||||
"rustls 0.23.29",
|
||||
"tokio",
|
||||
"tokio-rustls",
|
||||
"tokio-rustls 0.26.2",
|
||||
"tokio-util",
|
||||
"tower-layer",
|
||||
"tower-service",
|
||||
|
@ -521,7 +560,7 @@ dependencies = [
|
|||
"regex",
|
||||
"rustc-hash 1.1.0",
|
||||
"shlex",
|
||||
"syn",
|
||||
"syn 2.0.104",
|
||||
"which",
|
||||
]
|
||||
|
||||
|
@ -540,7 +579,7 @@ dependencies = [
|
|||
"regex",
|
||||
"rustc-hash 2.1.1",
|
||||
"shlex",
|
||||
"syn",
|
||||
"syn 2.0.104",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -794,7 +833,7 @@ dependencies = [
|
|||
"heck",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.104",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -972,7 +1011,7 @@ dependencies = [
|
|||
"rand 0.8.5",
|
||||
"regex",
|
||||
"reqwest",
|
||||
"ring",
|
||||
"ring 0.17.14",
|
||||
"ruma",
|
||||
"sanitize-filename",
|
||||
"serde",
|
||||
|
@ -1019,7 +1058,7 @@ dependencies = [
|
|||
"itertools 0.14.0",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.104",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1044,7 +1083,7 @@ dependencies = [
|
|||
"hyper-util",
|
||||
"log",
|
||||
"ruma",
|
||||
"rustls",
|
||||
"rustls 0.23.29",
|
||||
"sd-notify",
|
||||
"sentry",
|
||||
"sentry-tower",
|
||||
|
@ -1074,6 +1113,7 @@ dependencies = [
|
|||
"image",
|
||||
"ipaddress",
|
||||
"itertools 0.14.0",
|
||||
"ldap3",
|
||||
"log",
|
||||
"loole",
|
||||
"lru-cache",
|
||||
|
@ -1183,6 +1223,16 @@ dependencies = [
|
|||
"crossterm",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation"
|
||||
version = "0.9.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f"
|
||||
dependencies = [
|
||||
"core-foundation-sys",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation"
|
||||
version = "0.10.1"
|
||||
|
@ -1256,7 +1306,7 @@ dependencies = [
|
|||
"proc-macro2",
|
||||
"quote",
|
||||
"strict",
|
||||
"syn",
|
||||
"syn 2.0.104",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1366,7 +1416,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "32a2785755761f3ddc1492979ce1e48d2c00d09311c39e4466429188f3dd6501"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.104",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1393,7 +1443,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.104",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1434,6 +1484,20 @@ dependencies = [
|
|||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "der-parser"
|
||||
version = "8.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dbd676fbbab537128ef0278adb5576cf363cff6aa22a7b24effe97347cfab61e"
|
||||
dependencies = [
|
||||
"asn1-rs",
|
||||
"displaydoc",
|
||||
"nom",
|
||||
"num-bigint",
|
||||
"num-traits",
|
||||
"rusticata-macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "deranged"
|
||||
version = "0.4.0"
|
||||
|
@ -1461,7 +1525,7 @@ dependencies = [
|
|||
"convert_case",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.104",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1483,7 +1547,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.104",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1544,7 +1608,7 @@ dependencies = [
|
|||
"heck",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.104",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1564,7 +1628,7 @@ checksum = "44f23cf4b44bfce11a86ace86f8a73ffdec849c9fd00a386a53d278bd9e81fb3"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.104",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1734,6 +1798,7 @@ checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876"
|
|||
dependencies = [
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-executor",
|
||||
"futures-io",
|
||||
"futures-sink",
|
||||
"futures-task",
|
||||
|
@ -1781,7 +1846,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.104",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -2030,7 +2095,7 @@ dependencies = [
|
|||
"ipnet",
|
||||
"once_cell",
|
||||
"rand 0.9.2",
|
||||
"ring",
|
||||
"ring 0.17.14",
|
||||
"serde",
|
||||
"thiserror 2.0.12",
|
||||
"tinyvec",
|
||||
|
@ -2122,7 +2187,7 @@ dependencies = [
|
|||
"markup5ever",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.104",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -2216,11 +2281,11 @@ dependencies = [
|
|||
"http",
|
||||
"hyper",
|
||||
"hyper-util",
|
||||
"rustls",
|
||||
"rustls-native-certs",
|
||||
"rustls 0.23.29",
|
||||
"rustls-native-certs 0.8.1",
|
||||
"rustls-pki-types",
|
||||
"tokio",
|
||||
"tokio-rustls",
|
||||
"tokio-rustls 0.26.2",
|
||||
"tower-service",
|
||||
"webpki-roots 1.0.2",
|
||||
]
|
||||
|
@ -2444,7 +2509,7 @@ checksum = "c34819042dc3d3971c46c2190835914dfbe0c3c13f61449b2997f4e9722dfa60"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.104",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -2613,7 +2678,7 @@ dependencies = [
|
|||
"proc-macro2",
|
||||
"quote",
|
||||
"regex",
|
||||
"syn",
|
||||
"syn 2.0.104",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -2628,6 +2693,43 @@ version = "1.3.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
|
||||
|
||||
[[package]]
|
||||
name = "lber"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2df7f9fd9f64cf8f59e1a4a0753fe7d575a5b38d3d7ac5758dcee9357d83ef0a"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"nom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ldap3"
|
||||
version = "0.11.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "166199a8207874a275144c8a94ff6eed5fcbf5c52303e4d9b4d53a0c7ac76554"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"bytes",
|
||||
"futures",
|
||||
"futures-util",
|
||||
"lazy_static",
|
||||
"lber",
|
||||
"log",
|
||||
"nom",
|
||||
"percent-encoding",
|
||||
"ring 0.16.20",
|
||||
"rustls 0.21.12",
|
||||
"rustls-native-certs 0.6.3",
|
||||
"thiserror 1.0.69",
|
||||
"tokio",
|
||||
"tokio-rustls 0.24.1",
|
||||
"tokio-stream",
|
||||
"tokio-util",
|
||||
"url",
|
||||
"x509-parser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lebe"
|
||||
version = "0.5.2"
|
||||
|
@ -2866,7 +2968,7 @@ checksum = "a9882ef5c56df184b8ffc107fc6c61e33ee3a654b021961d790a78571bb9d67a"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.104",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -3032,7 +3134,7 @@ checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.104",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -3094,6 +3196,15 @@ dependencies = [
|
|||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "oid-registry"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9bedf36ffb6ba96c2eb7144ef6270557b52e54b20c0a8e1eb2ff99a6c6959bff"
|
||||
dependencies = [
|
||||
"asn1-rs",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.21.3"
|
||||
|
@ -3284,7 +3395,7 @@ dependencies = [
|
|||
"proc-macro2",
|
||||
"proc-macro2-diagnostics",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.104",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -3358,7 +3469,7 @@ checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.104",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -3458,7 +3569,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "ff24dfcda44452b9816fff4cd4227e1bb73ff5a2f1bc1105aa92fb8565ce44d2"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"syn",
|
||||
"syn 2.0.104",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -3487,7 +3598,7 @@ checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.104",
|
||||
"version_check",
|
||||
"yansi",
|
||||
]
|
||||
|
@ -3508,7 +3619,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "52717f9a02b6965224f95ca2a81e2e0c5c43baacd28ca057577988930b6c3d5b"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.104",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -3531,7 +3642,7 @@ dependencies = [
|
|||
"itertools 0.14.0",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.104",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -3597,7 +3708,7 @@ dependencies = [
|
|||
"quinn-proto",
|
||||
"quinn-udp",
|
||||
"rustc-hash 2.1.1",
|
||||
"rustls",
|
||||
"rustls 0.23.29",
|
||||
"socket2",
|
||||
"thiserror 2.0.12",
|
||||
"tokio",
|
||||
|
@ -3615,9 +3726,9 @@ dependencies = [
|
|||
"getrandom 0.3.3",
|
||||
"lru-slab",
|
||||
"rand 0.9.2",
|
||||
"ring",
|
||||
"ring 0.17.14",
|
||||
"rustc-hash 2.1.1",
|
||||
"rustls",
|
||||
"rustls 0.23.29",
|
||||
"rustls-pki-types",
|
||||
"slab",
|
||||
"thiserror 2.0.12",
|
||||
|
@ -3876,16 +3987,16 @@ dependencies = [
|
|||
"percent-encoding",
|
||||
"pin-project-lite",
|
||||
"quinn",
|
||||
"rustls",
|
||||
"rustls-native-certs",
|
||||
"rustls-pemfile",
|
||||
"rustls 0.23.29",
|
||||
"rustls-native-certs 0.8.1",
|
||||
"rustls-pemfile 2.2.0",
|
||||
"rustls-pki-types",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_urlencoded",
|
||||
"sync_wrapper",
|
||||
"tokio",
|
||||
"tokio-rustls",
|
||||
"tokio-rustls 0.26.2",
|
||||
"tokio-socks",
|
||||
"tokio-util",
|
||||
"tower 0.5.2",
|
||||
|
@ -3909,6 +4020,21 @@ version = "0.8.52"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0c6a884d2998352bb4daf0183589aec883f16a6da1f4dde84d8e2e9a5409a1ce"
|
||||
|
||||
[[package]]
|
||||
name = "ring"
|
||||
version = "0.16.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
"once_cell",
|
||||
"spin",
|
||||
"untrusted 0.7.1",
|
||||
"web-sys",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ring"
|
||||
version = "0.17.14"
|
||||
|
@ -3919,7 +4045,7 @@ dependencies = [
|
|||
"cfg-if",
|
||||
"getrandom 0.2.16",
|
||||
"libc",
|
||||
"untrusted",
|
||||
"untrusted 0.9.0",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
|
@ -4093,7 +4219,7 @@ dependencies = [
|
|||
"quote",
|
||||
"ruma-identifiers-validation",
|
||||
"serde",
|
||||
"syn",
|
||||
"syn 2.0.104",
|
||||
"toml",
|
||||
]
|
||||
|
||||
|
@ -4178,6 +4304,15 @@ dependencies = [
|
|||
"semver",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rusticata-macros"
|
||||
version = "4.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "faf0c4a6ece9950b9abdb62b1cfcf2a68b3b67a10ba445b3bb85be2a293d0632"
|
||||
dependencies = [
|
||||
"nom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "0.38.44"
|
||||
|
@ -4204,6 +4339,18 @@ dependencies = [
|
|||
"windows-sys 0.60.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls"
|
||||
version = "0.21.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e"
|
||||
dependencies = [
|
||||
"log",
|
||||
"ring 0.17.14",
|
||||
"rustls-webpki 0.101.7",
|
||||
"sct",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls"
|
||||
version = "0.23.29"
|
||||
|
@ -4213,13 +4360,25 @@ dependencies = [
|
|||
"aws-lc-rs",
|
||||
"log",
|
||||
"once_cell",
|
||||
"ring",
|
||||
"ring 0.17.14",
|
||||
"rustls-pki-types",
|
||||
"rustls-webpki",
|
||||
"rustls-webpki 0.103.4",
|
||||
"subtle",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls-native-certs"
|
||||
version = "0.6.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a9aace74cb666635c918e9c12bc0d348266037aa8eb599b5cba565709a8dff00"
|
||||
dependencies = [
|
||||
"openssl-probe",
|
||||
"rustls-pemfile 1.0.4",
|
||||
"schannel",
|
||||
"security-framework 2.11.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls-native-certs"
|
||||
version = "0.8.1"
|
||||
|
@ -4229,7 +4388,16 @@ dependencies = [
|
|||
"openssl-probe",
|
||||
"rustls-pki-types",
|
||||
"schannel",
|
||||
"security-framework",
|
||||
"security-framework 3.2.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls-pemfile"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c"
|
||||
dependencies = [
|
||||
"base64 0.21.7",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -4251,6 +4419,16 @@ dependencies = [
|
|||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls-webpki"
|
||||
version = "0.101.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765"
|
||||
dependencies = [
|
||||
"ring 0.17.14",
|
||||
"untrusted 0.9.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls-webpki"
|
||||
version = "0.103.4"
|
||||
|
@ -4258,9 +4436,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "0a17884ae0c1b773f1ccd2bd4a8c72f16da897310a98b0e84bf349ad5ead92fc"
|
||||
dependencies = [
|
||||
"aws-lc-rs",
|
||||
"ring",
|
||||
"ring 0.17.14",
|
||||
"rustls-pki-types",
|
||||
"untrusted",
|
||||
"untrusted 0.9.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -4319,6 +4497,16 @@ version = "1.2.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||
|
||||
[[package]]
|
||||
name = "sct"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414"
|
||||
dependencies = [
|
||||
"ring 0.17.14",
|
||||
"untrusted 0.9.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sd-notify"
|
||||
version = "0.4.5"
|
||||
|
@ -4328,6 +4516,19 @@ dependencies = [
|
|||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "security-framework"
|
||||
version = "2.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02"
|
||||
dependencies = [
|
||||
"bitflags 2.9.1",
|
||||
"core-foundation 0.9.4",
|
||||
"core-foundation-sys",
|
||||
"libc",
|
||||
"security-framework-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "security-framework"
|
||||
version = "3.2.0"
|
||||
|
@ -4335,7 +4536,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "271720403f46ca04f7ba6f55d438f8bd878d6b8ca0a1046e8228c4145bcbb316"
|
||||
dependencies = [
|
||||
"bitflags 2.9.1",
|
||||
"core-foundation",
|
||||
"core-foundation 0.10.1",
|
||||
"core-foundation-sys",
|
||||
"libc",
|
||||
"security-framework-sys",
|
||||
|
@ -4365,7 +4566,7 @@ checksum = "255914a8e53822abd946e2ce8baa41d4cded6b8e938913b7f7b9da5b7ab44335"
|
|||
dependencies = [
|
||||
"httpdate",
|
||||
"reqwest",
|
||||
"rustls",
|
||||
"rustls 0.23.29",
|
||||
"sentry-backtrace",
|
||||
"sentry-contexts",
|
||||
"sentry-core",
|
||||
|
@ -4509,7 +4710,7 @@ checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.104",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -4723,6 +4924,12 @@ dependencies = [
|
|||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "spin"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
|
||||
|
||||
[[package]]
|
||||
name = "spki"
|
||||
version = "0.7.3"
|
||||
|
@ -4791,6 +4998,17 @@ version = "2.6.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.109"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.104"
|
||||
|
@ -4811,6 +5029,18 @@ dependencies = [
|
|||
"futures-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "synstructure"
|
||||
version = "0.12.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "synstructure"
|
||||
version = "0.13.2"
|
||||
|
@ -4819,7 +5049,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.104",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -4910,7 +5140,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.104",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -4921,7 +5151,7 @@ checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.104",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -5088,7 +5318,7 @@ checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.104",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -5103,13 +5333,23 @@ dependencies = [
|
|||
"tokio-stream",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-rustls"
|
||||
version = "0.24.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081"
|
||||
dependencies = [
|
||||
"rustls 0.21.12",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-rustls"
|
||||
version = "0.26.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e727b36a1a0e8b74c376ac2211e40c2c8af09fb4013c60d910495810f008e9b"
|
||||
dependencies = [
|
||||
"rustls",
|
||||
"rustls 0.23.29",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
|
@ -5307,7 +5547,7 @@ source = "git+https://forgejo.ellis.link/continuwuation/tracing?rev=1e64095a8051
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.104",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -5461,12 +5701,24 @@ version = "0.2.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4a1a07cc7db3810833284e8d372ccdc6da29741639ecc70c9ec107df0fa6154c"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
|
||||
|
||||
[[package]]
|
||||
name = "unsafe-libyaml"
|
||||
version = "0.2.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861"
|
||||
|
||||
[[package]]
|
||||
name = "untrusted"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"
|
||||
|
||||
[[package]]
|
||||
name = "untrusted"
|
||||
version = "0.9.0"
|
||||
|
@ -5482,7 +5734,7 @@ dependencies = [
|
|||
"base64 0.22.1",
|
||||
"log",
|
||||
"once_cell",
|
||||
"rustls",
|
||||
"rustls 0.23.29",
|
||||
"rustls-pki-types",
|
||||
"url",
|
||||
"webpki-roots 0.26.11",
|
||||
|
@ -5617,7 +5869,7 @@ dependencies = [
|
|||
"log",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.104",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
|
@ -5652,7 +5904,7 @@ checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.104",
|
||||
"wasm-bindgen-backend",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
@ -5832,7 +6084,7 @@ checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.104",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -5843,7 +6095,7 @@ checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.104",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -6164,6 +6416,23 @@ version = "0.6.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb"
|
||||
|
||||
[[package]]
|
||||
name = "x509-parser"
|
||||
version = "0.15.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7069fba5b66b9193bd2c5d3d4ff12b839118f6bcbef5328efafafb5395cf63da"
|
||||
dependencies = [
|
||||
"asn1-rs",
|
||||
"data-encoding",
|
||||
"der-parser",
|
||||
"lazy_static",
|
||||
"nom",
|
||||
"oid-registry",
|
||||
"rusticata-macros",
|
||||
"thiserror 1.0.69",
|
||||
"time",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "xml5ever"
|
||||
version = "0.18.1"
|
||||
|
@ -6221,8 +6490,8 @@ checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"synstructure",
|
||||
"syn 2.0.104",
|
||||
"synstructure 0.13.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -6242,7 +6511,7 @@ checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.104",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -6262,8 +6531,8 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"synstructure",
|
||||
"syn 2.0.104",
|
||||
"synstructure 0.13.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -6302,7 +6571,7 @@ checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.104",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
|
@ -546,6 +546,11 @@ features = ["std"]
|
|||
[workspace.dependencies.maplit]
|
||||
version = "1.0.2"
|
||||
|
||||
[workspace.dependencies.ldap3]
|
||||
version = "0.11.5"
|
||||
default-features = false
|
||||
features = ["sync", "tls-rustls"]
|
||||
|
||||
#
|
||||
# Patches
|
||||
#
|
||||
|
|
|
@ -1696,6 +1696,10 @@
|
|||
#
|
||||
#config_reload_signal = true
|
||||
|
||||
# This item is undocumented. Please contribute documentation for it.
|
||||
#
|
||||
#ldap = false
|
||||
|
||||
[global.tls]
|
||||
|
||||
# Path to a valid TLS certificate file.
|
||||
|
@ -1774,3 +1778,90 @@
|
|||
# is 33.55MB. Setting it to 0 disables blurhashing.
|
||||
#
|
||||
#blurhash_max_raw_size = 33554432
|
||||
|
||||
[global.ldap]
|
||||
|
||||
# Whether to enable LDAP login.
|
||||
#
|
||||
# example: "true"
|
||||
#
|
||||
#enable = false
|
||||
|
||||
# URI of the LDAP server.
|
||||
#
|
||||
# example: "ldap://ldap.example.com:389"
|
||||
#
|
||||
#uri =
|
||||
|
||||
# Root of the searches.
|
||||
#
|
||||
# example: "ou=users,dc=example,dc=org"
|
||||
#
|
||||
#base_dn = false
|
||||
|
||||
# Bind DN if anonymous search is not enabled.
|
||||
#
|
||||
# You can use the variable `{username}` that will be replaced by the
|
||||
# entered username. In such case, the password used to bind will be the
|
||||
# one provided for the login and not the one given by
|
||||
# `bind_password_file`. Beware: automatically granting admin rights will
|
||||
# not work if you use this direct bind instead of a LDAP search.
|
||||
#
|
||||
# example: "cn=ldap-reader,dc=example,dc=org" or
|
||||
# "cn={username},ou=users,dc=example,dc=org"
|
||||
#
|
||||
#bind_dn = false
|
||||
|
||||
# Path to a file on the system that contains the password for the
|
||||
# `bind_dn`.
|
||||
#
|
||||
# The server must be able to access the file, and it must not be empty.
|
||||
#
|
||||
#bind_password_file = false
|
||||
|
||||
# Search filter to limit user searches.
|
||||
#
|
||||
# You can use the variable `{username}` that will be replaced by the
|
||||
# entered username for more complex filters.
|
||||
#
|
||||
# example: "(&(objectClass=person)(memberOf=matrix))"
|
||||
#
|
||||
#filter = "(objectClass=*)"
|
||||
|
||||
# Attribute to use to uniquely identify the user.
|
||||
#
|
||||
# example: "uid" or "cn"
|
||||
#
|
||||
#uid_attribute = "uid"
|
||||
|
||||
# Attribute containing the mail of the user.
|
||||
#
|
||||
# example: "mail"
|
||||
#
|
||||
#mail_attribute = "mail"
|
||||
|
||||
# Attribute containing the distinguished name of the user.
|
||||
#
|
||||
# example: "givenName" or "sn"
|
||||
#
|
||||
#name_attribute = "givenName"
|
||||
|
||||
# Root of the searches for admin users.
|
||||
#
|
||||
# Defaults to `base_dn` if empty.
|
||||
#
|
||||
# example: "ou=admins,dc=example,dc=org"
|
||||
#
|
||||
#admin_base_dn = false
|
||||
|
||||
# The LDAP search filter to find administrative users for conduwuit.
|
||||
#
|
||||
# If left blank, administrative state must be configured manually for each
|
||||
# user.
|
||||
#
|
||||
# You can use the variable `{username}` that will be replaced by the
|
||||
# entered username for more complex filters.
|
||||
#
|
||||
# example: "(objectClass=conduwuitAdmin)" or "(uid={username})"
|
||||
#
|
||||
#admin_filter = false
|
||||
|
|
|
@ -68,7 +68,8 @@ pub(super) async fn create_user(&self, username: String, password: Option<String
|
|||
// Create user
|
||||
self.services
|
||||
.users
|
||||
.create(&user_id, Some(password.as_str()))?;
|
||||
.create(&user_id, Some(password.as_str()), None)
|
||||
.await?;
|
||||
|
||||
// Default to pretty displayname
|
||||
let mut displayname = user_id.localpart().to_owned();
|
||||
|
@ -284,6 +285,7 @@ pub(super) async fn reset_password(&self, username: String, password: Option<Str
|
|||
.services
|
||||
.users
|
||||
.set_password(&user_id, Some(new_password.as_str()))
|
||||
.await
|
||||
{
|
||||
| Err(e) => return Err!("Couldn't reset the password for user {user_id}: {e}"),
|
||||
| Ok(()) => {
|
||||
|
|
|
@ -49,6 +49,9 @@ jemalloc_stats = [
|
|||
"conduwuit-core/jemalloc_stats",
|
||||
"conduwuit-service/jemalloc_stats",
|
||||
]
|
||||
ldap = [
|
||||
"conduwuit-service/ldap"
|
||||
]
|
||||
release_max_log_level = [
|
||||
"conduwuit-core/release_max_log_level",
|
||||
"conduwuit-service/release_max_log_level",
|
||||
|
|
|
@ -373,7 +373,7 @@ pub(crate) async fn register_route(
|
|||
let password = if is_guest { None } else { body.password.as_deref() };
|
||||
|
||||
// Create user
|
||||
services.users.create(&user_id, password)?;
|
||||
services.users.create(&user_id, password, None).await?;
|
||||
|
||||
// Default to pretty displayname
|
||||
let mut displayname = user_id.localpart().to_owned();
|
||||
|
@ -659,7 +659,8 @@ pub(crate) async fn change_password_route(
|
|||
|
||||
services
|
||||
.users
|
||||
.set_password(sender_user, Some(&body.new_password))?;
|
||||
.set_password(sender_user, Some(&body.new_password))
|
||||
.await?;
|
||||
|
||||
if body.logout_devices {
|
||||
// Logout all devices except the current one
|
||||
|
|
|
@ -90,7 +90,7 @@ pub(crate) async fn get_displayname_route(
|
|||
.await
|
||||
{
|
||||
if !services.users.exists(&body.user_id).await {
|
||||
services.users.create(&body.user_id, None)?;
|
||||
services.users.create(&body.user_id, None, None).await?;
|
||||
}
|
||||
|
||||
services
|
||||
|
@ -189,7 +189,7 @@ pub(crate) async fn get_avatar_url_route(
|
|||
.await
|
||||
{
|
||||
if !services.users.exists(&body.user_id).await {
|
||||
services.users.create(&body.user_id, None)?;
|
||||
services.users.create(&body.user_id, None, None).await?;
|
||||
}
|
||||
|
||||
services
|
||||
|
@ -248,7 +248,7 @@ pub(crate) async fn get_profile_route(
|
|||
.await
|
||||
{
|
||||
if !services.users.exists(&body.user_id).await {
|
||||
services.users.create(&body.user_id, None)?;
|
||||
services.users.create(&body.user_id, None, None).await?;
|
||||
}
|
||||
|
||||
services
|
||||
|
|
|
@ -6,10 +6,11 @@ use conduwuit::{
|
|||
Err, Error, Result, debug, err, info, utils,
|
||||
utils::{ReadyExt, hash},
|
||||
};
|
||||
use conduwuit_service::uiaa::SESSION_ID_LENGTH;
|
||||
use conduwuit_core::debug_error;
|
||||
use conduwuit_service::{Services, uiaa::SESSION_ID_LENGTH};
|
||||
use futures::StreamExt;
|
||||
use ruma::{
|
||||
UserId,
|
||||
OwnedUserId, UserId,
|
||||
api::client::{
|
||||
session::{
|
||||
get_login_token,
|
||||
|
@ -49,6 +50,147 @@ pub(crate) async fn get_login_types_route(
|
|||
]))
|
||||
}
|
||||
|
||||
/// Authenticates the given user by its ID and its password.
|
||||
///
|
||||
/// Returns the user ID if successful, and an error otherwise.
|
||||
#[tracing::instrument(skip_all, fields(%user_id), name = "password")]
|
||||
pub(crate) async fn password_login(
|
||||
services: &Services,
|
||||
user_id: &UserId,
|
||||
lowercased_user_id: &UserId,
|
||||
password: &str,
|
||||
) -> Result<OwnedUserId> {
|
||||
// Restrict login to accounts only of type 'password', including untyped
|
||||
// legacy accounts which are equivalent to 'password'.
|
||||
if services
|
||||
.users
|
||||
.origin(user_id)
|
||||
.await
|
||||
.is_ok_and(|origin| origin != "password")
|
||||
{
|
||||
return Err!(Request(Forbidden("Account does not permit password login.")));
|
||||
}
|
||||
|
||||
let (hash, user_id) = match services.users.password_hash(user_id).await {
|
||||
| Ok(hash) => (hash, user_id),
|
||||
| Err(_) => services
|
||||
.users
|
||||
.password_hash(lowercased_user_id)
|
||||
.await
|
||||
.map(|hash| (hash, lowercased_user_id))
|
||||
.map_err(|_| err!(Request(Forbidden("Wrong username or password."))))?,
|
||||
};
|
||||
|
||||
if hash.is_empty() {
|
||||
return Err!(Request(UserDeactivated("The user has been deactivated")));
|
||||
}
|
||||
|
||||
hash::verify_password(password, &hash)
|
||||
.inspect_err(|e| debug_error!("{e}"))
|
||||
.map_err(|_| err!(Request(Forbidden("Wrong username or password."))))?;
|
||||
|
||||
Ok(user_id.to_owned())
|
||||
}
|
||||
|
||||
/// Authenticates the given user through the configured LDAP server.
|
||||
///
|
||||
/// Creates the user if the user is found in the LDAP and do not already have an
|
||||
/// account.
|
||||
#[tracing::instrument(skip_all, fields(%user_id), name = "ldap")]
|
||||
pub(super) async fn ldap_login(
|
||||
services: &Services,
|
||||
user_id: &UserId,
|
||||
lowercased_user_id: &UserId,
|
||||
password: &str,
|
||||
) -> Result<OwnedUserId> {
|
||||
let (user_dn, is_ldap_admin) = match services.config.ldap.bind_dn.as_ref() {
|
||||
| Some(bind_dn) if bind_dn.contains("{username}") =>
|
||||
(bind_dn.replace("{username}", lowercased_user_id.localpart()), false),
|
||||
| _ => {
|
||||
debug!("Searching user in LDAP");
|
||||
|
||||
let dns = services.users.search_ldap(user_id).await?;
|
||||
if dns.len() >= 2 {
|
||||
return Err!(Ldap("LDAP search returned two or more results"));
|
||||
}
|
||||
|
||||
let Some((user_dn, is_admin)) = dns.first() else {
|
||||
return password_login(services, user_id, lowercased_user_id, password).await;
|
||||
};
|
||||
|
||||
(user_dn.clone(), *is_admin)
|
||||
},
|
||||
};
|
||||
|
||||
let user_id = services
|
||||
.users
|
||||
.auth_ldap(&user_dn, password)
|
||||
.await
|
||||
.map(|()| lowercased_user_id.to_owned())?;
|
||||
|
||||
// LDAP users are automatically created on first login attempt. This is a very
|
||||
// common feature that can be seen on many services using a LDAP provider for
|
||||
// their users (synapse, Nextcloud, Jellyfin, ...).
|
||||
//
|
||||
// LDAP users are crated with a dummy password but non empty because an empty
|
||||
// password is reserved for deactivated accounts. The conduwuit password field
|
||||
// will never be read to login a LDAP user so it's not an issue.
|
||||
if !services.users.exists(lowercased_user_id).await {
|
||||
services
|
||||
.users
|
||||
.create(lowercased_user_id, Some("*"), Some("ldap"))
|
||||
.await?;
|
||||
}
|
||||
|
||||
let is_conduwuit_admin = services.admin.user_is_admin(lowercased_user_id).await;
|
||||
|
||||
if is_ldap_admin && !is_conduwuit_admin {
|
||||
services.admin.make_user_admin(lowercased_user_id).await?;
|
||||
} else if !is_ldap_admin && is_conduwuit_admin {
|
||||
services.admin.revoke_admin(lowercased_user_id).await?;
|
||||
}
|
||||
|
||||
Ok(user_id)
|
||||
}
|
||||
|
||||
pub(crate) async fn handle_login(
|
||||
services: &Services,
|
||||
body: &Ruma<login::v3::Request>,
|
||||
identifier: &Option<uiaa::UserIdentifier>,
|
||||
password: &str,
|
||||
user: &Option<String>,
|
||||
) -> Result<OwnedUserId> {
|
||||
debug!("Got password login type");
|
||||
let user_id =
|
||||
if let Some(uiaa::UserIdentifier::UserIdOrLocalpart(user_id)) = identifier {
|
||||
UserId::parse_with_server_name(user_id, &services.config.server_name)
|
||||
} else if let Some(user) = user {
|
||||
UserId::parse_with_server_name(user, &services.config.server_name)
|
||||
} else {
|
||||
return Err!(Request(Unknown(
|
||||
debug_warn!(?body.login_info, "Valid identifier or username was not provided (invalid or unsupported login type?)")
|
||||
)));
|
||||
}
|
||||
.map_err(|e| err!(Request(InvalidUsername(warn!("Username is invalid: {e}")))))?;
|
||||
|
||||
let lowercased_user_id = UserId::parse_with_server_name(
|
||||
user_id.localpart().to_lowercase(),
|
||||
&services.config.server_name,
|
||||
)?;
|
||||
|
||||
if !services.globals.user_is_local(&user_id)
|
||||
|| !services.globals.user_is_local(&lowercased_user_id)
|
||||
{
|
||||
return Err!(Request(Unknown("User ID does not belong to this homeserver")));
|
||||
}
|
||||
|
||||
if cfg!(feature = "ldap") && services.config.ldap.enable {
|
||||
ldap_login(services, &user_id, &lowercased_user_id, password).await
|
||||
} else {
|
||||
password_login(services, &user_id, &lowercased_user_id, password).await
|
||||
}
|
||||
}
|
||||
|
||||
/// # `POST /_matrix/client/v3/login`
|
||||
///
|
||||
/// Authenticates the user and returns an access token it can use in subsequent
|
||||
|
@ -80,70 +222,7 @@ pub(crate) async fn login_route(
|
|||
password,
|
||||
user,
|
||||
..
|
||||
}) => {
|
||||
debug!("Got password login type");
|
||||
let user_id =
|
||||
if let Some(uiaa::UserIdentifier::UserIdOrLocalpart(user_id)) = identifier {
|
||||
UserId::parse_with_server_name(user_id, &services.config.server_name)
|
||||
} else if let Some(user) = user {
|
||||
UserId::parse_with_server_name(user, &services.config.server_name)
|
||||
} else {
|
||||
return Err!(Request(Unknown(
|
||||
debug_warn!(?body.login_info, "Valid identifier or username was not provided (invalid or unsupported login type?)")
|
||||
)));
|
||||
}
|
||||
.map_err(|e| err!(Request(InvalidUsername(warn!("Username is invalid: {e}")))))?;
|
||||
|
||||
let lowercased_user_id = UserId::parse_with_server_name(
|
||||
user_id.localpart().to_lowercase(),
|
||||
&services.config.server_name,
|
||||
)?;
|
||||
|
||||
if !services.globals.user_is_local(&user_id)
|
||||
|| !services.globals.user_is_local(&lowercased_user_id)
|
||||
{
|
||||
return Err!(Request(Unknown("User ID does not belong to this homeserver")));
|
||||
}
|
||||
|
||||
// first try the username as-is
|
||||
let hash = services
|
||||
.users
|
||||
.password_hash(&user_id)
|
||||
.await
|
||||
.inspect_err(|e| debug!("{e}"));
|
||||
|
||||
match hash {
|
||||
| Ok(hash) => {
|
||||
if hash.is_empty() {
|
||||
return Err!(Request(UserDeactivated("The user has been deactivated")));
|
||||
}
|
||||
|
||||
hash::verify_password(password, &hash)
|
||||
.inspect_err(|e| debug!("{e}"))
|
||||
.map_err(|_| err!(Request(Forbidden("Wrong username or password."))))?;
|
||||
|
||||
user_id
|
||||
},
|
||||
| Err(_e) => {
|
||||
let hash_lowercased_user_id = services
|
||||
.users
|
||||
.password_hash(&lowercased_user_id)
|
||||
.await
|
||||
.inspect_err(|e| debug!("{e}"))
|
||||
.map_err(|_| err!(Request(Forbidden("Wrong username or password."))))?;
|
||||
|
||||
if hash_lowercased_user_id.is_empty() {
|
||||
return Err!(Request(UserDeactivated("The user has been deactivated")));
|
||||
}
|
||||
|
||||
hash::verify_password(password, &hash_lowercased_user_id)
|
||||
.inspect_err(|e| debug!("{e}"))
|
||||
.map_err(|_| err!(Request(Forbidden("Wrong username or password."))))?;
|
||||
|
||||
lowercased_user_id
|
||||
},
|
||||
}
|
||||
},
|
||||
}) => handle_login(&services, &body, identifier, password, user).await?,
|
||||
| login::v3::LoginInfo::Token(login::v3::Token { token }) => {
|
||||
debug!("Got token login type");
|
||||
if !services.server.config.login_via_existing_session {
|
||||
|
|
|
@ -292,7 +292,7 @@ pub(crate) async fn get_timezone_key_route(
|
|||
.await
|
||||
{
|
||||
if !services.users.exists(&body.user_id).await {
|
||||
services.users.create(&body.user_id, None)?;
|
||||
services.users.create(&body.user_id, None, None).await?;
|
||||
}
|
||||
|
||||
services
|
||||
|
@ -352,7 +352,7 @@ pub(crate) async fn get_profile_key_route(
|
|||
.await
|
||||
{
|
||||
if !services.users.exists(&body.user_id).await {
|
||||
services.users.create(&body.user_id, None)?;
|
||||
services.users.create(&body.user_id, None, None).await?;
|
||||
}
|
||||
|
||||
services
|
||||
|
|
|
@ -1948,6 +1948,10 @@ pub struct Config {
|
|||
pub allow_invalid_tls_certificates_yes_i_know_what_the_fuck_i_am_doing_with_this_and_i_know_this_is_insecure:
|
||||
bool,
|
||||
|
||||
// external structure; separate section
|
||||
#[serde(default)]
|
||||
pub ldap: LdapConfig,
|
||||
|
||||
// external structure; separate section
|
||||
#[serde(default)]
|
||||
pub blurhashing: BlurhashConfig,
|
||||
|
@ -2042,6 +2046,102 @@ pub struct BlurhashConfig {
|
|||
pub blurhash_max_raw_size: u64,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Deserialize)]
|
||||
#[config_example_generator(filename = "conduwuit-example.toml", section = "global.ldap")]
|
||||
pub struct LdapConfig {
|
||||
/// Whether to enable LDAP login.
|
||||
///
|
||||
/// example: "true"
|
||||
#[serde(default)]
|
||||
pub enable: bool,
|
||||
|
||||
/// URI of the LDAP server.
|
||||
///
|
||||
/// example: "ldap://ldap.example.com:389"
|
||||
pub uri: Option<Url>,
|
||||
|
||||
/// Root of the searches.
|
||||
///
|
||||
/// example: "ou=users,dc=example,dc=org"
|
||||
#[serde(default)]
|
||||
pub base_dn: String,
|
||||
|
||||
/// Bind DN if anonymous search is not enabled.
|
||||
///
|
||||
/// You can use the variable `{username}` that will be replaced by the
|
||||
/// entered username. In such case, the password used to bind will be the
|
||||
/// one provided for the login and not the one given by
|
||||
/// `bind_password_file`. Beware: automatically granting admin rights will
|
||||
/// not work if you use this direct bind instead of a LDAP search.
|
||||
///
|
||||
/// example: "cn=ldap-reader,dc=example,dc=org" or
|
||||
/// "cn={username},ou=users,dc=example,dc=org"
|
||||
#[serde(default)]
|
||||
pub bind_dn: Option<String>,
|
||||
|
||||
/// Path to a file on the system that contains the password for the
|
||||
/// `bind_dn`.
|
||||
///
|
||||
/// The server must be able to access the file, and it must not be empty.
|
||||
#[serde(default)]
|
||||
pub bind_password_file: Option<PathBuf>,
|
||||
|
||||
/// Search filter to limit user searches.
|
||||
///
|
||||
/// You can use the variable `{username}` that will be replaced by the
|
||||
/// entered username for more complex filters.
|
||||
///
|
||||
/// example: "(&(objectClass=person)(memberOf=matrix))"
|
||||
///
|
||||
/// default: "(objectClass=*)"
|
||||
#[serde(default = "default_ldap_search_filter")]
|
||||
pub filter: String,
|
||||
|
||||
/// Attribute to use to uniquely identify the user.
|
||||
///
|
||||
/// example: "uid" or "cn"
|
||||
///
|
||||
/// default: "uid"
|
||||
#[serde(default = "default_ldap_uid_attribute")]
|
||||
pub uid_attribute: String,
|
||||
|
||||
/// Attribute containing the mail of the user.
|
||||
///
|
||||
/// example: "mail"
|
||||
///
|
||||
/// default: "mail"
|
||||
#[serde(default = "default_ldap_mail_attribute")]
|
||||
pub mail_attribute: String,
|
||||
|
||||
/// Attribute containing the distinguished name of the user.
|
||||
///
|
||||
/// example: "givenName" or "sn"
|
||||
///
|
||||
/// default: "givenName"
|
||||
#[serde(default = "default_ldap_name_attribute")]
|
||||
pub name_attribute: String,
|
||||
|
||||
/// Root of the searches for admin users.
|
||||
///
|
||||
/// Defaults to `base_dn` if empty.
|
||||
///
|
||||
/// example: "ou=admins,dc=example,dc=org"
|
||||
#[serde(default)]
|
||||
pub admin_base_dn: String,
|
||||
|
||||
/// The LDAP search filter to find administrative users for conduwuit.
|
||||
///
|
||||
/// If left blank, administrative state must be configured manually for each
|
||||
/// user.
|
||||
///
|
||||
/// You can use the variable `{username}` that will be replaced by the
|
||||
/// entered username for more complex filters.
|
||||
///
|
||||
/// example: "(objectClass=conduwuitAdmin)" or "(uid={username})"
|
||||
#[serde(default)]
|
||||
pub admin_filter: String,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Clone, Debug)]
|
||||
#[serde(transparent)]
|
||||
struct ListeningPort {
|
||||
|
@ -2431,3 +2531,11 @@ pub(super) fn default_blurhash_x_component() -> u32 { 4 }
|
|||
pub(super) fn default_blurhash_y_component() -> u32 { 3 }
|
||||
|
||||
// end recommended & blurhashing defaults
|
||||
|
||||
fn default_ldap_search_filter() -> String { "(objectClass=*)".to_owned() }
|
||||
|
||||
fn default_ldap_uid_attribute() -> String { String::from("uid") }
|
||||
|
||||
fn default_ldap_mail_attribute() -> String { String::from("mail") }
|
||||
|
||||
fn default_ldap_name_attribute() -> String { String::from("givenName") }
|
||||
|
|
|
@ -110,6 +110,8 @@ pub enum Error {
|
|||
InconsistentRoomState(&'static str, ruma::OwnedRoomId),
|
||||
#[error(transparent)]
|
||||
IntoHttp(#[from] ruma::api::error::IntoHttpError),
|
||||
#[error("{0}")]
|
||||
Ldap(Cow<'static, str>),
|
||||
#[error(transparent)]
|
||||
Mxc(#[from] ruma::MxcUriError),
|
||||
#[error(transparent)]
|
||||
|
|
|
@ -374,6 +374,10 @@ pub(super) static MAPS: &[Descriptor] = &[
|
|||
name: "userid_masterkeyid",
|
||||
..descriptor::RANDOM_SMALL
|
||||
},
|
||||
Descriptor {
|
||||
name: "userid_origin",
|
||||
..descriptor::RANDOM
|
||||
},
|
||||
Descriptor {
|
||||
name: "userid_password",
|
||||
..descriptor::RANDOM
|
||||
|
|
|
@ -53,6 +53,9 @@ jemalloc_stats = [
|
|||
"conduwuit-core/jemalloc_stats",
|
||||
"conduwuit-database/jemalloc_stats",
|
||||
]
|
||||
ldap = [
|
||||
"dep:ldap3"
|
||||
]
|
||||
media_thumbnail = [
|
||||
"dep:image",
|
||||
]
|
||||
|
@ -89,6 +92,8 @@ image.workspace = true
|
|||
image.optional = true
|
||||
ipaddress.workspace = true
|
||||
itertools.workspace = true
|
||||
ldap3.workspace = true
|
||||
ldap3.optional = true
|
||||
log.workspace = true
|
||||
loole.workspace = true
|
||||
lru-cache.workspace = true
|
||||
|
|
|
@ -38,7 +38,7 @@ pub async fn create_admin_room(services: &Services) -> Result {
|
|||
|
||||
// Create a user for the server
|
||||
let server_user = services.globals.server_user.as_ref();
|
||||
services.users.create(server_user, None)?;
|
||||
services.users.create(server_user, None, None).await?;
|
||||
|
||||
let create_content = {
|
||||
use RoomVersionId::*;
|
||||
|
|
|
@ -41,6 +41,11 @@ impl crate::Service for Service {
|
|||
return Ok(());
|
||||
}
|
||||
|
||||
if self.services.config.ldap.enable {
|
||||
warn!("emergency password feature not available with LDAP enabled.");
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
self.set_emergency_access().await.inspect_err(|e| {
|
||||
error!("Could not set the configured emergency password for the server user: {e}");
|
||||
})
|
||||
|
@ -57,7 +62,8 @@ impl Service {
|
|||
|
||||
self.services
|
||||
.users
|
||||
.set_password(server_user, self.services.config.emergency_password.as_deref())?;
|
||||
.set_password(server_user, self.services.config.emergency_password.as_deref())
|
||||
.await?;
|
||||
|
||||
let (ruleset, pwd_set) = match self.services.config.emergency_password {
|
||||
| Some(_) => (Ruleset::server_default(server_user), true),
|
||||
|
|
|
@ -49,7 +49,7 @@ pub async fn update_membership(
|
|||
#[allow(clippy::collapsible_if)]
|
||||
if !self.services.globals.user_is_local(user_id) {
|
||||
if !self.services.users.exists(user_id).await {
|
||||
self.services.users.create(user_id, None)?;
|
||||
self.services.users.create(user_id, None, None).await?;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,11 +1,19 @@
|
|||
use std::{collections::BTreeMap, mem, sync::Arc};
|
||||
use std::{
|
||||
collections::{BTreeMap, HashMap},
|
||||
mem,
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
use conduwuit::{
|
||||
Err, Error, Result, Server, at, debug_warn, err, trace,
|
||||
Err, Error, Result, Server, at, debug_warn, err, is_equal_to,
|
||||
result::LogErr,
|
||||
trace,
|
||||
utils::{self, ReadyExt, stream::TryIgnore, string::Unquoted},
|
||||
};
|
||||
use conduwuit_core::{debug, error};
|
||||
use database::{Deserialized, Ignore, Interfix, Json, Map};
|
||||
use futures::{Stream, StreamExt, TryFutureExt};
|
||||
use ldap3::{LdapConnAsync, Scope, SearchEntry};
|
||||
use ruma::{
|
||||
DeviceId, KeyId, MilliSecondsSinceUnixEpoch, OneTimeKeyAlgorithm, OneTimeKeyId,
|
||||
OneTimeKeyName, OwnedDeviceId, OwnedKeyId, OwnedMxcUri, OwnedUserId, RoomId, UInt, UserId,
|
||||
|
@ -63,6 +71,7 @@ struct Data {
|
|||
userid_displayname: Arc<Map>,
|
||||
userid_lastonetimekeyupdate: Arc<Map>,
|
||||
userid_masterkeyid: Arc<Map>,
|
||||
userid_origin: Arc<Map>,
|
||||
userid_password: Arc<Map>,
|
||||
userid_suspension: Arc<Map>,
|
||||
userid_selfsigningkeyid: Arc<Map>,
|
||||
|
@ -100,6 +109,7 @@ impl crate::Service for Service {
|
|||
userid_displayname: args.db["userid_displayname"].clone(),
|
||||
userid_lastonetimekeyupdate: args.db["userid_lastonetimekeyupdate"].clone(),
|
||||
userid_masterkeyid: args.db["userid_masterkeyid"].clone(),
|
||||
userid_origin: args.db["userid_origin"].clone(),
|
||||
userid_password: args.db["userid_password"].clone(),
|
||||
userid_suspension: args.db["userid_suspension"].clone(),
|
||||
userid_selfsigningkeyid: args.db["userid_selfsigningkeyid"].clone(),
|
||||
|
@ -136,9 +146,21 @@ impl Service {
|
|||
}
|
||||
|
||||
/// Create a new user account on this homeserver.
|
||||
///
|
||||
/// User origin is by default "password" (meaning that it will login using
|
||||
/// its user_id/password). Users with other origins (currently only "ldap"
|
||||
/// is available) have special login processes.
|
||||
#[inline]
|
||||
pub fn create(&self, user_id: &UserId, password: Option<&str>) -> Result<()> {
|
||||
self.set_password(user_id, password)
|
||||
pub async fn create(
|
||||
&self,
|
||||
user_id: &UserId,
|
||||
password: Option<&str>,
|
||||
origin: Option<&str>,
|
||||
) -> Result<()> {
|
||||
self.db
|
||||
.userid_origin
|
||||
.insert(user_id, origin.unwrap_or("password"));
|
||||
self.set_password(user_id, password).await
|
||||
}
|
||||
|
||||
/// Deactivate account
|
||||
|
@ -152,7 +174,7 @@ impl Service {
|
|||
// result in an empty string, so the user will not be able to log in again.
|
||||
// Systems like changing the password without logging in should check if the
|
||||
// account is deactivated.
|
||||
self.set_password(user_id, None)?;
|
||||
self.set_password(user_id, None).await?;
|
||||
|
||||
// TODO: Unhook 3PID
|
||||
Ok(())
|
||||
|
@ -253,13 +275,34 @@ impl Service {
|
|||
.ready_filter_map(|(u, p): (&UserId, &[u8])| (!p.is_empty()).then_some(u))
|
||||
}
|
||||
|
||||
/// Returns the origin of the user (password/LDAP/...).
|
||||
pub async fn origin(&self, user_id: &UserId) -> Result<String> {
|
||||
self.db.userid_origin.get(user_id).await.deserialized()
|
||||
}
|
||||
|
||||
/// Returns the password hash for the given user.
|
||||
pub async fn password_hash(&self, user_id: &UserId) -> Result<String> {
|
||||
self.db.userid_password.get(user_id).await.deserialized()
|
||||
}
|
||||
|
||||
/// Hash and set the user's password to the Argon2 hash
|
||||
pub fn set_password(&self, user_id: &UserId, password: Option<&str>) -> Result<()> {
|
||||
pub async fn set_password(&self, user_id: &UserId, password: Option<&str>) -> Result<()> {
|
||||
// Cannot change the password of a LDAP user. There are two special cases :
|
||||
// - a `None` password can be used to deactivate a LDAP user
|
||||
// - a "*" password is used as the default password of an active LDAP user
|
||||
if cfg!(feature = "ldap")
|
||||
&& password.is_some_and(|pwd| pwd != "*")
|
||||
&& self
|
||||
.db
|
||||
.userid_origin
|
||||
.get(user_id)
|
||||
.await
|
||||
.deserialized::<String>()
|
||||
.is_ok_and(is_equal_to!("ldap"))
|
||||
{
|
||||
return Err!(Request(InvalidParam("Cannot change password of a LDAP user")));
|
||||
}
|
||||
|
||||
password
|
||||
.map(utils::hash::password)
|
||||
.transpose()
|
||||
|
@ -1132,6 +1175,154 @@ impl Service {
|
|||
self.db.useridprofilekey_value.del(key);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "ldap"))]
|
||||
pub async fn search_ldap(&self, _user_id: &UserId) -> Result<Vec<(String, bool)>> {
|
||||
Err!(FeatureDisabled("ldap"))
|
||||
}
|
||||
|
||||
#[cfg(feature = "ldap")]
|
||||
pub async fn search_ldap(&self, user_id: &UserId) -> Result<Vec<(String, bool)>> {
|
||||
let localpart = user_id.localpart().to_owned();
|
||||
let lowercased_localpart = localpart.to_lowercase();
|
||||
|
||||
let config = &self.services.server.config.ldap;
|
||||
let uri = config
|
||||
.uri
|
||||
.as_ref()
|
||||
.ok_or_else(|| err!(Ldap(error!("LDAP URI is not configured."))))?;
|
||||
|
||||
debug!(?uri, "LDAP creating connection...");
|
||||
let (conn, mut ldap) = LdapConnAsync::new(uri.as_str())
|
||||
.await
|
||||
.map_err(|e| err!(Ldap(error!(?user_id, "LDAP connection setup error: {e}"))))?;
|
||||
|
||||
let driver = self.services.server.runtime().spawn(async move {
|
||||
match conn.drive().await {
|
||||
| Err(e) => error!("LDAP connection error: {e}"),
|
||||
| Ok(()) => debug!("LDAP connection completed."),
|
||||
}
|
||||
});
|
||||
|
||||
match (&config.bind_dn, &config.bind_password_file) {
|
||||
| (Some(bind_dn), Some(bind_password_file)) => {
|
||||
let bind_pw = String::from_utf8(std::fs::read(bind_password_file)?)?;
|
||||
ldap.simple_bind(bind_dn, bind_pw.trim())
|
||||
.await
|
||||
.and_then(ldap3::LdapResult::success)
|
||||
.map_err(|e| err!(Ldap(error!("LDAP bind error: {e}"))))?;
|
||||
},
|
||||
| (..) => {},
|
||||
}
|
||||
|
||||
let attr = [&config.uid_attribute, &config.name_attribute];
|
||||
|
||||
let user_filter = &config.filter.replace("{username}", &lowercased_localpart);
|
||||
|
||||
let (entries, _result) = ldap
|
||||
.search(&config.base_dn, Scope::Subtree, user_filter, &attr)
|
||||
.await
|
||||
.and_then(ldap3::SearchResult::success)
|
||||
.inspect(|(entries, result)| trace!(?entries, ?result, "LDAP Search"))
|
||||
.map_err(|e| err!(Ldap(error!(?attr, ?user_filter, "LDAP search error: {e}"))))?;
|
||||
|
||||
let mut dns: HashMap<String, bool> = entries
|
||||
.into_iter()
|
||||
.filter_map(|entry| {
|
||||
let search_entry = SearchEntry::construct(entry);
|
||||
debug!(?search_entry, "LDAP search entry");
|
||||
search_entry
|
||||
.attrs
|
||||
.get(&config.uid_attribute)
|
||||
.into_iter()
|
||||
.chain(search_entry.attrs.get(&config.name_attribute))
|
||||
.any(|ids| ids.contains(&localpart) || ids.contains(&lowercased_localpart))
|
||||
.then_some((search_entry.dn, false))
|
||||
})
|
||||
.collect();
|
||||
|
||||
if !config.admin_filter.is_empty() {
|
||||
let admin_base_dn = if config.admin_base_dn.is_empty() {
|
||||
&config.base_dn
|
||||
} else {
|
||||
&config.admin_base_dn
|
||||
};
|
||||
|
||||
let admin_filter = &config
|
||||
.admin_filter
|
||||
.replace("{username}", &lowercased_localpart);
|
||||
|
||||
let (admin_entries, _result) = ldap
|
||||
.search(admin_base_dn, Scope::Subtree, admin_filter, &attr)
|
||||
.await
|
||||
.and_then(ldap3::SearchResult::success)
|
||||
.inspect(|(entries, result)| trace!(?entries, ?result, "LDAP Admin Search"))
|
||||
.map_err(|e| {
|
||||
err!(Ldap(error!(?attr, ?admin_filter, "Ldap admin search error: {e}")))
|
||||
})?;
|
||||
|
||||
dns.extend(admin_entries.into_iter().filter_map(|entry| {
|
||||
let search_entry = SearchEntry::construct(entry);
|
||||
debug!(?search_entry, "LDAP search entry");
|
||||
search_entry
|
||||
.attrs
|
||||
.get(&config.uid_attribute)
|
||||
.into_iter()
|
||||
.chain(search_entry.attrs.get(&config.name_attribute))
|
||||
.any(|ids| ids.contains(&localpart) || ids.contains(&lowercased_localpart))
|
||||
.then_some((search_entry.dn, true))
|
||||
}));
|
||||
}
|
||||
|
||||
ldap.unbind()
|
||||
.await
|
||||
.map_err(|e| err!(Ldap(error!("LDAP unbind error: {e}"))))?;
|
||||
|
||||
driver.await.log_err().ok();
|
||||
|
||||
Ok(dns.drain().collect())
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "ldap"))]
|
||||
pub async fn auth_ldap(&self, _user_dn: &str, _password: &str) -> Result {
|
||||
Err!(FeatureDisabled("ldap"))
|
||||
}
|
||||
|
||||
#[cfg(feature = "ldap")]
|
||||
pub async fn auth_ldap(&self, user_dn: &str, password: &str) -> Result {
|
||||
let config = &self.services.server.config.ldap;
|
||||
let uri = config
|
||||
.uri
|
||||
.as_ref()
|
||||
.ok_or_else(|| err!(Ldap(error!("LDAP URI is not configured."))))?;
|
||||
|
||||
debug!(?uri, "LDAP creating connection...");
|
||||
let (conn, mut ldap) = LdapConnAsync::new(uri.as_str())
|
||||
.await
|
||||
.map_err(|e| err!(Ldap(error!(?user_dn, "LDAP connection setup error: {e}"))))?;
|
||||
|
||||
let driver = self.services.server.runtime().spawn(async move {
|
||||
match conn.drive().await {
|
||||
| Err(e) => error!("LDAP connection error: {e}"),
|
||||
| Ok(()) => debug!("LDAP connection completed."),
|
||||
}
|
||||
});
|
||||
|
||||
ldap.simple_bind(user_dn, password)
|
||||
.await
|
||||
.and_then(ldap3::LdapResult::success)
|
||||
.map_err(|e| {
|
||||
err!(Request(Forbidden(debug_error!("LDAP authentication error: {e}"))))
|
||||
})?;
|
||||
|
||||
ldap.unbind()
|
||||
.await
|
||||
.map_err(|e| err!(Ldap(error!("LDAP unbind error: {e}"))))?;
|
||||
|
||||
driver.await.log_err().ok();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_master_key(
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue