LDAP support #921

Merged
nex merged 6 commits from RatCornu/continuwuity:ldap into main 2025-08-23 20:01:34 +00:00
Contributor

Hi!

I implemented few monthes ago LDAP login for tuwunel and I discovered after this. I would like to add the LDAP feature here to: I tried to stick as much as possible with continuwuity code style starting from the tuwunel implementation, but I'm not sure if everything is alright: I'll change everything needed to comply to your standards.

I tested basic login on a real LDAP server with cURL and it works. As the implementation is litteraly the same as in tuwunel, I would expect other features to work as inteded.

Closes #691.

Hi! I implemented few monthes ago LDAP login for tuwunel and I discovered after this. I would like to add the LDAP feature here to: I tried to stick as much as possible with continuwuity code style starting from the tuwunel implementation, but I'm not sure if everything is alright: I'll change everything needed to comply to your standards. I tested basic login on a real LDAP server with cURL and it works. As the implementation is litteraly the same as in tuwunel, I would expect other features to work as inteded. Closes #691.
Owner

Hey, great to see you! Thanks for the PR - looks like the preflight checks ran fine, so your style was perfectly fine. I'll just give this a quick test later (hopefully tomorrow), then should be good to get it merged :)

Hey, great to see you! Thanks for the PR - looks like the preflight checks ran fine, so your style was perfectly fine. I'll just give this a quick test later (hopefully tomorrow), then should be good to get it merged :)
Author
Contributor

Nice! Moreover, do not hesitate to ping for LDAP issues if needed, I'll be using continuwuity + LDAP in near future and I'm willing to help wherever I can ^^

Nice! Moreover, do not hesitate to ping for LDAP issues if needed, I'll be using continuwuity + LDAP in near future and I'm willing to help wherever I can ^^
Jade approved these changes 2025-08-10 00:43:30 +00:00
Jade left a comment
Owner

