# usermanager **Repository Path**: blueLotu/usermanager ## Basic Information - **Project Name**: usermanager - **Description**: No description available - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: main - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-09-22 - **Last Updated**: 2026-09-29 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # usermanager A reusable, transport/storage-agnostic **user & permission management backend**, written in C++17. It follows a client/server model: this repository provides the **server** side — an HTTP+JSON API backed by SQLite — packaged as a library (`server_module`) plus a small disposable driver executable (`server_app`). A future Qt frontend ("plan B") is expected to consume the HTTP API documented below; this README is the authoritative API contract for that integration. Key properties: - **C++17**, built with both CMake and qmake from one shared source tree. - **Transport is abstracted** behind `um::ITransportServer` (core/include/um/transport.h); the only concrete implementation shipped is an HTTP+JSON server built on cpp-httplib (`server/src/transport/http_server.cpp`). Business logic never touches httplib types directly. - **Storage is abstracted** behind `um::IUserRepository` / `um::IRoleRepository` (core/include/um/repositories.h); the only concrete implementation shipped is SQLite (`server/src/db/`). Business logic never touches `sqlite3*` types directly. - Authentication is a minimal in-memory bearer-token scheme (no expiry) — see `server/src/service/auth_service.cpp`. ## Third-party vendoring All third-party dependencies are **vendored as single files** under `third_party/` (no package manager / submodules required): | File | Purpose | |---|---| | `third_party/doctest.h` | doctest — unit test framework (`um_tests` / qmake `tests`) | | `third_party/json.hpp` | nlohmann/json — JSON parsing/serialization | | `third_party/httplib.h` | cpp-httplib — HTTP server used by `HttpServer` | | `third_party/sqlite3.h`, `third_party/sqlite3.c` | SQLite amalgamation — storage backend | | `third_party/sha256.h`, `third_party/sha256.c` | Vendored SHA-256 — used by the PBKDF2-HMAC-SHA256 password hasher | ## Build — CMake ```bash cmake -S . -B build-cmake -DCMAKE_BUILD_TYPE=Debug cmake --build build-cmake -j ctest --test-dir build-cmake --output-on-failure ``` Targets produced: - `core` — static library: permission/role constants, DTOs, JSON codec, response envelope. - `server_module` — static library: HTTP transport, SQLite storage, services, router, `UserManagerServer` facade. Links `core`. - `server_app` — disposable executable driver (`server/app/main.cpp`) that assembles a `UserManagerServer` from environment variables. - `um_tests` — doctest-based test binary, registered with `ctest` as test `um_tests`. Verified: the commands above succeed end-to-end (configure, build all 4 targets, `ctest` passes) as of this writing. ## Build — qmake The qmake project files live under `server/`, `tests/`, `core/`, and share the same source tree as the CMake build via `.pri` includes in `qmake/`. Build the server driver and the test binary independently: ```bash # server_app qmake server/server_app.pro && make # tests qmake tests/tests.pro && make ``` (Alternatively, run `qmake usermanager.pro && make` from the repo root to build everything — `core`, `server/server_module.pro`, `server/server_app.pro`, and `tests` — as ordered subdirs.) Verified: `qmake usermanager.pro && make` succeeds and produces `server/server_app` and `tests/tests`. ## Run `server_app` `server_app` reads a `um::ServerConfig` with these defaults, overridable via environment variables (see `server/app/main.cpp`): | Env var | Overrides | Default | |---|---|---| | `UM_PORT` | `cfg.port` | `8080` | | `UM_DB` | `cfg.dbPath` | `usermanager.db` | | `UM_DEV_USER` | `cfg.developerUser` | `developer` | | `UM_DEV_PASS` | `cfg.developerPassword` | `developer` | `cfg.host` defaults to `127.0.0.1` and is not currently overridable via env var. On first run against a fresh database, the schema is created and seeded with the three built-in roles (`developer`, `admin`, `user`) and a `developer` account using `UM_DEV_USER` / `UM_DEV_PASS` (or the defaults). ```bash UM_PORT=8080 UM_DB=./usermanager.db ./server_app ``` Example login: ```bash curl -s -X POST http://127.0.0.1:8080/api/login \ -H 'Content-Type: application/json' \ -d '{"username":"developer","password":"developer"}' ``` Response: ```json {"ok":true,"data":{"token":"","user":{"id":1,"username":"developer","displayName":"Developer","roleCode":"developer","status":"active","createdAt":"...","updatedAt":"..."},"permissions":["profile.edit_self","profile.view_self","role.assign","role.edit_permissions","role.view","user.create","user.delete","user.edit","user.view"]},"error":null} ``` ## API contract ### Envelope Every response body is a JSON object: ```json {"ok": true, "data": , "error": null} {"ok": false, "data": null, "error": {"code": "", "message": ""}} ``` All endpoints except `POST /api/login` require an `Authorization: Bearer ` header, where `` is the value returned from login. Missing/invalid tokens yield `401 unauthorized`. ### Error codes | HTTP status | `error.code` | Meaning | |---|---|---| | 400 | `bad_request` | Malformed JSON body or invalid/missing field | | 401 | `unauthorized` | Missing/invalid token, or bad login credentials | | 403 | `forbidden` | Authenticated but lacking the required permission point (or a business rule, e.g. admin trying to assign a non-`user` role) | | 404 | `not_found` | Unknown route, or referenced resource (user/role) does not exist | | 409 | `conflict` | Uniqueness/state conflicts — raised by `POST /api/users` when the requested username already exists (`SqliteUserRepository::create`) | | 500 | `internal_error` | Uncaught exception | ### Endpoints Verified against `server/src/handler/router.cpp` and `server/src/service/*.cpp`. | Method | Path | Required permission | Description | |---|---|---|---| | POST | `/api/login` | none (public) | Authenticate with `{"username","password"}`; returns `{token, user, permissions}` | | POST | `/api/logout` | valid token only | Invalidate the bearer token | | GET | `/api/users` | `user.view` | List users; query params `offset` (default 0), `limit` (default 50, max 200) | | POST | `/api/users` | `user.create` | Create a user: `{"username","password","displayName","roleCode"}`. Non-developer callers may only create `user`-role accounts | | PUT | `/api/users/:id` | `user.edit` | Edit `displayName` and/or `status` (`active`/`disabled`) | | DELETE | `/api/users/:id` | `user.delete` | Delete a user (cannot delete a `developer` account) | | PUT | `/api/users/:id/role` | `role.assign` | Reassign a user's role: `{"roleCode"}`. Cannot assign `developer`; non-developer callers may only assign `user` | | GET | `/api/roles` | `role.view` | List roles with their permissions. The hidden `developer` role is omitted unless the caller is a `developer` | | PUT | `/api/roles/:id/permissions` | `role.edit_permissions` | Replace a role's permission set: `{"permissions":[...]}`. Cannot modify the `developer` role | | GET | `/api/profile` | `profile.view_self` | View the caller's own user record | | PUT | `/api/profile` | `profile.edit_self` | Edit the caller's own `displayName` and/or `password` | ## Permission points Defined in `core/include/um/permissions.h` (9 total): `user.view`, `user.create`, `user.edit`, `user.delete`, `role.view`, `role.assign`, `role.edit_permissions`, `profile.view_self`, `profile.edit_self` ## Default role → permission mapping Built-in roles (`core/src/permissions.cpp`, seeded by `server/src/db/sqlite_db.cpp`): | Role | Hidden | Permissions | |---|---|---| | `developer` | yes (hidden from non-developer clients; omitted from `GET /api/roles` for them) | all 9 permission points | | `admin` | no | `user.view`, `user.create`, `user.edit`, `role.view`, `role.assign`, `profile.view_self`, `profile.edit_self` | | `user` | no | `user.view`, `profile.view_self`, `profile.edit_self` | Notes: - `admin` does **not** get `user.delete` or `role.edit_permissions` by default. - `user` **includes** `user.view` by default (any authenticated user can list/view users; only holders of `profile.view_self`/`profile.edit_self` can access their own profile via `/api/profile`). - Permission sets are mutable at runtime via `PUT /api/roles/:id/permissions`, except for the `developer` role, whose permissions are fixed (cannot be edited, and the role itself cannot be assigned/deleted). ## Integration guide To embed the backend in another program (e.g. the future Qt frontend, or any C++ host process): 1. Link against `server_module` (which transitively links `core` and the vendored `sqlite3`/`sha256` static libs, plus `Threads`). 2. Include the single public facade header: ```cpp #include "um/server/user_manager_server.h" ``` 3. Construct a config and start the server: ```cpp um::ServerConfig cfg; cfg.host = "127.0.0.1"; cfg.port = 8080; cfg.dbPath = "usermanager.db"; cfg.developerUser = "developer"; cfg.developerPassword = "developer"; um::UserManagerServer server(cfg); if (!server.start()) { /* handle failure */ } // ... run your own event loop ... server.stop(); ``` `um::UserManagerServer` is a pimpl-based facade (`server/include/um/server/user_manager_server.h`); no third-party types (httplib, sqlite3) leak into its public interface. `server_app` (`server/app/main.cpp`) is a thin, disposable driver that only reads environment variables into a `ServerConfig` and runs a signal-handling loop around `UserManagerServer`. It is not part of the public API and can be excluded entirely when integrating `server_module` into another host application — just link `server_module` and use the facade directly as shown above. ## Qt 客户端 (client_module) A Qt Widgets frontend ("plan B") lives under `client/`, built from the same source tree as the server. It is packaged as a library (`client_module`) plus a small disposable driver executable (`client_app`), mirroring the `server_module` / `server_app` split above. ### Dependency Qt 5.15, modules **Widgets** and **Network** (plus **Test** for the headless doctest harness). No other third-party client dependency. ### Build — CMake ```bash cmake -S . -B build-cmake -DCMAKE_BUILD_TYPE=Debug cmake --build build-cmake -j ctest --test-dir build-cmake --output-on-failure ``` `client/CMakeLists.txt` calls `find_package(Qt5 COMPONENTS Widgets Network Test REQUIRED)` and is included from the top-level `CMakeLists.txt`. Targets produced: - `client_module` — static library: `IApiClient` abstraction, `HttpApiClient`, `LoginWidget`, `MainWindow`, nav-permission mapping, login-response parser. - `client_app` — disposable executable driver (`client/app/main.cpp`). - `um_client_tests` — doctest-based test binary (Qt Test integration for widget tests), registered with `ctest` as test `um_client_tests`, run with `QT_QPA_PLATFORM=offscreen` so it works headlessly. ### Build — qmake ```bash qmake client/client_app.pro && make ``` (`client/client_module.pro` and `client/client_tests.pro` build the library and the `um_client_tests` equivalent independently; both share `.pri` includes under `qmake/`, same pattern as the server's qmake build.) ### Run `client_app` ```bash ./client_app [baseUrl] # or UM_CLIENT_BASE=http://127.0.0.1:8080 ./client_app ``` The base URL of the backend API defaults to `http://127.0.0.1:8080`, overridable by the `UM_CLIENT_BASE` environment variable, then by `argv[1]` (argv wins if both are given). `client_app` needs a running `server_app` (see above) to log in against — it is a plain HTTP client of the API documented in this README. ### Architecture - UI code depends only on the abstract `um::client::IApiClient` interface (`client/include/um/client/api_client.h`), never on transport details. - `um::client::HttpApiClient` is the concrete HTTP+JSON implementation (built on `QNetworkAccessManager`), talking to the same `/api/login` endpoint documented under [API contract](#api-contract). - On successful login, `um::client::LoginWidget` emits `loggedIn(const um::LoginResult&)`; the driver calls `api->setAuthToken(result.token)` (so subsequent requests carry the session token) and then constructs `um::client::MainWindow(api, result)` — a shell window whose navigation list is derived from the caller's permission set (`nav_permissions.h`). Each nav entry now opens a real business page wired to the same `IApiClient`/session: `users` → `UserManagementPage` (list/create/edit/ delete/assign-role), `roles` → `RolePermissionPage` (per-role permission checkboxes), `profile` → `ProfilePage` (view/edit own display name and password); any unrecognized nav id still falls back to a placeholder label. Every page calls its corresponding backend endpoint documented under [API contract](#api-contract) and is permission-gated: action buttons the current user lacks permission for are disabled/hidden, and the role-permission page is read-only unless the caller holds `role.edit_permissions`. ### Integration guide To embed the Qt frontend in another host application: 1. Link against `client_module` (which transitively links `Qt5::Widgets` and `Qt5::Network`). 2. Include the headers you need, e.g.: ```cpp #include "um/client/login_widget.h" #include "um/client/main_window.h" #include "um/client/http_api_client.h" ``` 3. Provide your own `um::client::IApiClient` implementation (or reuse `HttpApiClient`) and inject it into `LoginWidget`; swapping the implementation lets you change transport/protocol without touching UI code. `client_app` (`client/app/main.cpp`) is a thin, disposable example driver — it only wires `HttpApiClient` → `LoginWidget` → `MainWindow` and calls `app.exec()`. It is not part of the public API and can be excluded entirely when integrating `client_module` into another host application. ## Layout - `core/` — core static library (permission/role constants, DTOs, JSON codec, transport & repository interfaces, password hasher interface). - `server/` — server module (`server_module`): SQLite repositories, services, router, HTTP transport, `UserManagerServer` facade — and `server_app`, the disposable driver executable. - `client/` — Qt client module (`client_module`): `IApiClient` abstraction, `HttpApiClient`, `LoginWidget`, `MainWindow` shell — and `client_app`, the disposable driver executable. - `tests/` — doctest-based unit tests (`um_tests` under CMake / `tests` under qmake), covering both `core` and `server_module`. - `third_party/` — vendored single-file dependencies (see table above). - `docs/` — design/task specs.