[PR #1193] [CLOSED] server,app: add user account disable/enable functionality #1173

Closed
opened 2026-02-27 09:11:11 +03:00 by kerem · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/lldap/lldap/pull/1193
Author: @coolguy1771
Created: 6/24/2025
Status: Closed

Base: mainHead: feature/user-account-disable


📝 Commits (10+)

  • a1205b8 server,app: add user account disable/enable functionality
  • e91f706 chore: run cargo formatting
  • ba99bc7 fix: revert version to 0.6.2-alpha in Cargo.toml for app and server
  • c8a31f1 fix: revert lldap and lldap_app versions to 0.6.2-alpha in Cargo.lock
  • c031b7e feat: Implement login_enabled attribute for user management
  • 126f20d feat: Add session-aware backend handler to manage user session invalidation on login status change
  • 421921d Merge branch 'main' into feature/user-account-disable
  • 36576cc Refactor access control logic and improve error messages
  • 6d961a1 Update server/src/session_aware_backend_handler.rs
  • 2978369 Update app/src/components/user_details_form.rs

📊 Changes

39 files changed (+1596 additions, -92 deletions)

View changed files

📝 app/queries/get_user_details.graphql (+1 -0)
📝 app/queries/list_users.graphql (+1 -0)
app/queries/set_user_login_enabled.graphql (+5 -0)
📝 app/src/components/form/attribute_input.rs (+41 -7)
📝 app/src/components/form/date_input.rs (+16 -5)
📝 app/src/components/form/file_input.rs (+13 -1)
📝 app/src/components/user_details_form.rs (+1 -0)
📝 app/src/components/user_table.rs (+12 -0)
📝 app/src/infra/schema.rs (+1 -0)
📝 crates/access-control/src/lib.rs (+13 -6)
📝 crates/auth/src/access_control.rs (+6 -0)
📝 crates/domain-model/src/model/deserialize.rs (+6 -0)
📝 crates/domain-model/src/model/users.rs (+5 -0)
📝 crates/domain/src/deserialize.rs (+9 -0)
📝 crates/domain/src/public_schema.rs (+9 -0)
📝 crates/domain/src/requests.rs (+1 -0)
📝 crates/domain/src/types.rs (+25 -0)
📝 crates/graphql-server/Cargo.toml (+7 -3)
📝 crates/graphql-server/src/api.rs (+19 -15)
📝 crates/graphql-server/src/mutation.rs (+343 -13)

...and 19 more files

📄 Description

Summary

Implements comprehensive user account disable/enable functionality with automatic session invalidation to allow administrators to
prevent users from logging in and immediately revoke existing sessions without deleting their accounts.

Closes #750

Changes

Backend (Server)

  • Add login_enabled boolean field to User domain model and database schema
  • Implement database migration (v11) to add login_enabled column with safe defaults (true)
  • Update authentication handlers to reject login attempts for disabled users
  • Extend GraphQL schema with loginEnabled field in User type and UpdateUserInput
  • Add login_enabled field support to LDAP protocol attribute mapping
  • Implement automatic session invalidation: When users are disabled, all their existing JWT sessions are immediately blacklisted
  • Add admin protection logic: prevent disabling admin users if they're the only member of critical groups
  • Allow lldap_password_manager group members to disable/enable user accounts
  • Include comprehensive test coverage for disable/enable functionality

Frontend (App)

  • Add login status column to user table with visual indicators
  • Implement toggle switch in user details form for authorized users
  • Update GraphQL queries to include loginEnabled field
  • Add proper form state management for checkbox-based toggle
  • Show appropriate controls based on user permissions (admin/password manager)

Session Management

  • Automatic session invalidation: SessionAwareBackendHandler wrapper automatically invalidates all user sessions when login_enabled is
    set to false
  • Updates both database JWT blacklist and in-memory cache for immediate effect
  • Uses existing JWT blacklisting infrastructure for consistency and security

Features

  • Role-based control: Administrators and password managers can disable/enable user accounts
  • Authentication blocking: Disabled users cannot authenticate via LDAP or OPAQUE protocols
  • Immediate session invalidation: Existing sessions are terminated when user is disabled
  • Admin protection: Prevents system lockout by protecting the last admin user
  • Visual feedback: Clear UI indicators (enabled/disabled status)
  • Backward compatibility: Database migration ensures existing users remain enabled
  • LDAP support: Login status is queryable via LDAP search filters
  • Immediate feedback: Toggle updates instantly in the UI with proper form handling

LDAP Usage

Query disabled users:

ldapsearch -b "ou=people,dc=example,dc=com" "(login_enabled=FALSE)" uid login_enabled

Filter out disabled users:

ldapsearch -b "ou=people,dc=example,dc=com" "(&(objectClass=person)(login_enabled=TRUE))" uid

Security Features

  • Session invalidation: Disabled users are immediately logged out of all sessions
  • Admin protection: Cannot disable the last admin user to prevent system lockout
  • Permission-based access: Only admins and password managers can modify login status
  • Immediate enforcement: Changes take effect instantly without requiring restart

Testing

  • Unit tests for backend disable/enable functionality
  • Authentication rejection tests for disabled users
  • Session invalidation tests
  • Admin protection logic tests
  • Database migration tests
  • Frontend toggle functionality
  • LDAP attribute mapping tests

Breaking Changes

None - this is a backward compatible feature addition.

Database Migration

  • Migration v11 adds login_enabled column with DEFAULT true
  • All existing users remain enabled after migration
  • No manual intervention required

Implementation Details

Session Invalidation Architecture

  • SessionAwareBackendHandler wraps the SQL backend handler
  • Intercepts update_user calls and detects when login_enabled changes to false
  • Automatically calls blacklist_jwts() to mark user's JWT tokens as invalid in database
  • Updates in-memory JWT blacklist cache for immediate session termination
  • Maintains full compatibility with existing authentication flow

Admin Protection Logic

  • Checks if user being disabled is in lldap_admin or lldap_password_manager groups
  • Counts remaining enabled users in these critical groups
  • Prevents disabling if it would leave the group empty
  • Ensures system remains administrable

Screenshots

User Table with Status Column

Screenshot 2025-06-29 at 11 28 15 PM

Disabled user trying to log in

Screenshot 2025-06-30 at 12 07 40 AM
2025-06-30T04:07:34.632435+00:00     DEBUG    HTTP request [ 7.33ms | 10.86% / 100.00% ] method: "POST" | uri: "/auth/opaque/login/start"
2025-06-30T04:07:34.633034+00:00     DEBUG    ┝━ opaque_login_start [ 6.54ms | 1.21% / 89.14% ]
2025-06-30T04:07:34.633054+00:00     DEBUG    │  ┕━ login_start [ 6.45ms | 76.15% / 87.93% ]
2025-06-30T04:07:34.633092+00:00     INFO     │     ┝━ i [info]: OPAQUE login attempt for "admin2"
2025-06-30T04:07:34.633106+00:00     DEBUG    │     ┕━ get_password_file_for_user [ 864µs | 11.78% ] user_id: "admin2"
2025-06-30T04:07:34.640386+00:00     DEBUG    ┕━ 🐛 [debug]:  | status_code: 200
2025-06-30T04:07:34.713384+00:00     DEBUG    HTTP request [ 1.32ms | 21.66% / 100.00% ] method: "POST" | uri: "/auth/opaque/login/finish"
2025-06-30T04:07:34.713623+00:00     DEBUG    ┝━ opaque_login_finish [ 1.03ms | 10.04% / 78.34% ]
2025-06-30T04:07:34.713645+00:00     DEBUG    │  ┕━ login_finish [ 901µs | 32.88% / 68.29% ]
2025-06-30T04:07:34.714097+00:00     DEBUG    │     ┝━ check_user_enabled [ 467µs | 35.41% ] user_id: "admin2"
2025-06-30T04:07:34.714744+00:00     WARN     │     ┝━ 🚧 [warn]: OPAQUE login attempt for user with login blocked "admin2"
2025-06-30T04:07:34.714760+00:00     ERROR    │     ┕━ 🚨 [error]:  | error: Authentication error Login blocked for user "admin2"
2025-06-30T04:07:34.714936+00:00     DEBUG    ┕━ 🐛 [debug]:  | status_code: 401

🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/lldap/lldap/pull/1193 **Author:** [@coolguy1771](https://github.com/coolguy1771) **Created:** 6/24/2025 **Status:** ❌ Closed **Base:** `main` ← **Head:** `feature/user-account-disable` --- ### 📝 Commits (10+) - [`a1205b8`](https://github.com/lldap/lldap/commit/a1205b81332eadf3c52495f2d6c08be083c7b02f) server,app: add user account disable/enable functionality - [`e91f706`](https://github.com/lldap/lldap/commit/e91f706baa08fc850716f52f6ff846b03d6a3aed) chore: run cargo formatting - [`ba99bc7`](https://github.com/lldap/lldap/commit/ba99bc7f310901145ba0aef41f44d1d32aaefdc2) fix: revert version to 0.6.2-alpha in Cargo.toml for app and server - [`c8a31f1`](https://github.com/lldap/lldap/commit/c8a31f1f1d05f76008d6f2dd88958966f5ddfbfe) fix: revert lldap and lldap_app versions to 0.6.2-alpha in Cargo.lock - [`c031b7e`](https://github.com/lldap/lldap/commit/c031b7ef928088af44d9e98fec6b8fded1f821c2) feat: Implement login_enabled attribute for user management - [`126f20d`](https://github.com/lldap/lldap/commit/126f20dcad14ea45cccd4b2af899ba55c8a3a80c) feat: Add session-aware backend handler to manage user session invalidation on login status change - [`421921d`](https://github.com/lldap/lldap/commit/421921d0f668a3129de4312e290dff62d435a74d) Merge branch 'main' into feature/user-account-disable - [`36576cc`](https://github.com/lldap/lldap/commit/36576cc6a22d80b0124da8050f115d3d873070f7) Refactor access control logic and improve error messages - [`6d961a1`](https://github.com/lldap/lldap/commit/6d961a11b376c6bb52de46df6bb544b21d1d03e0) Update server/src/session_aware_backend_handler.rs - [`2978369`](https://github.com/lldap/lldap/commit/29783697a9a2a9e93419f8af24ac19a2f7401a04) Update app/src/components/user_details_form.rs ### 📊 Changes **39 files changed** (+1596 additions, -92 deletions) <details> <summary>View changed files</summary> 📝 `app/queries/get_user_details.graphql` (+1 -0) 📝 `app/queries/list_users.graphql` (+1 -0) ➕ `app/queries/set_user_login_enabled.graphql` (+5 -0) 📝 `app/src/components/form/attribute_input.rs` (+41 -7) 📝 `app/src/components/form/date_input.rs` (+16 -5) 📝 `app/src/components/form/file_input.rs` (+13 -1) 📝 `app/src/components/user_details_form.rs` (+1 -0) 📝 `app/src/components/user_table.rs` (+12 -0) 📝 `app/src/infra/schema.rs` (+1 -0) 📝 `crates/access-control/src/lib.rs` (+13 -6) 📝 `crates/auth/src/access_control.rs` (+6 -0) 📝 `crates/domain-model/src/model/deserialize.rs` (+6 -0) 📝 `crates/domain-model/src/model/users.rs` (+5 -0) 📝 `crates/domain/src/deserialize.rs` (+9 -0) 📝 `crates/domain/src/public_schema.rs` (+9 -0) 📝 `crates/domain/src/requests.rs` (+1 -0) 📝 `crates/domain/src/types.rs` (+25 -0) 📝 `crates/graphql-server/Cargo.toml` (+7 -3) 📝 `crates/graphql-server/src/api.rs` (+19 -15) 📝 `crates/graphql-server/src/mutation.rs` (+343 -13) _...and 19 more files_ </details> ### 📄 Description ## Summary Implements comprehensive user account disable/enable functionality with automatic session invalidation to allow administrators to prevent users from logging in and immediately revoke existing sessions without deleting their accounts. Closes #750 ## Changes ### Backend (Server) - Add `login_enabled` boolean field to `User` domain model and database schema - Implement database migration (v11) to add `login_enabled` column with safe defaults (true) - Update authentication handlers to reject login attempts for disabled users - Extend GraphQL schema with `loginEnabled` field in `User` type and `UpdateUserInput` - Add `login_enabled` field support to LDAP protocol attribute mapping - Implement automatic session invalidation: When users are disabled, all their existing JWT sessions are immediately blacklisted - Add admin protection logic: prevent disabling admin users if they're the only member of critical groups - Allow `lldap_password_manager` group members to disable/enable user accounts - Include comprehensive test coverage for disable/enable functionality ### Frontend (App) - Add login status column to user table with visual indicators - Implement toggle switch in user details form for authorized users - Update GraphQL queries to include `loginEnabled` field - Add proper form state management for checkbox-based toggle - Show appropriate controls based on user permissions (admin/password manager) ### Session Management - Automatic session invalidation: `SessionAwareBackendHandler` wrapper automatically invalidates all user sessions when `login_enabled` is set to false - Updates both database JWT blacklist and in-memory cache for immediate effect - Uses existing JWT blacklisting infrastructure for consistency and security ## Features - Role-based control: Administrators and password managers can disable/enable user accounts - Authentication blocking: Disabled users cannot authenticate via LDAP or OPAQUE protocols - Immediate session invalidation: Existing sessions are terminated when user is disabled - Admin protection: Prevents system lockout by protecting the last admin user - Visual feedback: Clear UI indicators (enabled/disabled status) - Backward compatibility: Database migration ensures existing users remain enabled - LDAP support: Login status is queryable via LDAP search filters - Immediate feedback: Toggle updates instantly in the UI with proper form handling ## LDAP Usage Query disabled users: ```bash ldapsearch -b "ou=people,dc=example,dc=com" "(login_enabled=FALSE)" uid login_enabled ``` Filter out disabled users: ```bash ldapsearch -b "ou=people,dc=example,dc=com" "(&(objectClass=person)(login_enabled=TRUE))" uid ``` ## Security Features - Session invalidation: Disabled users are immediately logged out of all sessions - Admin protection: Cannot disable the last admin user to prevent system lockout - Permission-based access: Only admins and password managers can modify login status - Immediate enforcement: Changes take effect instantly without requiring restart ## Testing - Unit tests for backend disable/enable functionality - Authentication rejection tests for disabled users - Session invalidation tests - Admin protection logic tests - Database migration tests - Frontend toggle functionality - LDAP attribute mapping tests ## Breaking Changes None - this is a backward compatible feature addition. ## Database Migration - Migration v11 adds `login_enabled` column with DEFAULT true - All existing users remain enabled after migration - No manual intervention required ## Implementation Details ### Session Invalidation Architecture - SessionAwareBackendHandler wraps the SQL backend handler - Intercepts `update_user` calls and detects when `login_enabled` changes to false - Automatically calls `blacklist_jwts()` to mark user's JWT tokens as invalid in database - Updates in-memory JWT blacklist cache for immediate session termination - Maintains full compatibility with existing authentication flow ### Admin Protection Logic - Checks if user being disabled is in `lldap_admin` or `lldap_password_manager` groups - Counts remaining enabled users in these critical groups - Prevents disabling if it would leave the group empty - Ensures system remains administrable ## Screenshots ### User Table with Status Column <img width="1728" alt="Screenshot 2025-06-29 at 11 28 15 PM" src="https://github.com/user-attachments/assets/41473a3e-6a6a-4a78-a4ed-2b2ef0bf68ae" /> ### Disabled user trying to log in <img width="1726" alt="Screenshot 2025-06-30 at 12 07 40 AM" src="https://github.com/user-attachments/assets/5147d4a6-cbd4-46cc-8529-9ed196acac5c" /> ``` 2025-06-30T04:07:34.632435+00:00 DEBUG HTTP request [ 7.33ms | 10.86% / 100.00% ] method: "POST" | uri: "/auth/opaque/login/start" 2025-06-30T04:07:34.633034+00:00 DEBUG ┝━ opaque_login_start [ 6.54ms | 1.21% / 89.14% ] 2025-06-30T04:07:34.633054+00:00 DEBUG │ ┕━ login_start [ 6.45ms | 76.15% / 87.93% ] 2025-06-30T04:07:34.633092+00:00 INFO │ ┝━ i [info]: OPAQUE login attempt for "admin2" 2025-06-30T04:07:34.633106+00:00 DEBUG │ ┕━ get_password_file_for_user [ 864µs | 11.78% ] user_id: "admin2" 2025-06-30T04:07:34.640386+00:00 DEBUG ┕━ 🐛 [debug]: | status_code: 200 2025-06-30T04:07:34.713384+00:00 DEBUG HTTP request [ 1.32ms | 21.66% / 100.00% ] method: "POST" | uri: "/auth/opaque/login/finish" 2025-06-30T04:07:34.713623+00:00 DEBUG ┝━ opaque_login_finish [ 1.03ms | 10.04% / 78.34% ] 2025-06-30T04:07:34.713645+00:00 DEBUG │ ┕━ login_finish [ 901µs | 32.88% / 68.29% ] 2025-06-30T04:07:34.714097+00:00 DEBUG │ ┝━ check_user_enabled [ 467µs | 35.41% ] user_id: "admin2" 2025-06-30T04:07:34.714744+00:00 WARN │ ┝━ 🚧 [warn]: OPAQUE login attempt for user with login blocked "admin2" 2025-06-30T04:07:34.714760+00:00 ERROR │ ┕━ 🚨 [error]: | error: Authentication error Login blocked for user "admin2" 2025-06-30T04:07:34.714936+00:00 DEBUG ┕━ 🐛 [debug]: | status_code: 401 ``` --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
kerem 2026-02-27 09:11:11 +03:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
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
starred/lldap-lldap#1173
No description provided.