add rbac
This commit is contained in:
@@ -111,6 +111,57 @@ Request/response field shapes match AuthHex's own payloads one-for-one (project-
|
||||
session-issuing responses omit `AccessToken`/`RefreshToken` (cookie-delivered instead) and `refreshToken` is read from the
|
||||
`erp_rt` cookie rather than the request body.
|
||||
|
||||
### 2.0.1 RBAC — Roles, sidebar nav, Users (added 2026-07-18; sidebar-visibility only, see docs/10 C.8)
|
||||
|
||||
`GET /api/v1/auth/me` — the frontend's authoritative source for the current session's role and permitted sidebar sections
|
||||
(replaces the previous client-only `roleId` cached in localStorage). No payload.
|
||||
**200 OK**
|
||||
```json
|
||||
{ "roleCode": "ADMIN", "roleName": "Administrator", "navCodes": ["dashboard", "products", "products.item", "settings.roles", "..."] }
|
||||
```
|
||||
|
||||
`GET /api/v1/nav` — read-only sidebar tree (`NavItem` + nested `SubNavItem`), seeded to mirror the frontend's hardcoded
|
||||
sidebar (`components/Layouts/AppSidebar.tsx`); used to render the Role permission-assignment checkbox UI. Not admin-editable
|
||||
in this phase.
|
||||
|
||||
**Roles** (`RolesController`) — `Role` is a **local shadow of AuthHex's Role** (same pattern as `USER`/`auth_user_id`,
|
||||
docs/10 C.9): every write below forwards to AuthHex's new `/api/role` functions first, then mirrors the result locally.
|
||||
| Route | Notes |
|
||||
|---|---|
|
||||
| `GET /roles` | Paged list; `q`, `status` filters. |
|
||||
| `GET /roles/{roleId}` | `ETag` header for `If-Match` on update. |
|
||||
| `POST /roles` | `{ code, name }` → `201`, forwards to AuthHex `createRole`. |
|
||||
| `PUT /roles/{roleId}` | Requires `If-Match`; forwards to AuthHex `updateRole`. |
|
||||
| `PATCH /roles/{roleId}/status` | `{ status }` → `204`. |
|
||||
| `DELETE /roles/{roleId}` | Forwards to AuthHex `deleteRole`; `409 ROLE_IN_USE` if any user still holds it. |
|
||||
| `GET /roles/{roleId}/permissions` | `{ roleId, navItemIds, subNavItemIds }`. |
|
||||
| `PUT /roles/{roleId}/permissions` | Replaces the role's full permission set from `{ navItemIds, subNavItemIds }` — purely local, no AuthHex call. |
|
||||
|
||||
> **`code` is server-accepted but frontend-derived, never hand-typed (2026-07-18).** The Roles UI computes `code` from
|
||||
> `name` (uppercased, non-alphanumeric → `_`) and submits it read-only; the field stays free-form here for API callers,
|
||||
> but no UI lets an operator type or edit it directly, on create or later. The Create Role dialog also now includes the
|
||||
> permission checkbox tree, so `POST /roles` and `PUT /roles/{roleId}/permissions` fire as one user action.
|
||||
>
|
||||
> **Bug fixed (2026-07-18): `POST /roles`/`PUT /roles/{roleId}` 500ing via `AUTH_UPSTREAM_ERROR`.** `IsSystemRole` being
|
||||
> omitted serialized as JSON `null`, and AuthHex's `createRole`/`updateRole` called `JsonElement.GetBoolean()` on it
|
||||
> unconditionally when the key was present — which throws on `null` (unlike `GetString()`, which tolerates it). Fixed on
|
||||
> both sides: AuthHex now checks `ValueKind != JsonValueKind.Null` before reading `isSystemRole`, and ERPCore's
|
||||
> `AuthHexClient` now serializes with `JsonIgnoreCondition.WhenWritingNull` so unset nullable fields are omitted from the
|
||||
> payload entirely rather than sent as explicit nulls — closing this class of bug for any other nullable field sent to AuthHex.
|
||||
|
||||
**Users** (`UsersController`) — manages the local shadow `User` table and orchestrates account creation in AuthHex.
|
||||
| Route | Notes |
|
||||
|---|---|
|
||||
| `GET /users` | Paged list, joined with `Role`. |
|
||||
| `GET /users/{userId}` | Single record. |
|
||||
| `POST /users` | Creates the account in **both** backends: calls AuthHex's `registerUser` (which persists the password and emails it to the given `email`), then immediately mirrors the local shadow `User` row (rather than waiting for next-login JIT provisioning). Body: `{ username, fullName, roleId, userTypeId, email, nic?, mobileNumber?, password? }` (`password` empty ⇒ AuthHex auto-generates one). |
|
||||
| `PUT /users/{userId}/role` | `{ roleId }` — local role reassignment only; status/lock changes reuse the existing `/auth/status` and `/auth/lock` proxy endpoints. |
|
||||
| `GET /users/user-types` | Added 2026-07-18. Proxies AuthHex's new `listUserTypes` — `[{ userTypeId, code, description }]`. Populates the Create User form's UserType select so operators pick from a real list instead of typing an AuthHex GUID by hand; defaults to the sole existing type when only one exists. |
|
||||
|
||||
> **Known gap, not fixed (flagged 2026-07-18):** AuthHex's `loginUser` resolves `identifier` against `Email`/`MobileNumber`/`Nic`
|
||||
> only — **not** `Username` (`UserManageRepository.GetUserByIdentifierAndType`). A user created via `POST /users` can log in
|
||||
> with their email but not their username. Out of scope for this change; revisit if/when asked.
|
||||
|
||||
### 2.1 Items
|
||||
> **`itemType` → `stockNature` (2026-07-16).** The Stocked/NonStocked/Service field was renamed so the name `itemType` could be taken by the new Item Type master (§2.7) — an unrelated concept. Items gained `subCategoryId` and `brandId` (both nullable). Items carry **no** item-type reference: the values chosen in the builder are encoded into the client-generated SKU (docs/10 Part C.9).
|
||||
|
||||
|
||||
Reference in New Issue
Block a user