Skip to content

Commit 0ca0220

Browse files
committed
Attempt LDAP bind if password hash isn't Argon2
When using an LDAP server with a password hash scheme other than Argon2, Quality-time would not attempt an LDAP bind to verify the user. Fixes #12595.
1 parent 6c7b764 commit 0ca0220

File tree

6 files changed

+23
-11
lines changed

6 files changed

+23
-11
lines changed

components/api_server/src/routes/auth.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,10 +54,14 @@ def set_session_cookie(session_id: SessionId, expires_datetime: datetime) -> Non
5454
bottle.response.set_cookie("session_id", session_id, **options)
5555

5656

57-
def check_password(password_hash, password) -> bool:
57+
def check_password(password_hash: bytes, password: str) -> bool:
5858
"""Check the OpenLDAP password hash against the given password."""
5959
# Note we currently only support ARGON2 hashes
60-
return argon2.PasswordHasher().verify(password_hash.decode().removeprefix("{ARGON2}"), password)
60+
hash_prefix = "{ARGON2}"
61+
decoded_hash = password_hash.decode()
62+
if decoded_hash.startswith(hash_prefix):
63+
return argon2.PasswordHasher().verify(decoded_hash.removeprefix(hash_prefix), password)
64+
return False
6165

6266

6367
def get_credentials() -> tuple[str, str]:

components/api_server/tests/routes/test_auth.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -198,17 +198,17 @@ def test_login_search_error(self, logging_mock, connection_mock, connection_ente
198198
self.ldap_connection.bind.assert_called_once()
199199
self.assert_log(logger.exception, exceptions.LDAPResponseTimeoutError)
200200

201-
@patch("logging.getLogger")
202-
def test_login_password_hash_error(self, logging_mock, connection_mock, connection_enter):
203-
"""Test login fails when LDAP password hash is not ARGON2."""
204-
logger = logging_mock.return_value = Mock()
201+
@patch("routes.auth.datetime", MOCK_DATETIME)
202+
def test_login_no_argon2(self, connection_mock, connection_enter):
203+
"""Test login proceeds with LDAP bind when LDAP password hash is not ARGON2."""
205204
connection_mock.return_value = None
206205
self.ldap_entry.userPassword.value = b"{XSHA}whatever-here"
207206
connection_enter.return_value = self.ldap_connection
208-
self.assertEqual(self.login_nok, login(self.database))
207+
self.assertEqual(self.login_ok, login(self.database))
208+
self.assert_cookie_has_session_id()
209+
self.assert_ldap_lookup_connection_created(connection_mock)
210+
self.assert_ldap_bind_connection_created(connection_mock)
209211
self.assert_ldap_connection_search_called()
210-
self.assertEqual(self.LOG_ERROR_MESSAGE_TEMPLATE, logger.exception.call_args_list[0][0][0])
211-
self.assert_log(logger.exception, argon2.exceptions.InvalidHashError)
212212

213213
@patch("logging.getLogger")
214214
def test_login_wrong_password(self, logging_mock, connection_mock, connection_enter):

docs/src/changelog.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,12 @@ If your currently installed *Quality-time* version is not the penultimate versio
1010

1111
<!-- The line "## <square-bracket>Unreleased</square-bracket>" is replaced by the release/release.py script with the new release version and release date. -->
1212

13+
## [Unreleased]
14+
15+
### Fixed
16+
17+
- When using an LDAP server with a password hash scheme other than Argon2, Quality-time would not attempt an LDAP bind to verify the user. Fixes [#12595](https://github.com/ICTU/quality-time/issues/12595).
18+
1319
## v5.48.3 - 2026-01-29
1420

1521
### Fixed

docs/src/conf.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,4 +88,5 @@
8888
# 404 Client Error: Not Found
8989
# See https://community.sonarsource.com/t/https-rules-sonarsource-com-down/177294?u=fniessink:
9090
"https://rules.sonarsource.com/.*",
91+
"https://www.ictu.nl/en/about-us", # False negative: 415 Client Error: Unsupported Media Type
9192
]

docs/src/deployment.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ See [https://ldap.com/ldap-filters/](https://ldap.com/ldap-filters/) for more in
9292
Quality-time tries two methods to authenticate users:
9393

9494
- If the LDAP-server returns the `userPassword` (containing a hash of the users' password), Quality-time uses that to verify the password. Note that currently only `ARGON2` hashes are supported. Please submit a feature request if you need support for another type of hash.
95-
- If the `userPassword` is not returned, Quality-time attempts an LDAP-bind.
95+
- If the `userPassword` is not returned or it is no `ARGON2` hash, Quality-time attempts an LDAP-bind operation using the user's distinguished name as returned by the LDAP-server and the password entered by the user.
9696

9797
```{index} Forwarded Authentication
9898
```

docs/src/versioning.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@ The MongoDB version, the MongoDB feature compatibility, and the migrations all l
2929

3030
| Version | Date | Mongo | FC | Migrations | Max. downgrade | Max. upgrade | Manual changes |
3131
|------------|------------|--------|--------|-------------|----------------|--------------|----------------|
32-
| v5.48.3 | 2026-01-29 | v8 | v8 | | v5.47.2 | n/a | no |
32+
| v5.48.4 | 2026-02-06 | v8 | v8 | | v5.47.2 | n/a | no |
33+
| v5.48.3 | 2026-01-29 | v8 | v8 | | v5.47.2 | latest | no |
3334
| v5.48.2 | 2026-01-09 | v8 | v8 | | v5.47.2 | latest | no |
3435
| v5.48.1 | 2025-12-19 | v8 | v8 | | v5.47.2 | latest | no |
3536
| v5.48.0 | 2025-12-12 | v8 | v8 | | v5.47.2 | latest | no |

0 commit comments

Comments
 (0)