From my late night phone review, it all looks pretty good to me. Just some minor tweaks. (I've also rambled a bit in some of the comments)

From my late night phone review, it all looks pretty good to me. Just some minor tweaks. (I've also rambled a bit in some of the comments)
@ -1777,0 +1838,4 @@
#
# example: "mail"
#
#mail_attribute = "mail"
Owner

I feel like this should be optional? We don't need email, and not every system will have it

I feel like this should be optional? We don't need email, and not every system will have it
Owner

Oh yeah we don't even use it in this diff afaict, probably worth removing entirely or hiding

Oh yeah we don't even use it in this diff afaict, probably worth removing entirely or hiding
Author
Contributor

This is a discussion in the tuwunel implementation: https://github.com/matrix-construct/tuwunel/issues/114.
I can remove it for now and add it back later if a way to store custom profile fields is added into continuwuity

This is a discussion in the tuwunel implementation: https://github.com/matrix-construct/tuwunel/issues/114. I can remove it for now and add it back later if a way to store custom profile fields is added into continuwuity
Author
Contributor

I removed it for now, it's always possible to add it back later

I removed it for now, it's always possible to add it back later
RatCornu marked this conversation as resolved
@ -1777,0 +1840,4 @@
#
#mail_attribute = "mail"
# Attribute containing the distinguished name of the user.
Owner

Distinguished name being display name? Might be better to use Matrix terminology here, if that's what you mean.

Distinguished name being display name? Might be better to use Matrix terminology here, if that's what you mean.
Author
Contributor

Indeed, distinguished name is the common terminology when dealing with LDAP, I change this to display name

Indeed, distinguished name is the common terminology when dealing with LDAP, I change this to display name
RatCornu marked this conversation as resolved
@ -1777,0 +1854,4 @@
#
#admin_base_dn = false
# The LDAP search filter to find administrative users for conduwuit.
Owner

Continuwuity

Continuwuity
Jade marked this conversation as resolved
@ -52,0 +75,4 @@
| Ok(hash) => (hash, user_id),
| Err(_) => services
.users
.password_hash(lowercased_user_id)
Owner

In what circumstances is this case sensitive, and when is it not? Might need some explainer comments here.

In what circumstances is this case sensitive, and when is it not? Might need some explainer comments here.
Owner

I see, this is copying logic from the original function. Ah well.

I see, this is copying logic from the original function. Ah well.
Jade marked this conversation as resolved
@ -52,0 +136,4 @@
// 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
Owner

Ugh this feels hacky and like it will bite in the future, I know it's a bit out of scope but definitely a thing to improve if we get the opportunity in future. In a new database version number we could do a migration for it.

Ugh this feels hacky and like it will bite in the future, I know it's a bit out of scope but definitely a thing to improve if we get the opportunity in future. In a new database version number we could do a migration for it.
Owner

Oh also in the event of a downgrade the logic behind this being unreachable doesn't work, because the account origin won't be checked anymore. It's still probably fine because it's all or nothing and people aren't likely to downgrade off an LDAP supporting version when their server relies on it, but this wouldn't work for OIDC for example, or for partial rather than total server management.

I would say that this requires an upgrade in database version number, but tuwunel didn't do it when they added this afaik, so... I just hope no one uses LDAP, downgrades to a version without it, and expects things to work.

Oh also in the event of a downgrade the logic behind this being unreachable doesn't work, because the account origin won't be checked anymore. It's still probably fine because it's all or nothing and people aren't likely to downgrade off an LDAP supporting version when their server relies on it, but this wouldn't work for OIDC for example, or for partial rather than total server management. I would say that this requires an upgrade in database version number, but tuwunel didn't do it when they added this afaik, so... I just hope no one uses LDAP, downgrades to a version without it, and expects things to work.
Jade marked this conversation as resolved
@ -52,0 +185,4 @@
}
if cfg!(feature = "ldap") && services.config.ldap.enable {
ldap_login(services, &user_id, &lowercased_user_id, password).await
Owner

That it's 'all or nothing' is probably something to note in any documentation.

That it's 'all or nothing' is probably something to note in any documentation.
Author
Contributor

It's also possible to try normal password login if LDAP login fails if you prefer, both implementations are possible

It's also possible to try normal password login if LDAP login fails if you prefer, both implementations are possible
Author
Contributor

I added an option ldap_only making things clearer

I added an option `ldap_only` making things clearer
RatCornu marked this conversation as resolved
@ -2044,0 +2120,4 @@
#[serde(default = "default_ldap_name_attribute")]
pub name_attribute: String,
/// Root of the searches for admin users.
Owner

All these ones without default at the last line should probably have it so the example config doesn't just say = false for each item.

All these ones without default at the last line should probably have it so the example config doesn't just say `= false` for each item.
Owner

OK so what I meant here @RatCornu is that for the example config, you can write default: something and that will show up in the config as option = something. Like in one of them you've got default: "(objectClass=*)" and in teh config it shows up as filter = "(objectClass=*)". In any case, I'm not sure why these were showing up as false when in other places that do the same they show as blank.

OK so what I meant here @RatCornu is that for the example config, you can write `default: something` and that will show up in the config as `option = something`. Like in one of them you've got `default: "(objectClass=*)"` and in teh config it shows up as `filter = "(objectClass=*)"`. In any case, I'm not sure why these were showing up as false when in other places that do the same they show as blank.
Author
Contributor

It should be fixed!

It should be fixed!
RatCornu marked this conversation as resolved
Owner

Oh, I've just noticed that formatting ci hasn't run for whatever reason. I'm going to need to run cargo format plus cargo clippy manually lol

Oh, I've just noticed that formatting ci hasn't run for whatever reason. I'm going to need to run cargo format plus cargo clippy manually lol
Owner

Isn't that part of the preflight check?

Isn't that part of the preflight check?
Owner

Nope, there are some formatting checks there, but in CI rustfmt has its own step

Nope, there are some formatting checks there, but in CI rustfmt has its own step
Jade force-pushed ldap from 0d4c5adc5e
Some checks failed
Documentation / Build and Deploy Documentation (pull_request) Failing after 0s
Checks / Prefligit / prefligit (pull_request) Successful in 24s
to 785789afb5
Some checks failed
Documentation / Build and Deploy Documentation (pull_request) Failing after 0s
Checks / Prefligit / prefligit (pull_request) Successful in 14s
2025-08-10 19:37:42 +00:00
Compare
Jade force-pushed ldap from d39579ea47
Some checks failed
Documentation / Build and Deploy Documentation (pull_request) Failing after 0s
Checks / Prefligit / prefligit (pull_request) Successful in 14s
to 1a21f4a852
Some checks failed
Documentation / Build and Deploy Documentation (pull_request) Failing after 0s
Checks / Prefligit / prefligit (pull_request) Successful in 16s
2025-08-10 20:09:56 +00:00
Compare
Owner

OK I've fixed some issues preventing it from building, etc. It should be good for testing now - it's now included in the default features.

OK I've fixed some issues preventing it from building, etc. It should be good for testing now - it's now included in the default features.
RatCornu force-pushed ldap from 94cde15ab2
Some checks failed
Documentation / Build and Deploy Documentation (pull_request) Failing after 0s
Checks / Prefligit / prefligit (pull_request) Successful in 26s
to 976387ab33
Some checks failed
Documentation / Build and Deploy Documentation (pull_request) Failing after 0s
Checks / Prefligit / prefligit (pull_request) Successful in 25s
2025-08-14 20:55:58 +00:00
Compare
Owner

Are you sure you meant to make those nix changes in the last commit?

Are you sure you meant to make those nix changes in the last commit?
RatCornu force-pushed ldap from 976387ab33
Some checks failed
Documentation / Build and Deploy Documentation (pull_request) Failing after 0s
Checks / Prefligit / prefligit (pull_request) Successful in 25s
to 4055fbe827
Some checks failed
Documentation / Build and Deploy Documentation (pull_request) Failing after 0s
Checks / Prefligit / prefligit (pull_request) Successful in 20s
2025-08-14 21:05:09 +00:00
Compare
Author
Contributor

Oops indeed sorry, I made git add . too quickly ^^'

Oops indeed sorry, I made `git add .` too quickly ^^'
Owner

Awesome :)

Awesome :)
RatCornu force-pushed ldap from 4055fbe827
Some checks failed
Documentation / Build and Deploy Documentation (pull_request) Failing after 0s
Checks / Prefligit / prefligit (pull_request) Successful in 20s
to 669fa34a0a
Some checks failed
Documentation / Build and Deploy Documentation (pull_request) Failing after 0s
Checks / Prefligit / prefligit (pull_request) Successful in 33s
2025-08-14 21:12:52 +00:00
Compare
nex added this to the v0.5.0-rc.8 milestone 2025-08-16 16:10:01 +00:00
nex approved these changes 2025-08-18 04:01:37 +00:00
nex left a comment
Owner

Nobody's raised any complaints after a call for testing, and the code looks good for me, so I'm happy to merge this in later today/tomorrow.

Nobody's raised any complaints after a call for testing, and the code looks good for me, so I'm happy to merge this in later today/tomorrow.
nex force-pushed ldap from 669fa34a0a
Some checks failed
Documentation / Build and Deploy Documentation (pull_request) Failing after 0s
Checks / Prefligit / prefligit (pull_request) Successful in 33s
to 57d7743037
Some checks failed
Documentation / Build and Deploy Documentation (pull_request) Has been skipped
Checks / Prek / Pre-commit & Formatting (pull_request) Successful in 15s
Release Docker Image / define-variables (push) Successful in 3s
Checks / Prek / Pre-commit & Formatting (push) Successful in 14s
Documentation / Build and Deploy Documentation (push) Successful in 33s
Checks / Rust / Format (push) Successful in 36s
Checks / Rust / Cargo Test (push) Failing after 4m40s
Checks / Rust / Clippy (push) Failing after 4m45s
Release Docker Image / build-image (linux/amd64, release, linux-amd64, base) (push) Failing after 9m28s
Release Docker Image / build-image (linux/arm64, release, linux-arm64, base) (push) Failing after 9m33s
Release Docker Image / merge (push) Has been skipped
2025-08-23 19:59:37 +00:00
Compare
nex scheduled this pull request to auto merge when all checks succeed 2025-08-23 19:59:46 +00:00
nex merged commit 57d7743037 into main 2025-08-23 20:01:34 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
4 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
continuwuation/continuwuity!921
No description provided.